Arun Suresh | 13 Oct 11:29

Query regarding Type classes

Hello folks,

Im kinda new to haskell. Ive only been fiddling around with it for bout 3 - 4 weeks now.
And for the life of me... i cant seem to figure out why this doesnt work :


class Foo a where
    fooFunc :: a -> Int

data FooData = FData

instance Foo FooData where
    fooFunc _ = 10



class Bar a where
    barFunc :: (Foo b) => a -> b -> Int

data BarData = BData

instance Bar BarData where
    barFunc _ FData = 20



When I compile I get this :
 Couldn't match expected type `b' against inferred type `FooData'
      `b' is a rigid type variable bound by
          the type signature for `barFunc' at Sample.hs:16:20
    In the pattern: FData
    In the definition of `barFunc': barFunc _ FData = 20
    In the definition for method `barFunc'


Think Im missing something really big...
Could somebody kindly help me out here...



Regards
Arun


Could so


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe <at> haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
Ryan Ingram | 13 Oct 12:03

Re: Query regarding Type classes

Step 1: Forget everything you know about OO classes, then try again :)

2008/10/13 Arun Suresh <arun.suresh <at> gmail.com>:
> class Foo a where
>     fooFunc :: a -> Int
>
> data FooData = FData
>
> instance Foo FooData where
>     fooFunc _ = 10

So far so good.

> class Bar a where
>     barFunc :: (Foo b) => a -> b -> Int

So now, barFunc advertises "for any type a which is in the Bar
typeclass, and any type b which is in the Foo typeclass, I can give
you a function from a to b to Int".  You can make an implementation of
this that has different functionality based on "a", but it needs to be
polymorphic in "b", taking any type "b" which is a member of Foo.

> data BarData = BData
>
> instance Bar BarData where
>     barFunc _ FData = 20

Uh oh!  FData is of type FooData!  But we just advertised that we
could take *anything* which is in the typeclass Foo, which can include
any number of other types.  It doesn't matter that FooData is the only
declared member at this point.  What that means is that if you want to
do anything with the second argument, you can only use functions that
accept any type which is a member of the Foo class.  Right now that
just means generically polymorphic functions (id, const, etc.), and
the fooFunc function inside the "Foo" typeclass.

For example, this declaration would work:

instance Bar BarData where
    barFunc _ f = 10 + fooFunc f

> When I compile I get this :
>  Couldn't match expected type `b' against inferred type `FooData'
>       `b' is a rigid type variable bound by
>           the type signature for `barFunc' at Sample.hs:16:20
>     In the pattern: FData
>     In the definition of `barFunc': barFunc _ FData = 20
>     In the definition for method `barFunc'

The compiler is just telling you what I just told you: barFunc says
that it should take any type "b", but the pattern FData constrains the
argument to be a FooData.

> Think Im missing something really big...
> Could somebody kindly help me out here...

I recommend reading http://www.haskell.org/haskellwiki/OOP_vs_type_classes

  -- ryan
Arun Suresh | 13 Oct 12:18

Re: Query regarding Type classes

Hello Ryan..

On Mon, Oct 13, 2008 at 3:33 PM, Ryan Ingram <ryani.spam <at> gmail.com> wrote:
Step 1: Forget everything you know about OO classes, then try again :)

you seem to have read my mind [:)].. i actaually hit upon this issue while trying to "transcode" some C++ to Haskell..


2008/10/13 Arun Suresh <arun.suresh <at> gmail.com>:
> class Foo a where
>     fooFunc :: a -> Int
>
> data FooData = FData
>
> instance Foo FooData where
>     fooFunc _ = 10

So far so good.

> class Bar a where
>     barFunc :: (Foo b) => a -> b -> Int

So now, barFunc advertises "for any type a which is in the Bar
typeclass, and any type b which is in the Foo typeclass, I can give
you a function from a to b to Int".  You can make an implementation of
this that has different functionality based on "a", but it needs to be
polymorphic in "b", taking any type "b" which is a member of Foo.

> data BarData = BData
>
> instance Bar BarData where
>     barFunc _ FData = 20

Uh oh!  FData is of type FooData!  But we just advertised that we
could take *anything* which is in the typeclass Foo, which can include
any number of other types.  It doesn't matter that FooData is the only
declared member at this point.  What that means is that if you want to
do anything with the second argument, you can only use functions that
accept any type which is a member of the Foo class.  Right now that
just means generically polymorphic functions (id, const, etc.), and
the fooFunc function inside the "Foo" typeclass.

For example, this declaration would work:

instance Bar BarData where
   barFunc _ f = 10 + fooFunc f

