
Am Freitag 04 September 2009 14:01:47 schrieb Colin Campbell-McPherson:
Hi Chaps,
I've been trying to write a function that would get the name of most data that is an instance of class "Animal". This getName function could then be overwritten for data that doesn't follow the normal pattern for "Animal" data types. So the getName defined in the Animal class would be a generic, or default implementation.
I've written a couple of examples that don't use classes, but that I hope expresses what I'm trying to accomplish. In the first example getName needs to be defined for Dogs and Birds, even though they're essentially identical. The tries to define a more general function that works for both Birds and Dogs, and anything else that might come along.
The first examples works, the second gives a "Parse error in pattern" error.
EXAMPLE 1
data Animal = Person String String | Dog String | Bird String deriving Show getName :: Animal -> String getName (Person firstName lastName) = firstName ++ " " ++ lastName getName (Dog name) = name getName (Bird name) = name
logan = Person "Logan" "Campbell" ebony = Dog "Ebony" poly = Bird "Poly"
main = do putStrLn $ show $ getName logan putStrLn $ show $ getName ebony putStrLn $ show $ getName poly
For such a datatype, named fields come in handy: data Animal = Person { firstName, lastName :: String } | Dog { name :: String } | Bird { name :: String } getName :: Animal -> String getName (Person f l) = f ++ " " ++ l -- other special cases if the datatype is extended getName other = name other
EXAMPLE 2
data Animal = Person String String | Dog String | Bird String deriving Show getName :: Animal -> String getName (Person firstName lastName) = firstName ++ " " ++ lastName getName (_ name) = name
logan = Person "Logan" "Campbell" ebony = Dog "Ebony" poly = Bird "Poly"
main = do putStrLn $ show $ getName logan putStrLn $ show $ getName ebony putStrLn $ show $ getName poly
I have a background with Ruby and Erlang, so if drawing from concepts in either of those languages would help explain please do so. I'm also quite new to haskell so if i've used the wrong terms, or I'm trying to apply concepts where they don't belong, sorry about that.
TLDR: How can I write a generic function that will work on most instances of a class, but be over over-written for instances of that class that deviate from the normal structure (ie. a data constructor with two params, rather than one).
In general: not. The problem is that potentially every datatype can be made an instance of the class, so in the default implementations, you can only[*] use functions which work on every datatype. There aren't many interesting functions that do. [*]well, you can also use methods of the class and superclasses, so e.g. class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y) class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>), (>=) :: a -> a -> Bool min, max :: a -> a -> a x < y = x <= y && x /= y x <= y = x < y || x == y -- etc. is possible. But you still have to write instances manually for every type (except instances you can let the compiler derive).
Many thanks, Colin