just.me
just.me

Reputation: 2285

Pattern matching for return values

I know I can use pattern matching for function parameters like this:

fn :: (Integral a) => (a,a) -> (a, a)
fn (x,y) = (y,x)

But how can I match the return value? I would expect something like this:

g :: (Integral a) => a -> a
g z = do
  (x, y) = fn (z, z + 5)
  x `mod` y

This results in a syntax error. Is there a way to match return values? Basically, I want to split the returned tuple into two variables.

Upvotes: 3

Views: 1447

Answers (2)

chi
chi

Reputation: 116139

My personal preference in the general case, is to use a case .. of expression. For instance, if f :: Int -> Maybe Int, we can write

g :: Int -> Int
g x = case f x of
   Nothing -> 5
   Just y  -> x+y

For types with only one constructor, like tuples, then let .. in can also be used:

h a = let (x, y) = foo a in ...

Remember, however, that case is strict while let is lazy. E.g.

case undefined of (x,y) -> 5

raises an error. Instead

let (x, y) = undefined in 5

evaluates to 5. So, they are not completely equivalent. They became such when using irrefutable patterns, or matching against a newtype constructor.

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

The do is used a syntactical sugar for monads. Your function is however not a monad.

What you can do is use a let-clause, like:

g :: (Integral a) => a -> a
g z = let (x,y) = fn (z,(z+5)) in x `mod` y

Or a where-clause:

g :: (Integral a) => a -> a
g z = x `mod` y
    where (x,y) = fn (z,(z+5))

You can also define a pattern in a lambda-expression, like:

g :: (Integral a) => a -> a
g z = (\(x,y) -> x `mod` y) $ fn (z,(z+5))

Along these lines, you can also define a helper function that does the pattern matching, like:

g :: (Integral a) => a -> a
g z = h $ fn (z,(z+5))
    where h (x,y) = x `mod` y

This can be useful if there are several patterns that need to be handled differently (like the Nothing and Just x for the Maybe a type).

Say for instance that you defined a function:

foo :: Int -> Int -> Maybe Int
foo x y | x > y = Just x
        | otherwise = Nothing

than you can define bar with a helper function qux to handle the output of foo, like:

bar :: Int -> Int -> Int
bar x y = qux $ foo x y
    where qux Nothing = y
          qux (Just z) = z

Finally in the case of 2-tuples, you can decide not to use pattern matching, but use fst :: (a,b) -> a and snd :: (a,b) -> b, like for instance:

g :: (Integral a) => a -> a
g z = let t = fn (z,(z+5)) in ( fst  t) `mod` (snd t)

But this is less elegant since here one has to start thinking about what fst and snd do, and furtermore if not optimized, it can result in additional computation overhead.

Which one to pick depends of course on the context and a bit on personal taste. Since here the pattern is the only one, I would pick the let or where pattern, but like the French say: "Les goûts et les couleurs ne se discutent pas.".

Upvotes: 9

Related Questions