Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class instance of two variables

Tags:

haskell

I'm very new to Haskell and created this class

class Printable a where 
    toString :: a -> String

And two of its instances

instance Printable Bool where
    toString x | x == True = "true"
               | x == False  = "false" 

instance Printable () where
    toString x = "unit type"

They work just fine, and now I want to create an instance that would take two parameters, but I'm having troubles. What I created so far is

instance (Printable a, Printable b) => Printable (a, b) where
    toString x | fst x == True = "(true,unit type)"

But now I get the error

Couldn't match expected type ‘a’ with actual type ‘Bool’

which is reasonable, but when I replace a with Bool I also get an error. How can I make it work in the first place, and can I restrict the types for a and b - for example, what if I want a to always be Bool and b to always be ()?

like image 372
EmberTraveller Avatar asked Jan 24 '26 07:01

EmberTraveller


2 Answers

You wrote an instance where you write that for a tuple (a, b), the tuple is Printable in case the items a and b are Printable. But that does not mean that a or b are Bools, or units.

You may think that - like in many languages - the (==) operator works on any two types, but in Haskell (==) has type (==) :: Eq a => a -> a -> Bool, so it is a function where the left and the right operand have the same type. So we can not use x == True, unless x is a Bool.

We do not need these guards. What we need to do is call the toString function of the operands, so:

instance (Printable a, Printable b) => Printable (a, b) where
    toString (x,y) = "(" ++ toString x ++ "," ++ toString y ++ ")"

We can also replace [x] ++ with x : ... so we can rewrite it to:

instance (Printable a, Printable b) => Printable (a, b) where
    toString (x,y) = '(' :  toString x ++ ',' : toString y ++ ")"

EDIT: in case you want a to be always a Bool, you can write:

instance Printable b => Printable (Bool, b) where
    toString (x,y) = '(' :  toString x ++ ',' : toString y ++ ")"

You can of course now add guards, pattern matching, or some other method to discriminate between x == True and x == False, but I advice you not to do this, since this violates the DRY principle: Don't Repeat Yourself.

like image 135
Willem Van Onsem Avatar answered Jan 25 '26 23:01

Willem Van Onsem


Just because a has a Printable instance doesn't mean you can assume a ~ Bool, so x == True fails.

You should be using pattern matching instead of equality, though, which would sidestep the issue.

class Printable a where 
    toString :: a -> String

instance Printable Bool where
    toString True = "true"
    toString False = "false"

instance Printable () where
    toString () = "unit type"

Your tuple instance should then make use of the fact that a and b are both Printable. You only need to pattern-match on the constructor, not the individual components.

instance (Printable a, Printable b) => Printable (a, b) where
    toString (x, y)  = "(" ++ toString x ++ "," ++ toString y ++ ")"
like image 38
chepner Avatar answered Jan 25 '26 23:01

chepner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!