Why isn't pattern matching lazy by default?

I'm experimenting with functional reactive programming for creating simple 2D/3D video games and interactive apps, trying to develop my own version of it from scratch, for learning Haskell. I got stuck with an endless loop when trying to split a stream into a pair of two streams (a kind of reactive if/then/else). Luckily I first read the Haskell School of Expression so I remembered that pattern matching is not lazy and this could be the cause, which it was (I had to replace (x:xs) by ~(x:xs)) I could also fix the problem by not using pattern matching at all, using head/tail calls instead. Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default. Thanks, Peter

On 9/19/07, Peter Verswyvelen
Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
AFAIK, pattern matches are desugared to cases, so f (x:xs) = rhs f [] = rhs' is equivalent to f y = case y of (x:xs) -> rhs [] -> rhs' Case's have to be strict because that's how we look inside the values in Haskell =). Of course I may be somehow mistaken here, as I am learning Haskell, too. -- Felipe.

Mmm, yes of course... blush... But shouldn't f ~(x:xs) = rhs give a compile-time error since neither x nor xs is used in the right hand side, and hence nothing will ever get pattern matched when this function is called, which clearly indicates a mistake? That is, if I understand lazy pattern matching correctly... And then in these cases the user would have to annotate the pattern match as being strict, so he is aware of the eager evaluation taking place Oh well, the way it is now is also easy to get used to, one just has to know how it works (just like M-theory ;-) ) Cheers, Peter Neil Mitchell wrote:
Hi
Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
f ~(x:xs) = rhs f ~[] = rhs'
Now guess what f [] does...
If you use a where binding then pattern matching is lazy.
Thanks
Neil

Hi Peter,
Mmm, yes of course... blush...
But shouldn't
f ~(x:xs) = rhs
give a compile-time error since neither x nor xs is used in the right hand side, and hence nothing will ever get pattern matched when this function is called, which clearly indicates a mistake? That is, if I understand lazy pattern matching correctly... And then in these cases the user would have to annotate the pattern match as being strict, so he is aware of the eager evaluation taking place
Well if you defined f: f ~(x:xs) = 1 + 2 f ~[] = 42 Then you will get a warning stating that the pattern matches of (x:xs) and [] are overlapped. It may not be a mistake though, so possibly a bold error for the compiler to throw, it just means that 1+2 will always be evaulated no matter what list you throw at it (provided of course that the result of f is needed to evaluate the rest of the program). It's interesting to note that if you had: f ~(x:xs) = x + 2 f ~[] = 42 Then f [] would give a complie error: Irrefutable pattern failed for pattern (x : xs) Hope that gives some insight. Chris.
Oh well, the way it is now is also easy to get used to, one just has to know how it works (just like M-theory ;-) )
Cheers, Peter
Neil Mitchell wrote:
Hi
Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
f ~(x:xs) = rhs f ~[] = rhs'
Now guess what f [] does...
If you use a where binding then pattern matching is lazy.
Thanks
Neil

f ~(x:xs) = x + 2 f ~[] = 42
Then f [] would give a complie error: Irrefutable pattern failed for pattern (x : xs)
Sorry, that should be *runtime* error! Chris.
Hope that gives some insight. Chris.
Oh well, the way it is now is also easy to get used to, one just has to know how it works (just like M-theory ;-) )
Cheers, Peter
Neil Mitchell wrote:
Hi
Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
f ~(x:xs) = rhs f ~[] = rhs'
Now guess what f [] does...
If you use a where binding then pattern matching is lazy.
Thanks
Neil
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

C.M.Brown wrote:
f ~(x:xs) = x + 2 f ~[] = 42
Then f [] would give a complie error: Irrefutable pattern failed for pattern (x : xs)
Sorry, that should be *runtime* error!
Chris.
It seems GHC does give a warning at compile-time about it, so you did get it right the first time :-) Thanks for the info, Peter

