Difference between revisions of "Case"

From HaskellWiki
Jump to navigation Jump to search
(moved from Hawiki)
 
(add MultiWayIf)
 
(12 intermediate revisions by 4 users not shown)
Line 1: Line 1:
  +
== Question ==
Can I have a {{{case}}} where the alternatives contain expressions?
 
   
  +
Can I have a <hask>case</hask> where the alternatives contain expressions?
   
  +
== Answer ==
You can make use of some SyntacticSugar of Haskell, namely of ["Guards"].
 
   
  +
There are several approaches to this problem.
{{{#!syntax haskell
 
case () of _
 
| cond1 -> ex1
 
| cond2 -> ex2
 
| cond3 -> ex3
 
| otherwise -> exDefault
 
}}}
 
   
  +
=== Using functions ===
Alternatively, one could simply factor out a function(/value) and use guards in the argument patterns.
 
   
  +
==== select ====
Why sticking to syntactic sugar? We can do it nicely with a function implemented in Haskell:
 
  +
{{{#!syntax haskell
 
  +
We can do this nicely with a function implemented in Haskell:
  +
<haskell>
 
select :: a -> [(Bool, a)] -> a
 
select :: a -> [(Bool, a)] -> a
 
select def = maybe def snd . List.find fst
 
select def = maybe def snd . List.find fst
  +
-- = fromMaybe def . lookup True
 
  +
-- = maybe def id . lookup True
   
 
select exDefault
 
select exDefault
Line 24: Line 22:
 
(cond2, ex2),
 
(cond2, ex2),
 
(cond3, ex3)]
 
(cond3, ex3)]
  +
</haskell>
}}}
 
  +
Unfortunately this function is not in the [[Prelude]].
  +