> When I compile I get this :
>  Couldn't match expected type `b' against inferred type `FooData'
>       `b' is a rigid type variable bound by
>           the type signature for `barFunc' at Sample.hs:16:20
>     In the pattern: FData
>     In the definition of `barFunc': barFunc _ FData = 20
>     In the definition for method `barFunc'

The compiler is just telling you what I just told you: barFunc says
that it should take any type "b", but the pattern FData constrains the
argument to be a FooData.

> Think Im missing something really big...
> Could somebody kindly help me out here...

I recommend reading http://www.haskell.org/haskellwiki/OOP_vs_type_classes
 
Thanks...
Arun
 

 -- ryan


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe <at> haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
Ryan Ingram | 13 Oct 12:38

Re: Query regarding Type classes

On Mon, Oct 13, 2008 at 11:18 AM, Arun Suresh <arun.suresh <at> gmail.com> wrote:
] you seem to have read my mind [:)].. i actaually hit upon this issue while
] trying to "transcode" some C++ to Haskell..

(literate haskell post, save it into "draw.lhs" and you can load it in ghci!)

What you usually want when you have some "closed world" OO class
hierarchy is to replace the hierarchy with an ADT.

For example:

class Drawable {
    virtual void render() = 0;
}

class Rectangle : public Drawable
{
    Rectangle(Point corner1, Point corner2);
    ...
}

class Circle : public Drawable
{
    Circle(Point center, float radius);
    ...
}

turns into

> data Point = Pt { x :: Float, y :: Float } deriving Show
> data Drawable = Rectangle Point Point | Circle Point Float

> render :: Drawable -> IO ()
> render (Rectangle p1 p2) = putStrLn (concat ["Rectangle from ", show p1, " to ", show p2, "."])
> render (Circle center radius) = putStrLn (concat ["Circle at ", show center, " with radius ", show radius, "."])

This inverts the control from the OO structure; in the OO the
interface is centralized and the data is spread throughout the source.
 In the pattern-matching style, the data is centralized (in the ADT)
but the interface can be spread out.

For example, to add a new function "area()" to Drawable, in the OO
style you have to add it to Drawable and put an implementation into
every class that descends from Drawable, modifying all those modules.
But in Haskell you could declare a new function in one place:

> area :: Drawable -> Float
> area (Rectangle p1 p2) = abs (x p2 - x p1) * abs (y p2 - y p1)
> area (Circle _ r) = pi * r * r

On the other hand, adding new data to Rectangle or Circle requires
changing all the pattern matches spread throughout the source, which
is somewhat annoying.  So there are benefits of both styles.

It's possible to emulate the OO style in Haskell, using existential
types, but it is a lot harder.  It's easier to stick with what the
language is good at! :)

  -- ryan
Arun Suresh | 13 Oct 12:50

Re: Query regarding Type classes


On Mon, Oct 13, 2008 at 4:08 PM, Ryan Ingram <ryani.spam <at> gmail.com> wrote:
On Mon, Oct 13, 2008 at 11:18 AM, Arun Suresh <arun.suresh <at> gmail.com> wrote:
] you seem to have read my mind [:)].. i actaually hit upon this issue while
] trying to "transcode" some C++ to Haskell..

(literate haskell post, save it into "draw.lhs" and you can load it in ghci!)

What you usually want when you have some "closed world" OO class
hierarchy is to replace the hierarchy with an ADT.

For example:

class Drawable {
   virtual void render() = 0;
}

class Rectangle : public Drawable
{
   Rectangle(Point corner1, Point corner2);
   ...
}

class Circle : public Drawable
{
   Circle(Point center, float radius);
   ...
}

turns into

> data Point = Pt { x :: Float, y :: Float } deriving Show
> data Drawable = Rectangle Point Point | Circle Point Float

I agree that this does look more succinct... but what if I write some generic code for the
in the render method of the Drawable class and package it into a library..

Now my client want to write another subclass for Drawable...
He can do that in any other file... package.. whatever...

How would he do that in Haskell ???
considering he may not modify the source file in while i have defined the Drawable ADT..

I know it is possible using Type Classes... Have a Drawable Type Class.. etc.. etc..
Is there probably a better way of dooing it ??



> render :: Drawable -> IO ()
> render (Rectangle p1 p2) = putStrLn (concat ["Rectangle from ", show p1, " to ", show p2, "."])
> render (Circle center radius) = putStrLn (concat ["Circle at ", show center, " with radius ", show radius, "."])

This inverts the control from the OO structure; in the OO the
interface is centralized and the data is spread throughout the source.
 In the pattern-matching style, the data is centralized (in the ADT)
but the interface can be spread out.

For example, to add a new function "area()" to Drawable, in the OO
style you have to add it to Drawable and put an implementation into
every class that descends from Drawable, modifying all those modules.
But in Haskell you could declare a new function in one place:

> area :: Drawable -> Float
> area (Rectangle p1 p2) = abs (x p2 - x p1) * abs (y p2 - y p1)
> area (Circle _ r) = pi * r * r

On the other hand, adding new data to Rectangle or Circle requires
changing all the pattern matches spread throughout the source, which
is somewhat annoying.  So there are benefits of both styles.

It's possible to emulate the OO style in Haskell, using existential
types, but it is a lot harder.  It's easier to stick with what the
language is good at! :)

Hmmm... I suspect this answers the question I stated above...


 -- ryan

-Arun

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe <at> haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
Bulat Ziganshin | 13 Oct 12:59

Re[2]: Query regarding Type classes

Hello Arun,

Monday, October 13, 2008, 2:50:27 PM, you wrote:

> I agree that this does look more succinct... but what if I write some generic code for the
> in the render method of the Drawable class and package it into a library..

i recommend you to read http://haskell.org/haskellwiki/OOP_vs_type_classes
first and then look into http://haskell.org/haskellwiki/Library/Streams
 - it's a good example of extensible library written using type
 classes. later i've used this approach to write extensible library in
 C++ using templates (not classes)

--

-- 
Best regards,
 Bulat                            mailto:Bulat.Ziganshin <at> gmail.com
Stephen Hicks | 14 Oct 00:04
Favicon

Re: Query regarding Type classes

(First of all, sorry for the double reply...)

2008/10/13 Arun Suresh <arun.suresh <at> gmail.com>:
> Now my client want to write another subclass for Drawable...
> He can do that in any other file... package.. whatever...
>
> How would he do that in Haskell ???
> considering he may not modify the source file in while i have defined the
> Drawable ADT..
>
> I know it is possible using Type Classes... Have a Drawable Type Class..
> etc.. etc..
> Is there probably a better way of dooing it ??

That depends on what you want to do.  If all you want to do is allow
the client to pass Drawable's to your API functions, which then only
use the class interface, you can get away without existentials:

> class Drawable d where
>   area :: d -> Int
>   draw :: d -> IO ()

> foo :: Drawable d -> IO ()
> foo d = do putStrLn $ "This shape has area "++show (area d)
>            draw d

On the other hand, if you want to do something like maintain a list of
drawables then you'll need to use existential types (and therefore
include {-# OPTIONS -fglasgow-exts #-} at the top of the source file):

> data Painting = Painting [(forall d. Drawable d => d)]

> addToPainting :: Drawable d => d -> Painting -> Painting
> addToPainting d (Painting ds) = Painting (d:ds)

> emptyPainting :: Painting
> emptyPainting = Painting []

> computeTotalArea :: Painting -> Int
> computeTotalArea (Painting []) = 0
> computeTotalArea (Painting (d:ds)) = area d + computeTotalArea (Painting ds)

> drawPainting :: Painting -> IO ()
> drawPainting (Painting []) = return ()
> drawPainting (Painting (d:ds)) = draw d >> drawPainting (Painting ds)

You can then expose just the Painting type as an abstract type (with
no exposed constructor), and this is similar to making, say,
 Drawable *painting;
Note that in all these examples (including the C++ one), you *only*
get the class interface to work with.  The main difference is that in
C++, it's a bit easier to downcast, whereas in Haskell it's
practically impossible, short of modifying your class interface:

> class Drawable d where
>   toCircle :: d -> Maybe Circle
>   toCircle _ = Nothing -- default implementation, so clients needn't implement

> instance Drawable Circle where
>   toCircle c = Just c

This could be done more generally as well, if you have a class hierarchy,

> class Drawable d => PointyShape d
> data AnyPointy = PointyShape d => AnyPointy d
> class Drawable d where
>   toPointy :: d -> Maybe AnyPointy

and so on.  But beware the difference between the two signatures

 Drawable a => a -> a -> ...
 (Drawable a,Drawable b) => a -> b -> ...

as pointed out in the OOP vs type classes article.  The former needs a
guarantee that the two arguments are the SAME drawable, and once
you're in an existential, you've lost that information (again, short
of doing some hokey stuff with extra class methods, i.e. convert ::
Drawable d' => d -> d' -> Maybe d
but that's just really ugly and you probably don't want to do that...)

Hope this helps,
steve
Andrew Coppin | 13 Oct 19:31

Re: Query regarding Type classes

Arun Suresh wrote:
> Hello folks,
>
> Im kinda new to haskell. Ive only been fiddling around with it for 
> bout 3 - 4 weeks now.
> And for the life of me... i cant seem to figure out why this doesnt work :
>
>
> class Foo a where
>     fooFunc :: a -> Int
>
> data FooData = FData
>
> instance Foo FooData where
>     fooFunc _ = 10
>
>
>
> class Bar a where
>     barFunc :: (Foo b) => a -> b -> Int
>
> data BarData = BData
>
> instance Bar BarData where
>     barFunc _ FData = 20

Ooo, ooo, I know this one, it's... oh, wait, I've been at work all day, 
so by now 30 other people have already replied. Heh. I need to find 
another day job... ;-)

Gmane