I have a question about Haskell polymorphism.
As I've learned, there are two types of polymorphism:
Para开发者_开发问答metric: where you do not specify the input type.
Example:
functionName :: [a] -> a
Overloading: as imperative programming, i.e. passing different arguments to the same function.
My problem is: how does Haskell handle overloading?
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.
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)
]
Haskell uses type classes for ad hoc polymorphism.
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.
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
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)
精彩评论