How does Haskell handle overloading polymorphism?

前端 未结 6 1610
余生分开走
余生分开走 2020-12-29 06:38

I have a question about Haskell polymorphism.

As I\'ve learned, there are two types of polymorphism:

  1. Parametric: where you do not spe

相关标签:
6条回答
  • 2020-12-29 06:57

    In some languages, overloading means using the same name for several functions that provide similar but different functionality, so you might try

    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
    

    which would give you the Duplicate type signature error you're getting.

    Haskell doesn't do this type of overloading, and a Haskell programmer would give these different names:

    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
    

    The reason that Haskell doesn't allow the kind of overloading I think you're asking about, is that it genuinely doesn't let you do anything that you couldn't do without it, and allowing it would make it almost impossible to do more advanced sorts of overloading. Haskell's overloading is a very powerful tool indeed; find out about type classes and constructor classes to get started.

    Actually, since String = [Char], and Haskell programmers love code reuse, they'd be far more likely to write:

    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
    

    Here Eq a is an example of a kind of overloading Haskell does allow, where splitOn will let you split any list as long as the items can be compared for equality (i.e. Haskell lets you define your own notion of equality). You can use this then to split a String, or for example a list of Strings, but you can't split a list of functions because you can't check two functions to see if they're equal. splitWith is an example of Haskell letting you treat a function just like most other data - you can pass one as an argument!

    [Note 1: words is a standard function, splitWith is in a library with a slightly different typesignature.]
    [Note 2: if you wanted to actually write these functions, here's how:

    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)
    

    ]

    0 讨论(0)
  • 2020-12-29 06:57

    You specify the type signature of your function in the original type class, then you create multiple instances of that function that you wrote the signature for. So, in the example hammar posted you can think of a as being polymorphic, and the type specified in each instance (e.g. instance Fooable Bool) as being the type of a (in this case a is a Bool). So when you call the function foo with a Bool value, the instance of Fooable for a Bool value gets called.

    BTW, you can put multiple functions in the type class, and you can define functions in terms of other functions defined as well.

    e.g.

    class  Eq a  where
        (==), (/=)  ::  a -> a -> Bool
    
        x /= y  = not (x == y)
        x == y  = not (x /= y)
    

    It may not be obvious here, but if you define an instance of Eq, you only need to define == or /=, not both, since they are defined in terms of each other.

    0 讨论(0)
  • 2020-12-29 07:04

    Basically overriding is quite different in Haskell, although you can do something similiar.

    I. Use Classes as selected answer.

    class YesNo a where  
        yesno :: a -> Bool  
    

    You have to implement it using instance, and then you can use it like this:

    > 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. Use pattern matching of type constructor, such as:

    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)
    

    and then you can use them like this:

    > 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. Have the "oveloaded" function in sepatate module, but you will need to prefix the import name or qualified imports name

    http://learnyouahaskell.com/modules

    0 讨论(0)
  • 2020-12-29 07:05

    Here, you have a simple example combining

    • ad-hoc polymorphism (overloading): same function with different behavior for different types (by means of Haskell type classes)

    • parametric polymorphism: same function with same behavior for different types (by means of a type parameterized function. In principle, type doesn't matter, but we used type classes to restrict acceptable types).

    Code:

    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)
    
    0 讨论(0)
  • 2020-12-29 07:08

    Haskell uses type classes for ad hoc polymorphism.

    0 讨论(0)
  • 2020-12-29 07:12

    Overloading in Haskell is done using type classes. For example, let's say you want to overload a function foo that returns an Int:

    class Fooable a where
        foo :: a -> Int
    
    instance Fooable Int where
        foo = id
    
    instance Fooable Bool where
        foo _ = 42
    

    However, they are more powerful than the overloading mechanisms found in most languages. For example, you can overload on the return type:

    class Barable a where
        bar :: Int -> a
    
    instance Barable Int where
        bar x = x + 3
    
    instance Barable Bool where
        bar x = x < 10
    

    For more examples, have a look at the predefined type classes in Haskell.

    0 讨论(0)
提交回复
热议问题