How to combine case statement patterns [duplicate] - haskell

This question already has answers here:
Haskell syntax for 'or' in case expressions
(5 answers)
Closed 7 years ago.
I'm trying to match on many different constructors in a case statement. For simplicity, assume in half the cases we do the same thing, and in the other half we do something else. Even if I factor out the logic to another function, I still have to write:
case x of
C1 -> foo x
C2 -> foo x
...
C10 -> bar x
C11 -> bar x
...
Is there some way to make case statements behave more like switch statements in C (i.e. with fallthrough), or so that I can match on one of many patterns at once, like:
case x of
C1, C2, C3 -> foo x
C10, C11, C12 -> bar x
Or perhaps another way to clean this up?

These are called disjunctive patterns, and Haskell does not have them. (OCaml and F# do.) There are a few typical workarounds, however. If your type is an enumeration, you can use equality, with for example elem, using a case expression, guards, or MultiWayIf:
exampleCase cond = case cond of
c
| c `elem` [C1, C2, C3] -> foo
| c `elem` [C10, C11, C12] -> bar
| otherwise -> baz
exampleGuards c
| c `elem` [C1, C2, C3] -> foo
| c `elem` [C10, C11, C12] -> bar
| otherwise -> baz
exampleIf c
= additionalProcessing $ if
| c `elem` [C1, C2, C3] -> foo
| c `elem` [C10, C11, C12] -> bar
| otherwise -> baz
And of course, if foo or bar are long expressions, thanks to laziness you can simply factor them into local definitions, so you only have to repeat the name and any pattern variables you need as arguments:
exampleWhere cond = case cond of
C1 x -> foo x
C2 y -> foo y
…
C10 -> bar
C11 -> bar
…
where
foo x = something long (involving x, presumably)
bar = if you please then something else quite long
If you frequently group constructors together in this way, you can use the PatternSynonyms language option, which is especially useful in conjunction with ViewPatterns, to make your own patterns for matching such groups:
{-# Language
LambdaCase,
PatternSynonyms,
ViewPatterns #-}
-- Write one function to match each property.
fooish :: T -> Maybe X
fooish = \ case
C1 x -> Just x
C2 x -> Just x
…
C10 -> Nothing
C11 -> Nothing
…
-- May use a wildcard ‘_’ here; I prefer not to,
-- to require updating cases when a type changes.
barrish :: T -> Bool
barrish = \ case
C1{} -> False
C2{} -> False
…
C10 -> True
C11 -> True
…
-- Create synonyms for matching those properties.
-- (These happen to be unidirectional only.)
pattern Fooish :: T -> Foo
pattern Fooish x <- (fooish -> Just x)
pattern Barrish :: T -> Bar
pattern Barrish <- (barrish -> True)
-- If they cover all cases, tell the compiler so.
-- This helps produce useful warnings with ‘-Wall’.
{-# Complete Fooish, Barrish #-}
-- Use them just like normal patterns.
exampleSynonyms x = case x of
Fooish x -> …
…
Barrish -> …
…

Related

Merge multiple cases in Haskell

With a case _ of syntax like this:
fun a b c =
case (a, b, c) of
(Just True, Just _, _) -> foo
(Just True, _, Just _) -> foo
_ -> bar
Can I merge the first two conditions and avoid repeating foo?
Alternatively, is there any other (cleaner) way to express that I want to run foo if and only if a is Just True and either b or c are not Nothing?
You can do something like this:
fun a b c = case (a, b <|> c) of
(Just True, Just _) -> foo
_ -> bar
Of course, that's just hiding the extra match in (<|>), but you gotta pay the piper some time.
If b and c do not have the same type, you can cause them to with the ill-named void.
Let me complement the other answers with a "boring" alternative:
fun a b c = let
foo' = foo
in case (a, b, c) of
(Just True, Just _, _) -> foo'
(Just True, _, Just _) -> foo'
_ -> bar
This might or might not answer the intended question, depending on the actual goal.
If the goal was to avoid writing two patterns in the case, this of course fails to meet the goal.
If instead the goal was to avoid repeating foo, which could be a very long expression (e.g., a long do block in some monad), this meets the goal by giving a short name to the long expression.
Don't know if this looks cleaner but you could use the good old pal if too:
fun a b c =
if a == Just True && (isJust b || isJust c)
then foo
else bar
or use guards
fun a b c =
case a of
Just True | isJust b || isJust c -> foo
_ -> bar
without case:
fun (Just True) b c | isJust b || isJust c = foo
fun _ _ _ = bar
all is using isJust and as Daniel pointed out those too will give the piper it's due (pattern match).

Read of types sum

When I want to read string to type A I write read str::A. Consider, I want to have generic function which can read string to different types, so I want to write something like read str::A|||B|||C or something similar. The only thing I could think of is:
{-# LANGUAGE TypeOperators #-}
infixr 9 |||
data a ||| b = A a|B b deriving Show
-- OR THIS:
-- data a ||| b = N | A a (a ||| b) | B b (a ||| b) deriving (Data, Show)
instance (Read a, Read b) => Read (a ||| b) where
readPrec = parens $ do
a <- (A <$> readPrec) <|> (B <$> readPrec)
-- OR:
-- a <- (flip A N <$> readPrec) <|> (flip B N <$> readPrec)
return a
And if I want to read something:
> read "'a'"::Int|||Char|||String
B (A 'a')
But what to do with such weird type? I want to fold it to Int or to Char or to String... Or to something another but "atomic" (scalar/simple). Final goal is to read strings like "1,'a'" to list-like [D 1, D 'a']. And main constraint here is that structure is flexible, so string can be "1, 'a'" or "'a', 1" or "\"xxx\", 1, 2, 'a'". I know how to read something separated with delimiter, but this something should be passed as type, not as sum of types like C Char|I Int|S String|etc. Is it possible? Or no way to accomplish it without sum of types?
There’s no way to do this in general using read, because the same input string might parse correctly to more than one of the valid types. You could, however, do this with a function like Text.Read.readMaybe, which returns Nothing on ambiguous input. You might also return a tuple or list of the valid interpretations, or have a rule for which order to attempt to parse the types in, such as: attempt to parse each type in the order they were declared.
Here’s some example code, as proof of concept:
import Data.Maybe (catMaybes, fromJust, isJust, isNothing)
import qualified Text.Read
data AnyOf3 a b c = FirstOf3 a | SecondOf3 b | ThirdOf3 c
instance (Show a, Show b, Show c) => Show (AnyOf3 a b c) where
show (FirstOf3 x) = show x -- Can infer the type from the pattern guard.
show (SecondOf3 x) = show x
show (ThirdOf3 x) = show x
main :: IO ()
main =
(putStrLn . unwords . map show . catMaybes . map readDBS)
["True", "2", "\"foo\"", "bar"] >>
(putStrLn . unwords . map show . readIID) "100"
readMaybe' :: (Read a, Read b, Read c) => String -> Maybe (AnyOf3 a b c)
-- Based on the function from Text.Read
readMaybe' x | isJust a && isNothing b && isNothing c =
(Just . FirstOf3 . fromJust) a -- Can infer the type of a from this.
| isNothing a && isJust b && isNothing c =
(Just . SecondOf3 . fromJust) b -- Can infer the type of b from this.
| isNothing a && isNothing b && isJust c =
(Just . ThirdOf3 . fromJust) c -- Can infer the type of c from this.
| otherwise = Nothing
where a = Text.Read.readMaybe x
b = Text.Read.readMaybe x
c = Text.Read.readMaybe x
readDBS :: String -> Maybe (AnyOf3 Double Bool String)
readDBS = readMaybe'
readToList :: (Read a, Read b, Read c) => String -> [AnyOf3 a b c]
readToList x = repack FirstOf3 x ++ repack SecondOf3 x ++ repack ThirdOf3 x
where repack constructor y | isJust z = [(constructor . fromJust) z]
| otherwise = []
where z = Text.Read.readMaybe y
readIID :: String -> [AnyOf3 Int Integer Double]
readIID = readToList
The first output line echoes every input that parsed successfully, that is, the Boolean constant, the number and the quoted string, but not bar. The second output line echoes every possible interpretation of the input, that is, 100 as an Int, an Integer and a Double.
For something more complicated, you want to write a parser. Haskell has some very good libraries to build them out of combinators. You might look at one such as Parsec. But it’s still helpful to understand what goes on under the hood.

What GHC optimization is responsible for duplicating case expressions?

Given the following code:
{-# OPTIONS_GHC -funbox-strict-fields #-}
module Test where
data X = X !Int !Int
test (X a b) (X c d) = X (max a c) (max b d)
GHC generates this core when compiling with optimizations (renamed to make reading easier):
test
test =
\ u v ->
case u of x { X y z ->
case v of c { X d e ->
case tagToEnum# (<=# y d) of _ {
False ->
case tagToEnum# (<=# z e) of _ {
False -> x;
True -> X y e
};
True ->
case tagToEnum# (<=# z e) of _ {
False -> X d z;
True -> c
}
}
}
}
Note how GHC has generated in total 4 different code paths. In general, the number of code paths grows exponentially with the number of conditions.
What GHC optimization leads to that behavior? Is there a flag to control this optimization? In my case, this generates huge code bloat, and makes core dumps very hard to read because of deeply nested case expressions.
After some research, I've found that the optimization responsible for this is the so called "case-of-case" transformation, that GHC does presumably in the simplifier, so it cannot be deactivated (since it is necessary for a lot of what GHC does and the simplifier is an integral part of GHC's optimization pipeline).
The following link explains how case of case leads to the duplication: http://lambda.jstolarek.com/2013/01/taking-magic-out-of-ghc-or-tracing-compilation-by-transformation/
In particular, case-of-case turns this:
case (
case C of
B1 -> F1
B2 -> F2
) of
A1 -> E1
A2 -> E2
into the following:
case C of
B1 -> case F1 of
A1 -> E1
A2 -> E2
B2 -> case F2 of
A1 -> E1
A2 -> E2
where the outer case has been duplicated and pushed into the branches.

Haskell - pattern matching syntactic sugar and where

Often I have a function of such pattern:
f :: a -> b
f x = case x of
... -> g ...
... -> g ...
...
... -> g ...
where g = ...
There is an syntactic sugar for almost this case:
f :: a -> b
f ... = g ...
f ... = g ...
...
f ... = g ...
Unfortunately I can't attach my where to it: I'll obviously get bunch of not in scopes.
I can make g a separate function, but it's not nice: my module's namespace will be polluted with utility functions.
Is there any workaround?
I think that your first example isn't bad at all. The only syntactic weight is case x of, plus -> instead of =; the latter is offset by the fact that you can omit the function name for each clause. Indeed, even dflemstr's proposed go helper function is syntactically heavier.
Admittedly, it's slightly inconsistent compared to the normal function clause syntax, but this is probably a good thing: it more precisely visually delimits the scope in which x is available.
No, there is no workaround. When you have multiple clauses for a function like that, they cannot share a where-clause. Your only option is to use a case statement, or do something like this:
f x =
go x
where
go ... = g ...
go ... = g ...
g = ...
...if you really want to use a function form for some reason.
f = g . h -- h is most of your original f
where h ... = ...
h ... = ...
g =
From Haskell 2010 on, or with GHC you can also do:
f x
| m1 <- x = g
| m2 <- x = g
...
where g =
but note that you cannot use the variables bound in the patterns in g. It's equivalent to:
f x = let g = ... in case () of
() -> case x of
m1 -> g
_ -> case x of
m2 -> g
....
Your original solution seems to be the best and only workaround. Syntactically it's not any heavier than direct pattern matching on function parameters if not even lighter.
But just in case if what you need is just to check preconditions and not pattern match don't forget about guards, which allow you to access the where scope freely. But really I see nothing bad in your case of solution.
f :: a -> b
f a
| a == 2 = ...
| isThree a = ...
| a >= 4 = ...
| otherwise = ...
where isThree x = x == 3
With LambdaCase, you can also do this:
{-# language LambdaCase #-}
f :: a -> b
f = \case
... -> g ...
... -> g ...
...
... -> g ...
where g = ...
Is it safe to assume that you consistently use g on most, if not all, of the different branches of the case statement?
Operating with the assumption that f :: a -> b for some a and b (possibly polymorphic), g is necessarily some function of the form c -> d, which means that there must be a way to consistently extract a c out of an a. Call that getC :: a -> c. In that case, the solution would be to simply use h . g . getC for all cases, where h :: d -> b.
But suppose you can't always get the c out of an a. Perhaps a is of the form f c, where f is a Functor? Then you could fmap g :: f c -> f d, and then somehow transform f d into a b.
Just sort of rambling here, but fmap was the first thing that came to mind when I saw that you appeared to be applying g on every branch.

Pattern-matching in case, Haskell

I'm fairly new to Haskell and have a question about pattern-matching.
Here is a heavily simplified version of the code:
data Value = MyBool Bool | MyInt Integer
codeDuplicate1 :: Value -> Value -> IO Value
codeDuplicate1 = generalFunction True
codeDuplicate2 :: Value -> Value -> IO Value
codeDuplicate2 = generalFunction False
generalFunction :: Bool -> Value -> Value -> IO Value
generalFunction b x1 x2 = do result <- eval x1
case result of
MyBool b -> do putStrLn $ show b
return (MyBool b)
_ -> eval x2
eval :: Value -> IO Value
eval (MyInt x) | x > 10 = return (MyInt 10)
| x > 5 = return (MyBool True)
| otherwise = return (MyBool False)
Now, I realize that the argument b in generalFunction is not the same as the b in the case part, and therefore, this code will print b regardless of the input. I used the same name just to show my intentions. So my question is:
Is there a way to match the first b with the second, so that if the bs are the same it will print, otherwise it will evaluate x2? And, if there isn't, is there another good way to get the intended result?
I almost found the answer in this question, but I think this situation is slightly different.
You can use a guarded pattern. The first alternative will be executed if MyBool is matched and b == b2; otherwise the second alternative will be executed.
case result of
MyBool b2 | b == b2 -> do {print b; return $ MyBool b}
_ -> eval x2

Resources