It seems GHC does give a warning at compile-time about it, so you did get it right the first time :-)
Well the warning happens at compile time certainly. But the irrefutable pattern error only occurs at runtime. cmb21$ ghc --make Main.hs [1 of 1] Compiling Main ( Main.hs, Main.o ) Main.hs:3:0: Warning: Pattern match(es) are overlapped In the definition of `f': f ~[] = ... Linking Main ... cmb21$ ./a.out a.out: Main.hs:(3,0)-(4,10): Irrefutable pattern failed for pattern (x : xs) Cheers, Chris.
Thanks for the info, Peter

On Wed, 19 Sep 2007, Peter Verswyvelen wrote:
I got stuck with an endless loop when trying to split a stream into a pair of two streams (a kind of reactive if/then/else). Luckily I first read the Haskell School of Expression so I remembered that pattern matching is not lazy and this could be the cause, which it was (I had to replace (x:xs) by ~(x:xs))
... Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
It's even more confusing that pattern matching in 'let' _is_ lazy.

Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
It's even more confusing that pattern matching in 'let' _is_ lazy.
No, it's not. See, in let or where constructs you don't have a choice; you can't do different things depending on whether some value is Just x or Nothing. Therefore, there is no need to perform pattern matching strictly.

2007/9/19, Miguel Mitrofanov
Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
It's even more confusing that pattern matching in 'let' _is_ lazy.
No, it's not.
See, in let or where constructs you don't have a choice; you can't do different things depending on whether some value is Just x or Nothing. Therefore, there is no need to perform pattern matching strictly.
In other words, the pattern matching in case can't be lazy by default since the primary purpose of case is to choose an alternative and for that you NEED to evaluate the matched value. In let or where, the pattern matching is only there to allow convenient deconstruction of the value, but you don't make any choice based on the form of the value, so you can afford to be lazy (which is the default in the language everywhere it makes sense, it doesn't in a normal "case" construct). If you want to use your pattern matching in a case only to deconstruct the value but not to choose an alternative, you need to specify it using ~ . So, IMHO, the rules on strictness of the pattern matching are perfectly consistent and clear. You just need to reflect on the purpose of the constructs where you use it.. -- Jedaï

Chaddaï Fouché wrote:
2007/9/19, Miguel Mitrofanov
: Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default. It's even more confusing that pattern matching in 'let' _is_ lazy. No, it's not.
See, in let or where constructs you don't have a choice; you can't do different things depending on whether some value is Just x or Nothing. Therefore, there is no need to perform pattern matching strictly.
In other words, the pattern matching in case can't be lazy by default since the primary purpose of case is to choose an alternative and for that you NEED to evaluate the matched value. In let or where, the pattern matching is only there to allow convenient deconstruction of the value, but you don't make any choice based on the form of the value, so you can afford to be lazy (which is the default in the language everywhere it makes sense, it doesn't in a normal "case" construct). If you want to use your pattern matching in a case only to deconstruct the value but not to choose an alternative, you need to specify it using ~ .
So, IMHO, the rules on strictness of the pattern matching are perfectly consistent and clear. You just need to reflect on the purpose of the constructs where you use it..
Except that newtype deconstruction doesn't introduce strictness - and you can't necessarily see from the syntax of a pattern-match whether it's newtype or data. Isaac

On Wed, 19 Sep 2007, Miguel Mitrofanov wrote:
Now why isn't pattern matching lazy by default? This seems odd for a newbie since everything else is lazy by default.
It's even more confusing that pattern matching in 'let' _is_ lazy.
No, it's not.
See, in let or where constructs you don't have a choice; you can't do different things depending on whether some value is Just x or Nothing. Therefore, there is no need to perform pattern matching strictly.
Then why are patterns in lambdas not lazy?

On 9/19/07, Roberto Zunino
Henning Thielemann wrote:
Then why are patterns in lambdas not lazy?
Because they should allow for more branches! ;-))
null = \ [] -> True _ -> False
See http://hackage.haskell.org/trac/haskell-prime/ticket/114 for a relevant proposal for Haskell Prime regarding something like this. Bas

Hi Miguel,
See, in let or where constructs you don't have a choice; you can't do different things depending on whether some value is Just x or Nothing. Therefore, there is no need to perform pattern matching strictly.
This is not entirely true. This is actually one of those niches in Haskell where the left to right is not quite the same as right to left. A let can be converted to a where but the other way round may require a case introduction. So just like you can define: f (Just x) = x f Nothing = error "Nothing" You can also define: f x = g x where g (Just x) = x g Nothing = error "Nothing" g is strict in its first argument. Declared in a let it would look like: f x = let g x = case x of (Just y) -> y Nothing -> error "Nothing" in g x Again, g must be strict in its first argument. Chris.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

C.M.Brown wrote:
Hi Miguel,
See, in let or where constructs you don't have a choice; you can't do different things depending on whether some value is Just x or Nothing. Therefore, there is no need to perform pattern matching strictly.
This is not entirely true. This is actually one of those niches in Haskell where the left to right is not quite the same as right to left. A let can be converted to a where but the other way round may require a case introduction.
So just like you can define:
f (Just x) = x f Nothing = error "Nothing"
You can also define:
f x = g x where g (Just x) = x g Nothing = error "Nothing"
g is strict in its first argument. Declared in a let it would look like:
f x = let g x = case x of (Just y) -> y Nothing -> error "Nothing" in g x
Again, g must be strict in its first argument.
or f x = let g (Just x) = x g Nothing = error "Nothing" in g x also works perfectly fine. The (Just x) and Nothing are not let-patterns, they are function-definition-patterns, and of course are strict since they make a decision. Isaac

On 9/19/07, C.M.Brown
f x = let g (Just x) = x g Nothing = error "Nothing" in g x
That's interesting, thanks!
FYI, you may also use guards, even if you don't have a parameter. For example, you may turn f x = let k = if null x then 10 else head x in froobar $ quox k into f x = let k | null x = 10 | otherwise = head x in froobar $ quox k Okay, this example is ugly, however it serves the purpose of illustrating the idea =). -- Felipe.

On Wednesday 19 September 2007, C.M.Brown wrote:
g is strict in its first argument. Declared in a let it would look like:
f x = let g x = case x of (Just y) -> y Nothing -> error "Nothing" in g x
Again, g must be strict in its first argument.
Actually, f x = let g (Just x) = x g Nothing = error "Nothing" in g x is a valid definition. A let expression can have multiple definitions just like a where clause. -- Dan
participants (11)
-
Bas van Dijk
-
C.M.Brown
-
Chaddaï Fouché
-
Dan Doel
-
Felipe Almeida Lessa
-
Henning Thielemann
-
Isaac Dupree
-
Miguel Mitrofanov
-
Neil Mitchell
-
Peter Verswyvelen
-
Roberto Zunino