Как Haskell справляется с перегрузкой полиморфизма?
У меня вопрос о полиморфизме Хаскелла.
Как я узнал, существует два типа полиморфизма:
-
Параметрический: где вы не указываете тип ввода.
Пример:
functionName :: [a] -> a
-
Перегрузка: как обязательное программирование, т.е. передача различных аргументов одной и той же функции.
Моя проблема: как Haskell обрабатывает перегрузку?
Ответы
Ответ 1
Перегрузка в Haskell выполняется с использованием классов типов. Например, допустим, вы хотите перегрузить функцию foo
, которая возвращает Int
:
class Fooable a where
foo :: a -> Int
instance Fooable Int where
foo = id
instance Fooable Bool where
foo _ = 42
Однако они более мощные, чем механизмы перегрузки, обнаруженные на большинстве языков. Например, вы можете перегрузить возвращаемый тип:
class Barable a where
bar :: Int -> a
instance Barable Int where
bar x = x + 3
instance Barable Bool where
bar x = x < 10
Для получения дополнительных примеров ознакомьтесь с предопределенными типами классов в Haskell.
Ответ 2
Haskell использует типы классов для ad hoc-полиморфизма.
Ответ 3
В некоторых языках перегрузка означает использование одного и того же имени для нескольких функций, которые предоставляют аналогичные, но разные функции, поэтому вы можете попробовать
split :: String -> [String] -- splits on whitespace
split :: Char -> String -> [String] -- splits on the given character
split :: [Char] -> String -> [String] -- splits on any of the given characters
split :: (Char -> Bool) -> String -> [String] -- splits using a function that tells you when
который даст вам ошибку Duplicate type signature
, которую вы получаете.
Haskell не делает такого типа перегрузки, и программист Haskell дал бы эти разные имена:
words :: String -> [String] -- splits on whitespace
splitOn :: Char -> String -> [String] -- splits on the given character
splitsOn :: [Char] -> String -> [String] -- splits on any of the given characters
splitWith :: (Char -> Bool) -> String -> [String] -- splits using a function that tells you when
Причина, по которой Haskell не допускает такого перегруза, о котором я думаю, о чем вы спрашиваете, заключается в том, что он действительно не позволяет вам делать что-либо, что вы не могли бы сделать без него, и позволяя ему сделать это почти невозможно выполнить более сложные виды перегрузки. Перегрузка Haskell - действительно очень мощный инструмент; узнать о типах классов и классах конструкторов для начала.
Собственно, поскольку String
= [Char]
, а программисты Haskell любят повторное использование кода, они гораздо чаще будут писать:
words :: String -> [String] -- splits on whitespace
splitOn :: Eq a => a -> [a] -> [[a]] -- splits on the given item
splitsOn :: Eq a => [a] -> [a] -> [[a]] -- splits on any of the given items
splitWith :: (a -> Bool) -> [a] -> [[a]] -- splits using a function that tells you when
Здесь Eq a
является примером своего рода перегрузки Haskell позволяет, где splitOn
позволит вам разделить любой список, пока элементы могут сравниваться для равенства (т.е. Haskell позволяет вам определить свое собственное понятие равенства). Вы можете использовать это, чтобы разделить String или, например, список строк, но вы не можете разбить список функций, потому что вы не можете проверить две функции, чтобы убедиться, что они равны. splitWith
- пример Haskell, позволяющий вам рассматривать функцию так же, как и большинство других данных - вы можете передать ее как аргумент!
[Примечание 1: words
- стандартная функция, splitWith
находится в библиотеке со слегка отличающейся типоразмерностью.]
[Примечание 2: если вы действительно хотите написать эти функции, вот как:
splitWith isSplitter list = case dropWhile isSplitter list of
[] -> []
thisbit -> firstchunk : splitWith isSplitter therest
where (firstchunk, therest) = break isSplitter thisbit
-- words = splitWith isSpace -- not needed, standard function from the Prelude
splitOn c = splitWith (== c) -- notice I passed == in an argument!
splitsOn chars = splitWith (`elem` chars)
]
Ответ 4
Вы указываете сигнатуру типа вашей функции в исходном классе типа, тогда вы создаете несколько экземпляров этой функции, для которой вы написали подпись. Итак, в приведенном выше примере hammar вы можете считать a полиморфным и тип, указанный в каждом экземпляре (например, экземпляр Fooable Bool) как тип a (в этом случае a является Bool). Поэтому, когда вы вызываете функцию foo с помощью значения Bool, вызывается экземпляр Fooable для значения Bool.
BTW, вы можете поместить несколько функций в класс типа, и вы можете определять функции в терминах других определенных функций.
например.
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)
Это может быть не очевидно здесь, но если вы определяете экземпляр Eq, вам нужно только определить == или /=, а не оба, так как они определены в терминах друг друга.
Ответ 5
В Haskell основное отличие в Haskell совершенно иное, хотя вы можете сделать что-то похожее.
я. В качестве выбранного ответа используйте классы.
class YesNo a where
yesno :: a -> Bool
Вам нужно реализовать его с помощью экземпляра, а затем вы можете использовать его следующим образом:
> yesno $ length []
False
> yesno "haha"
True
> yesno ""
False
> yesno $ Just 0
True
> yesno True
True
ghci> yesno EmptyTree
False
> yesno []
False
> yesno [0,0,0]
True
http://learnyouahaskell.com/making-our-own-types-and-typeclasses
II. Используйте сопоставление шаблонов конструктора типов, например:
data Shape = Circle Float Float Float | Rectangle Float Float Float Float
surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
а затем вы можете использовать их следующим образом:
> surface $ Circle 10 20 10
314.15927
> surface $ Rectangle 0 0 100 100
10000.0
http://learnyouahaskell.com/making-our-own-types-and-typeclasses
III. Имейте функцию "oveloaded" в модуле sepatate, но вам нужно будет префикс имени импорта или имени импортируемого импорта
http://learnyouahaskell.com/modules
Ответ 6
Здесь у вас есть простой пример объединения
-
ad-hoc полиморфизм (перегрузка): одна и та же функция с различным поведением для разных типов (с помощью классов типа Haskell)
-
параметрический полиморфизм: одна и та же функция с одинаковым поведением для разных типов (с помощью параметризованной функции типа.
принцип, тип не имеет значения, но мы использовали классы типов для ограничения
приемлемые типы).
код:
import Data.Char
class MyTypeFamily t where
f1 :: t -> Int
f2 :: t -> Int
instance MyTypeFamily Int where
f1 x = x*x
f2 x = x+x
instance MyTypeFamily Char where
f1 x = (ord x) * (ord x)
f2 x = (ord x) + (ord x)
instance MyTypeFamily Bool where
f1 x
| x = 10
| otherwise = 10
f2 x
| x = 100
| otherwise = -100
-- ...............................................................
-- using f1, f2 as "overloaded" functions ("ad-hoc polymorphism)
-- (the algorithm for f1, f2 is chosen depending on their type)
--
-- using fun as polymorphic (parametric polymorphic function)
-- the algorithm of fun is always the same but it works on
-- different types
fun :: (MyTypeFamily t) => t -> Int
fun x = (f1 x) + (f2 x)
-- ...............................................................
-- ...............................................................
main = do
print $ fun 'J'
print $ fun True
print $ fun False
print $ fun (8 :: Int)