Haskell如何处理重载多态?

时间:2011-07-09 16:46:40

标签: haskell polymorphism

我对Haskell多态性有疑问。

据我所知,有两种类型的多态:

  1. 参数:您没有指定输入类型。

    示例:

    functionName :: [a] -> a
    
  2. 重载:作为命令式编程,即将不同的参数传递给同一个函数。

  3. 我的问题是:Haskell如何处理重载

6 个答案:

答案 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”功能,但您需要在导入名称或限定的导入名称前加上

http://learnyouahaskell.com/modules

答案 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)