tags:

views:

147

answers:

2

How do you extract a value from a variable of an unknown constructor?

For instance, I would like to negate the value in an Either if was constructed as a Right:

let Right x = getValue
in Right (negate x)

This code successfully binds Right's value (an Int in this case) to x.

This works, but what if getValue returns a Left instead? Is there a way to determine the type of a variable in a let expression? Or is there a better way to approach this problem?

+15  A: 

In general, what you can do is this:

case getValue of
  Right x -> Right $ negate x
  e       -> e

What this does should be clear: it's just like pattern matching in a function argument, but against a value. To do what you need, you have a default case which catches anything not matched, and then return that.

In your particular case, however, you can do something slightly nicer:

negate `fmap` getValue

Or, with import Control.Applicative, you can use <$> as a synonym for fmap (negate <$> getValue). The fmap function has type fmap :: Functor f => (a -> b) -> f a -> f b. For any functor1, fmap converts a function on ordinary values to a function within the functor. For instance, lists are a functor, and for lists, fmap = map. Here, Either e represents a functor which is either an exception Left e or a value Right a; applying a function to a Left does nothing, but applying a function to a Right applies it within the Right. In other words,

instance Functor (Either e) where
  fmap _ le@(Left _) = le
  fmap f (Right r)   = Right $ f r

Thus the case version is the direct answer to your question, but your particular example is more nicely approximated by fmap.

1: To a first approximation, functors are "containers". If you're not comfortable with the various type classes, I recommed the Typeclassopedia for a comprehensive reference; there are many more tutorials out there, and the best way to get a feel for them is to just play with them. However, the fmap for specific types is often readily usable (especially, in my opinion, when written <$>).

Antal S-Z
And don't forget nice `either Left (Right . negate) getValue` from `Data.Either` ;)
ony
Thanks. The case expression is what I was looking for. I don't understand the rest of your answer; I will come back to it once I've learned more.
titaniumdecoy
@ony: I think you should write your comment up as a seperate answer, it is really the intermediate level solution between the `case` and the `fmap`!
yatima2975
@yatima2975: I guess question is more general - pattern matching. `fmap` and `either` is just for particular example (where type `Either` is used).
ony
+2  A: 

Answer to the title of this question:
I don't see big difference between "... where" and "let ... in ...". Both allows you do declare several cases of function argument bindings:

f val = let negR (Right x) = Right (negate x)
            negR y = y
        in negR val

or let { negR (Right x) = Right (negate x); negR y = y; } in negR val

ony