我对Haskell多态性有疑问。
据我所知,有两种类型的多态:
参数:您没有指定输入类型。
示例:
functionName :: [a] -> a
重载:作为命令式编程,即将不同的参数传递给同一个函数。
我的问题是:Haskell如何处理重载?
答案 0 :(得分:43)
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中的predefined type classes。
答案 1 :(得分:6)
在某些语言中,重载意味着对提供相似但功能不同的多个函数使用相同的名称,因此您可以尝试
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允许您定义您的自己的平等观念)。您可以使用它来分割字符串,或者例如字符串列表,但是您不能拆分函数列表,因为您无法检查两个函数以查看它们是否相等。 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)
答案 2 :(得分:4)
Haskell使用type classes进行ad hoc多态。
答案 3 :(得分:3)
在原始类型类中指定函数的类型签名,然后创建为该函数编写签名的函数的多个实例。所以,在发布的示例中,你可以认为 a 是多态的,并且每个实例中指定的类型(例如实例Fooable Bool)都是a的类型(在这种情况下是一个Bool) )。因此,当您使用Bool值调用函数 foo 时,将调用Booo值的Fooable实例。
顺便说一句,您可以在类型类中放置多个函数,也可以根据定义的其他函数定义函数。
e.g。
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)
这里可能并不明显,但如果你定义了一个Eq实例,你只需要定义==或/ =,而不是两者,因为它们是按照彼此的方式定义的。
答案 4 :(得分:1)
基本上覆盖在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。在sepatate模块中有“oveloaded”功能,但您需要在导入名称或限定的导入名称前加上
答案 5 :(得分:0)
这里有一个结合
的简单示例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)