Generalised data constructor matching

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Generalised data constructor matching

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


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).

Many thanks,
Colin
Reply | Threaded
Open this post in threaded view
|

Generalised data constructor matching

Daniel Fischer-4
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


Reply | Threaded
Open this post in threaded view
|

Generalised data constructor matching

Chaddaï Fouché
On Fri, Sep 4, 2009 at 2:57 PM, Daniel Fischer<[hidden email]> wrote:
> 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.

And so by using Data and Typeable as superclasses, he could do what he
is asking for... And Data and Typeable are automatically derivables by
GHC (extension).

--
Jeda?
Reply | Threaded
Open this post in threaded view
|

Generalised data constructor matching

Daniel Fischer-4
Am Samstag 05 September 2009 09:53:34 schrieb Chadda? Fouch?:

> On Fri, Sep 4, 2009 at 2:57 PM, Daniel Fischer<[hidden email]> wrote:
> > 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.
>
> And so by using Data and Typeable as superclasses, he could do what he
> is asking for... And Data and Typeable are automatically derivables by
> GHC (extension).

True. But I don't think the code would become any shorter/better/easier to maintain
(*shudder*), so I'd stick to individual instance declarations.