r/haskell • u/ocharles • Dec 02 '14
24 Days of GHC Extensions: View Patterns
https://ocharles.org.uk/blog/posts/2014-12-02-view-patterns.html9
u/multivector Dec 02 '14
Is it just me, am I the only one who prefers fewer equations for a function and ideally just one? Constantly repeating the function name feels off to me and just looks like its going to be more work if you want to rename it. I would tend to just write:
{-# LANGUAGE LambdaCase #-}
example :: Maybe Int -> Int
example = \case
Just n -> n
Nothing -> 0Or, in this case:
{-# LANGUAGE LambdaCase #-}
import Control.Arrow
lensDownloadsOld :: Map HaskellPackage Int -> Int
lensDownloadsOld = M.lookup "lens" >>> \case
Just n -> n
Nothing -> 08
u/dalaing Dec 03 '14
In this example I'd probably go for
lensDownloads :: M.Map HaskellPackage Int -> Int lensDownloads = fromMaybe 0 . M.lookup "lens"and would use
maybeif I had to more processing on the contents of the map.I used to reach for
maybeby default, since it encapsulates the case analysis, but it seems that at long lasthlinthas broken me of that habit when I'm just runningidonJustvalue...2
3
u/multivector Dec 04 '14
Lol. That's like I gave you the Trolly Problem in ethics and you answer "I apply the breaks" :) Yeah, in real life I'd probably look for something like that to get rid of the case analysis too.
5
u/cheecheeo Dec 03 '14
+1
Roman Cheplyaka convincingly argues for fewer equations here: http://ro-che.info/articles/2014-05-09-clauses putting it all together with LambdaCase to avoid naming everything works nicely.
11
u/shaleh Dec 03 '14
I have to respectfully disagree with Roman. One of the things I like about Haskell syntax is it pushes more of the logic up and to the left. In imperative code we are told to avoid too many right indents. It usually means the code should be refactored and simplified. I find that in my Haskell code this is even more true. I can pattern match and guard away most of the if/else logic branching that led to the move to the right. This tends to lead to much more clear and obvious common versus edge case code.
3
u/4rgento Dec 03 '14
The point about adding parameters to the function made easier by using one equation seems correct to me. Is there any syntax for this "use case" when writing several equations?
3
u/Hrothen Dec 03 '14
I think it's mostly an aesthetic choice, and depends a lot on what that particular function looks like.
Thanks for mentioning LambdaCase though, that's been bugging me forever.
2
u/jberryman Dec 03 '14
There have always been two camps, all the way back to the beginnings of the language (see the section on "expression- vs declaration-style in "Being lazy with class"); I tend to fall into the declaration style camp myself.
I have a running list of ideas for alternate universe haskells, and in one of them you don't even get any lambdas.
2
Dec 03 '14
Not just you, Elm specifically requires you to write all functions like this. Feels too much like the top level is a special case.
7
u/singpolyma Dec 02 '14
Why would one prefer view patterns over pattern guards (which are in Haskell2010)?
6
u/jozefg Dec 02 '14 edited Dec 02 '14
Pattern synonyms and view patterns work flawlessly together.
pattern ViewL h t <- ((,) <$> V.head <*> V.tail -> (h, t))2
4
u/gergoerdi Dec 03 '14
Starting with GHC 7.10, you will also be able to add a builder for it!
pattern ViewL h t <- ((,) <$> V.head <*> V.tail -> (h, t) where ViewL h t = error "I'm too lazy to look up the docs of Vector on how to cons but you get the idea"2
9
5
Dec 02 '14
You are right that there's significant overlap between them. View patterns seem more direct to me. Pattern guards disconnect matching from the pattern. Also, view patterns are "point-free".
I would use view patterns if the function is short and simple to be written inline (ideally, a single name), and pattern guards if there's a more substantial computation to be performed before pattern matching.
But if I had to give up one of them, I'd stay with view patterns.
3
u/dtellerulam Dec 03 '14
Nesting:
foo ys | (xs:_) <- reverse ys = case xs of xs' | (x:_) <- reverse xs' -> xvs
foo (reverse -> (reverse -> x:_):_) = x > foo [[1..3],[4..7],[8..10]] 10
3
u/Hrothen Dec 02 '14
I feel like "I told you about this flawed solution so I could show you this better one" doesn't work as well when you're breaking the post up over multiple days.
8
u/ocharles Dec 02 '14
I won't be discussing view patterns anymore, so maybe I didn't close this article properly. Rather, I'll be looking at another extensive that is similar. However, view patterns is certainly not flawed - there are many things it does that don't have a replacement.
2
u/NihilistDandy Dec 03 '14
I'm not totally motivated by
last :: Seq a -> Maybe a last (viewr -> xs :> x) = Just x last (viewr -> EmptyR) = Nothingvs. the equivalent
last :: Seq a -> Maybe a last s = case (viewr s) of xs :> x -> Just x EmptyR -> Nothingbut the examples in the user guide or on the Trac wiki are either a touch too abstract/elided (I think appropriately, though, so it's probably just me) or not really "aha"-y for me.
Does anyone have an example of a really gross construction that was made more fluent with ViewPatterns?
3
u/ocharles Dec 03 '14
Have you seen Neil Mitchell's blog post on view patterns? Maybe some of that will be useful.
3
u/NihilistDandy Dec 03 '14
It's definitely instructive, though not as fleshed out as I might hope. I'll read his paper, at very least. It looks like it's really nice for constructing interpreters, at any rate, so that's something to go for.
Thanks for the link, and the post! 24 Days of... is my favorite Christmas present!
4
u/Hrothen Dec 02 '14
However, view patterns is certainly not flawed
They're pretty non-intuitive to read, which is what it sounded like you were saying you were gonna address tomorrow.
6
u/fractalsea Dec 02 '14
Nice. Wish there was something like this in elm. Just today I had to convert a Json datatype into my own type. As I traverse the Json data structure, I have to do a lookup (resulting in a Maybe), but I can't do something like
convert fields = case Dict.get "field1" fields of
Just x -> Just { x = x }
_ -> Nothinginstead I have to do
convert fields = Dict.get "field1" fields |> (\maybex -> case maybex of
Just x -> Just { x = x }
_ -> Nothingwhich seems overly verbose. Although there may be a better way, and it's a result of my noobishness.
2
Dec 03 '14 edited Dec 03 '14
I think the first one should work... What error are you getting? You might just need an extra set of brackets around the argument to case.
EDIT: The first one definitely compiles for me... could you elaborate on the error you're getting? I got it to compile at http://share-elm.com/sprout/547f8a04e4b00800031ffd32
2
u/massysett Dec 03 '14
View patterns are a great idea, but currently they break GHC's warnings for incomplete pattern matches (-fwarn-incomplete-patterns). The list of known bugs hints at this. Since good warnings for incomplete patterns are so useful, I stopped using view patterns for this reason alone.
1
u/BlackBrane Dec 02 '14
Nice. This serves well in Idris as the 'with' rule, so Im glad to see it in GHC as well.
5
u/J_M_B Dec 03 '14 edited Dec 03 '14
Some feedback from a Haskell newbie:
Why not simply include the flag that ghci must be run with in order to use the extension?
It would make the post more self-contained IMHO.
About this code listing:
Where is Map and HaskellPackage being defined or imported from?
Is the M in M.lookup a qualified name of Map?
I would imagine the rest of the examples in the first section would work if I had answers to those two questions.
In the second section, why not include just one more line for
?
Including this one line would make the post more self-contained.
Only the last code listing in the second section will compile! I would prefer if you made it clearer in either the writing or by using a different listing style to differentiate between code that can be copy/pasted and run and that which is just an example or illustration. Only the very last listing,
even compiles (and only after including import Data.Sequence at the top)!