It is however in the [http://hackage.haskell.org/packages/archive/utility-ht/0.0.1/doc/html/Data-Bool-HT.html#v%3Aselect utility-ht] package.
   
  +
==== nested 'if' ====
Alternative implementations are
 
{{{#!syntax haskell
 
select' def = fromMaybe def . lookup True
 
   
  +
Alternative implementations are
  +
<haskell>
 
{- a purely functional implementation of if-then-else -}
 
{- a purely functional implementation of if-then-else -}
 
if' :: Bool -> a -> a -> a
 
if' :: Bool -> a -> a -> a
Line 36: Line 36:
   
 
select'' = foldr (uncurry if')
 
select'' = foldr (uncurry if')
  +
</haskell>
}}}
 
The implementation of {{{select''}}} makes clear that {{{select}}} can be considered as nested {{{if}}}s.
+
The implementation of <hask>select''</hask> makes clear that <hask>select</hask> can be considered as nested <hask>if</hask>s.
The functional {{{if'}}} is also useful in connection with {{{zipWith3}}} since {{{zipWith3 if'}}} merges two lists according to a list of conditions.
+
The functional <hask>if'</hask> is also useful in connection with <hask>zipWith3</hask> since <hask>zipWith3 if'</hask> merges two lists according to a list of conditions.
  +
See [[if-then-else]].
   
  +
Alternatively you can unroll <hask>foldr</hask> and write
  +
<haskell>
  +
if' cond1 ex1 $
  +
if' cond2 ex2 $
  +
if' cond3 ex3 $
  +
exDefault
  +
</haskell>
   
  +
==== infix operator ====
If you don't like the parentheses for the pairs, you can also define
 
{{{#!syntax haskell
 
data SelectBranch a = (:->) {
 
condition :: Bool,
 
expression :: a
 
}
 
   
  +
If you use <hask>if'</hask> in infix form,
select :: a -> [SelectBranch a] -> a
 
  +
you may call it <hask>?</hask> like in C,
select def = maybe def expression . List.find condition
 
  +
then because of partial application it will work nicely together with '$' for the else clause.
  +
<haskell>
  +
infixl 1 ?
  +
(?) :: Bool -> a -> a -> a
  +
(?) = if'
   
  +
cond1 ? ex1 $
  +
cond2 ? ex2 $
  +
cond3 ? ex3 $
  +
exDefault
  +
</haskell>
   
  +
=== Using syntactic sugar ===
select exDefault
 
  +
[cond1 :-> ex1,
 
  +
==== Guards ====
cond2 :-> ex2,
 
  +
cond3 :-> ex3]
 
  +
You can make use of some [[syntactic sugar]] of Haskell, namely of [[guard]]s.
}}}
 
  +
  +
<haskell>
  +
case () of _
  +
| cond1 -> ex1
  +
| cond2 -> ex2
  +
| cond3 -> ex3
  +
| otherwise -> exDefault
  +
</haskell>
  +
  +
Alternatively, one could simply factor out a function(/value) and use guards in the argument patterns.
  +
  +
==== List comprehensions ====
  +
  +
An alternative sugarful approach is to use [[list comprehension]]s.
  +
  +
<haskell>
  +
head $
  +
[ ex1 | cond1 ] ++
  +
[ ex2 | cond2 ] ++
  +
[ ex3 | cond3 ] ++
  +
[ exDefault ]
  +
</haskell>
  +
  +
=== MultiWayIf ===
  +
  +
The MultiWayIf extension lets you write code similar to a <hask>case () of _</hask> form, using only the word <hask>if</hask>. To enable it, add <hask>{-# LANGUAGE MultiWayIf #-}</hask> to the top of a <tt>.hs</tt> file, run ghci with <tt>ghci -XMultiWayIf</tt>, or add <tt>MultiWayIf</tt> to the <tt>default-extensions</tt> in your <tt>.cabal</tt> file.
  +
  +
<haskell>
  +
if | guard1 -> expr1
  +
| ...
  +
| guardN -> exprN
  +
</haskell>
  +
  +
[[Category:FAQ]]
  +
[[Category:Idioms]]

Latest revision as of 11:36, 11 June 2020

Question

Can I have a case where the alternatives contain expressions?

Answer

There are several approaches to this problem.

Using functions

select

We can do this nicely with a function implemented in Haskell:

select :: a -> [(Bool, a)] -> a
select def = maybe def snd . List.find fst
        -- = fromMaybe def . lookup True
        -- = maybe def id . lookup True

select exDefault
    [(cond1, ex1),
     (cond2, ex2),
     (cond3, ex3)]

Unfortunately this function is not in the Prelude. It is however in the utility-ht package.

nested 'if'

Alternative implementations are

{- a purely functional implementation of if-then-else -}
if' :: Bool -> a -> a -> a
if' True  x _ = x
if' False _ y = y

select'' = foldr (uncurry if')

The implementation of select'' makes clear that select can be considered as nested ifs. The functional if' is also useful in connection with zipWith3 since zipWith3 if' merges two lists according to a list of conditions. See if-then-else.

Alternatively you can unroll foldr and write

if' cond1 ex1 $
if' cond2 ex2 $
if' cond3 ex3 $
   exDefault

infix operator

If you use if' in infix form, you may call it ? like in C, then because of partial application it will work nicely together with '$' for the else clause.

infixl 1 ?
(?) :: Bool -> a -> a -> a
(?) = if'

cond1 ? ex1 $
cond2 ? ex2 $
cond3 ? ex3 $
   exDefault

Using syntactic sugar

Guards

You can make use of some syntactic sugar of Haskell, namely of guards.

case () of _
             | cond1     -> ex1
             | cond2     -> ex2
             | cond3     -> ex3
             | otherwise -> exDefault

Alternatively, one could simply factor out a function(/value) and use guards in the argument patterns.

List comprehensions

An alternative sugarful approach is to use list comprehensions.

head $
  [ ex1 | cond1 ] ++
  [ ex2 | cond2 ] ++
  [ ex3 | cond3 ] ++
  [ exDefault ]

MultiWayIf

The MultiWayIf extension lets you write code similar to a case () of _ form, using only the word if. To enable it, add {-# LANGUAGE MultiWayIf #-} to the top of a .hs file, run ghci with ghci -XMultiWayIf, or add MultiWayIf to the default-extensions in your .cabal file.

if | guard1 -> expr1
   | ...
   | guardN -> exprN