Reputation: 2285
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
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
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