Pattern guards in comprehensions

I propose we add pattern guards in comprehensions, but I'm not sure what syntax would work. Consider this function: allMinus :: [Natural] -> [Natural] allMinus m = mapMaybe (-? m) where n -? m | m > n = Nothing | True = Just (n-m) One may wish to write such as a list comprehension, but it is cumbersome: allMinus m ns = [n' | n@((-? m) -> Just n') <- ns] This would be clearer with pattern guards, fictitious syntax here: allMinus m ns = [n' | n <- ns, Just n' <- n -? m] Alas, this conflicts with the other part of list comprehension syntax. Try we this, actual syntax now: allMinus m ns = [n' | n <- ns, let Just n' = n -? m] Nope, that's an error if (any (< m) ns). I recognize in this case the one in terms of mapMaybe is quite clear, but in the case of some other code I'm writing it's much more complicated. Ideas: 1. Modify semantics of let in comprehension to skip that element on pattern mismatch 2. Use another keyword, e.g. [n' | n <- ns where Just n' <- n -? m] Thoughts?

This can be done today:
allMinus m ns = [ n' | n <- ns, Just n' <- [n -? m] ]
The syntax is a bit regrettable, but it works quite well. You could use `Just n' <- return (n -? m)` if you wanted, or be even more pedantic and make a synonym `patternMatch :: a -> [a]; patternMatch = return` and say `Just n' <- patternMatch (n -? m)`.
Richard
On Aug 9, 2015, at 8:48 PM, M Farkas-Dyck
I propose we add pattern guards in comprehensions, but I'm not sure what syntax would work. Consider this function:
allMinus :: [Natural] -> [Natural] allMinus m = mapMaybe (-? m) where n -? m | m > n = Nothing | True = Just (n-m)
One may wish to write such as a list comprehension, but it is cumbersome:
allMinus m ns = [n' | n@((-? m) -> Just n') <- ns]
This would be clearer with pattern guards, fictitious syntax here:
allMinus m ns = [n' | n <- ns, Just n' <- n -? m]
Alas, this conflicts with the other part of list comprehension syntax. Try we this, actual syntax now:
allMinus m ns = [n' | n <- ns, let Just n' = n -? m]
Nope, that's an error if (any (< m) ns).
I recognize in this case the one in terms of mapMaybe is quite clear, but in the case of some other code I'm writing it's much more complicated.
Ideas: 1. Modify semantics of let in comprehension to skip that element on pattern mismatch 2. Use another keyword, e.g. [n' | n <- ns where Just n' <- n -? m]
Thoughts? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Apologies if my earlier message came across as a bit brutal. When I first saw this pattern a little while ago, I was surprised by it too!
Cheers,
Richard
On Aug 9, 2015, at 11:55 PM, M Farkas-Dyck
On 09/08/2015 at 22:53:01 -0400, Richard Eisenberg wrote:
This can be done today:
allMinus m ns = [ n' | n <- ns, Just n' <- [n -? m] ]
Ah, obviously. Please excuse my earlier stupidity.

There's an even nicer way! Use view patterns:
allMinus m ns = [ n' | ((?- m) -> Just n') <- ns ]
I wrote a blog post about this when i discovered it but my blog's not up at the moment...
Tom
El Aug 9, 2015, a las 22:53, Richard Eisenberg
This can be done today:
allMinus m ns = [ n' | n <- ns, Just n' <- [n -? m] ]
The syntax is a bit regrettable, but it works quite well. You could use `Just n' <- return (n -? m)` if you wanted, or be even more pedantic and make a synonym `patternMatch :: a -> [a]; patternMatch = return` and say `Just n' <- patternMatch (n -? m)`.
Richard
On Aug 9, 2015, at 8:48 PM, M Farkas-Dyck
wrote: I propose we add pattern guards in comprehensions, but I'm not sure what syntax would work. Consider this function:
allMinus :: [Natural] -> [Natural] allMinus m = mapMaybe (-? m) where n -? m | m > n = Nothing | True = Just (n-m)
One may wish to write such as a list comprehension, but it is cumbersome:
allMinus m ns = [n' | n@((-? m) -> Just n') <- ns]
This would be clearer with pattern guards, fictitious syntax here:
allMinus m ns = [n' | n <- ns, Just n' <- n -? m]
Alas, this conflicts with the other part of list comprehension syntax. Try we this, actual syntax now:
allMinus m ns = [n' | n <- ns, let Just n' = n -? m]
Nope, that's an error if (any (< m) ns).
I recognize in this case the one in terms of mapMaybe is quite clear, but in the case of some other code I'm writing it's much more complicated.
Ideas: 1. Modify semantics of let in comprehension to skip that element on pattern mismatch 2. Use another keyword, e.g. [n' | n <- ns where Just n' <- n -? m]
Thoughts? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

On 10/08/2015 at 08:40:43 -0400, amindfv@gmail.com wrote:
There's an even nicer way! Use view patterns:
allMinus m ns = [ n' | ((?- m) -> Just n') <- ns ]
In this case it's not bad, but in some cases the guarded form is clearer, e.g. f z ys = [g x y | y@(flip h z -> Just x) <- ys] vs. f z ys = [g x y | y <- ys, Just x <- [h y z]] tho this is personal preference.

On Sun, Aug 9, 2015 at 10:23 PM, Richard Eisenberg
This can be done today:
allMinus m ns = [ n' | n <- ns, Just n' <- [n -? m] ]
The syntax is a bit regrettable, but it works quite well.
On a somewhat related note, this trick is a nice way to introduce let-like bindings in languages like Python that have comprehension syntax and no explicit support for pure intermediate bindings: [ str(x) + ' is ' + str(i) + ' dozens' for i in range(1, 9) for x in [12*i] ] It’s also useful when you want to simulate monadic-ish things sometimes: x = {'a': { 'b': 'c', 'd': 'e'}, 'f': 'g'} [ z for y in [x.get('a')] if y for z in [y.get('b')] if z ] # => ['c'] [ z for y in [x.get('q')] if y for z in [y.get('b')] if z ] # => [] Almost feels like having some simple monads!
participants (4)
-
amindfv@gmail.com
-
M Farkas-Dyck
-
Manuel Gómez
-
Richard Eisenberg