Given that I think this is homework, I'll not answer, but give important hints:
If you look for the definitions on hoogle (http://www.haskell.org/hoogle/)
you find
data Bool = True | False
data Either a b = Left a | Right b
This means that Bool
can only be True
or False
, but that Either a b
can be Left a
or Right b
.
which means your functions should look like
pairWithBoolToEither :: (Bool,a) -> Either a a
pairWithBoolToEither (True,a) = ....
pairWithBoolToEither (False,a) = ....
and
eitherToPairWithBool :: Either a a -> (Bool,a)
eitherToPairWithBool (Left a) = ....
eitherToPairWithBool (Right a) = ....
Comparing with Maybe
Maybe a
is given by
data Maybe a = Just a | Nothing
so something of type Maybe Int
could be Just 7
or Nothing
.
Similarly, something of type Either Int Char
could be Left 5
or Right 'c'
.
Something of type Either Int Int
could be Left 7
or Right 4
.
So something with type Either Int Char
is either an Int
or a Char
, but something of type Either Int Int
is either an Int
or an Int
. You don't get to choose anything other than Int
, but you'll know whether it was a Left
or a Right
.
Why you've been asked this/thinking behind it
If you have something of type Either a a
, then the data (eg 5
in Left 5
) is always of type a
, and you've just tagged it with Left
or Right
. If you have something of type (Bool,a)
the a
-data (eg 5
in (True,5)
) is always the same type, and you've paired it with False
or True
.
The maths word for two things which perhaps look different but actually have the same content is "isomorphic". Your instructor has asked you to write a pair of functions which show this isomorphism. Your answer will go down better if pairWithBoolToEither . eitherToPairWithBool
and eitherToPairWithBool . pairWithBoolToEither
do what id
does, i.e. don't change anything. In fact, I've just spotted the comments in your question, where it says they should be inverses. In your write-up, you should show this by doing tests in ghci like
ghci> eitherToPairWithBool . pairWithBoolToEither $ (True,'h')
(True,'h')
and the other way round.
(In case you haven't seen it, $
is defined by f $ x = f x
but $
has really low precedence (infixr 0 $
), so f . g $ x
is (f . g) $ x
which is just (f . g) x
and .
is function composition, so (f.g) x = f (g x)
. That was a lot of explanation to save one pair of brackets!)
Functions that take or return functions
This can be a bit mind blowing at first when you're not used to it.
functionFromBoolToPair :: (Bool -> a) -> (a,a)
The only thing you can pattern match a function with is just a variable like f
, so we'll need to do something like
functionFromBoolToPair f = ...
but what can we do with that f
? Well, the easiest thing to do with a function you're given is to apply it to a value. What value(s) can we use f
on? Well f :: (Bool -> a)
so it takes a Bool
and gives you an a
, so we can either do f True
or f False
, and they'll give us two (probably different) values of type a
. Now that's handy, because we needed to a
values, didn't we?
Next have a look at
pairToFunctionFromBool :: (a,a) -> (Bool -> a)
The pattern match we can do for the type (a,a)
is something like (x,y)
so we'll need
pairToFunctionFromBool (x,y) = ....
but how can we return a function (Bool -> a)
on the right hand side?
There are two ways I think you'll find easiest. One is to notice that since ->
is right associative anyway, the type (a,a) -> (Bool -> a)
is the same as (a,a) -> Bool -> a
so we can actually move the arguments for the function we want to return to before the = sign, like this:
pairToFunctionFromBool (x,y) True = ....
pairToFunctionFromBool (x,y) False = ....
Another way, which feels perhaps a little easier, would to make a let
or where
clause to define a function called something like f
, where f :: Bool -> a
< a bit like:
pairToFunctionFromBool (x,y) = f where
f True = ....
f False = ....
Have fun. Mess around.