... | ... | @@ -9,7 +9,7 @@ The ticket associated with this design is #9671. |
|
|
The simplest use case is checking whether a set contains a value:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
-- Normal Haskell
|
|
|
answer :: Set Int -> String
|
|
|
answer set = case member 42 set of
|
... | ... | @@ -26,7 +26,7 @@ answer (member 42 -> False) = "Never mind." |
|
|
With this extension we could define patterns that check for containment:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern IsMember val <- (member val -> True)
|
|
|
pattern NotMember val <- (member val -> False)
|
|
|
|
... | ... | @@ -46,7 +46,7 @@ This allows us to avoid pattern matching on the Boolean result of `member`. In t |
|
|
Let's consider a similar example with a `Map` where we want to look up a value based on some key:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
-- Normal Haskell
|
|
|
addressAlice :: Map Name Address -> Either String Address
|
|
|
addressAlice people = case lookup "Alice" people of
|
... | ... | @@ -58,7 +58,7 @@ addressAlice people = case lookup "Alice" people of |
|
|
With the extension we define a pattern that only succeeds if `lookup` returns a value wrapped in `Just` which feeds that value back into the pattern:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern Lookup key val <- (lookup key -> Just val)
|
|
|
pattern LookupFail key <- (lookup key -> Nothing)
|
|
|
|
... | ... | @@ -78,7 +78,7 @@ where the key `"Alice"` is used in the view pattern expression and the resulting |
|
|
Now we don't have to unnecessarily give a name to an argument (like `people`) like with view patterns but unlike view patterns we don't need to mention to `Bool` and `Maybe` success result values of member and lookup. These kinds of patterns also nest arbitrarily without too much noise:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
foo :: Map Person (Map Receptacle (Set Items)) -> Status
|
|
|
foo = \case
|
|
|
Lookup "Bob" (Lookup Pocket (IsMember Keys)) -> HasKeys
|
... | ... | @@ -91,7 +91,7 @@ foo = \case |
|
|
Another simple example is the `Between` pattern that matches a particular range (a feature built into [Rust](http://doc.rust-lang.org/master/tutorial.html#pattern-matching)):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
import Data.Ix
|
|
|
|
|
|
pattern Between from to <- (inRange (from, to) -> True)
|
... | ... | @@ -107,7 +107,7 @@ isTeen _ = False |
|
|
that gets transformed into:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
isTeen :: Age -> Bool
|
|
|
isTeen (inRange (13, 19) -> True) = True
|
|
|
isTeen _ = False
|
... | ... | @@ -117,7 +117,7 @@ isTeen _ = False |
|
|
`Between` will work on any indexable type (`Ix a => a`):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
generalCategory' :: Char -> GeneralCategory
|
|
|
generalCategory' = \case
|
|
|
Between '\x00' '\x16' -> Control
|
... | ... | @@ -133,7 +133,7 @@ generalCategory' = \case |
|
|
The syntax from [PatternSynonyms](pattern-synonyms) can be reused for simplicity:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern Lookup key value <- (lookup key -> Just value)
|
|
|
```
|
|
|
|
... | ... | @@ -145,7 +145,7 @@ here `value` is a normal variable as in [PatternSynonyms](pattern-synonyms) but |
|
|
The function `fn`:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
fn :: Map String Int -> Int
|
|
|
fn (Lookup "five" n) = n
|
|
|
```
|
... | ... | @@ -211,7 +211,7 @@ where *pvarid<sub>j</sub>* may appear in `result` as in usual patterns. This the |
|
|
For the simple example of the pattern family `Take` this (where 3 is *expr<sub>1</sub>* and `xs` is *pval<sub>1</sub>*):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
foo (Lookup "five" n) = n + n
|
|
|
```
|
|
|
|
... | ... | @@ -219,7 +219,7 @@ foo (Lookup "five" n) = n + n |
|
|
would get translated to:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
foo (lookup "five" -> Just n) = n + n
|
|
|
```
|
|
|
|
... | ... | @@ -227,7 +227,7 @@ foo (lookup "five" -> Just n) = n + n |
|
|
which is the same as:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
foo ys = case lookup "five" n of
|
|
|
Just n -> n + n
|
|
|
```
|
... | ... | @@ -246,7 +246,7 @@ If *expr* in the view pattern is an expression of type *t* with free variables * |
|
|
Example from [ViewPatternsAlternative](view-patterns-alternative):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
module Set(Set, empty, insert, delete, has) where
|
|
|
|
|
|
newtype Set a = S [a]
|
... | ... | @@ -260,7 +260,7 @@ has x (S xs) | x `elem` xs = Just (S (xs \\ [x])) |
|
|
Using patterns indexed by an element of `Set a`:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern Has x set <- (has x -> Just set)
|
|
|
pattern HasNot x set <- (has x &&& id -> (Nothing, set))
|
|
|
```
|
... | ... | @@ -269,7 +269,7 @@ pattern HasNot x set <- (has x &&& id -> (Nothing, set)) |
|
|
One can write:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
delete :: Eq a => a -> Set a -> Set a
|
|
|
delete x (Has x set) = set
|
|
|
delete x set = set
|
... | ... | @@ -283,7 +283,7 @@ insert x set = set |
|
|
Compare that to the the [ViewPatternsAlternative](view-patterns-alternative) proposal:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
delete :: Eq a => a -> Set a -> Set a
|
|
|
delete x (r | Just s <- has r) = set
|
|
|
delete x set = set
|
... | ... | @@ -303,7 +303,7 @@ Where the user has to worry about matching on `Just`s. |
|
|
Another example stolen from [ViewPatternsAlternative](view-patterns-alternative) where the benefits are more apparent. Given a parsing function:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
-- (bits n bs) parses n bits from the front of bs, returning
|
|
|
-- the n-bit Word, and the remainder of bs
|
|
|
|
... | ... | @@ -314,7 +314,7 @@ bits :: Int -> ByteString -> Maybe (Word, ByteString) |
|
|
and using the following pattern family:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern Bits n val bs <- (bits n -> Just (val, bs))
|
|
|
```
|
|
|
|
... | ... | @@ -322,7 +322,7 @@ pattern Bits n val bs <- (bits n -> Just (val, bs)) |
|
|
one can write a pattern like this:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
parsePacket :: ByteString -> _
|
|
|
parsePacket (Bits 3 n (Bits n val bs)) = _
|
|
|
```
|
... | ... | @@ -331,7 +331,7 @@ parsePacket (Bits 3 n (Bits n val bs)) = _ |
|
|
Where we can nest pattern families, more examples of nesting follow in the more advanced examples below. Compare that to the more verbose [ViewPatternsAlternative](view-patterns-alternative) version:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
parsePacket :: ByteString -> _
|
|
|
parsePacket (p1 | Just (n, (p2 | Just (val, bs) <- bits n p2)) <- bits 3 p1) = _
|
|
|
```
|
... | ... | @@ -343,7 +343,7 @@ parsePacket (p1 | Just (n, (p2 | Just (val, bs) <- bits n p2)) <- bits 3 p1) = |
|
|
Another one from [ViewPatternsAlternative](view-patterns-alternative) using the following view and pattern family:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
np :: Num a => a -> a -> Maybe a
|
|
|
np k n | k <= n = Just (n-k)
|
|
|
| otherwise = Nothing
|
... | ... | @@ -355,7 +355,7 @@ pattern NP k n <- (np k -> Just n) |
|
|
Used as follows:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
fib :: Num a -> a -> a
|
|
|
fib 0 = 1
|
|
|
fib 1 = 1
|
... | ... | @@ -366,7 +366,7 @@ fib (NP 2 n) = fib (n + 1) + fib n |
|
|
Compare [ViewPatternsAlternative](view-patterns-alternative) version:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
fib :: Num a -> a -> a
|
|
|
fib 0 = 1
|
|
|
fib 1 = 1
|
... | ... | @@ -380,7 +380,7 @@ fib (n2 | let n = n2-2, n >= 0) = fib (n + 1) + fib n |
|
|
From [Bidirectional Typing Rules: A Tutorial](http://itu.dk/people/drc/tutorials/bidirectional.pdf):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
inferType ctx (If t1 t2 t3) = case (inferType ctx t1, inferType ctx t2, inferType ctx t3) of
|
|
|
(Just BoolT, Just ty2, Just ty3) ->
|
|
|
if ty2 = ty3
|
... | ... | @@ -393,7 +393,7 @@ inferType ctx (If t1 t2 t3) = case (inferType ctx t1, inferType ctx t2, inferTyp |
|
|
could be rewritten using pattern families as:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
-- Here ‘Inf’ is a family indexed by ‘ctx’
|
|
|
pattern Inf ctx ty <- (inferType ctx -> Just ty)
|
|
|
|
... | ... | @@ -406,7 +406,7 @@ inferType ctx If{} = Nothing |
|
|
allowing the user to pattern match *directly* on the inferable types without manually checking for `Just`s — note the use of the previous argument `ctx` to index later. This could currently be written somewhat awkwardly using view patterns:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
inferType ctx (If (inferType ctx -> Just BoolT) (inferType ctx -> Just ty1) (inferType ctx -> Just ty2))
|
|
|
| ty1 == ty2 = Just ty1
|
|
|
inferType ctx If{} = Nothing
|
... | ... | @@ -420,7 +420,7 @@ which is longer and clunkier, especially since the user is forced to deal with ` |
|
|
Again one could use operators (`:⇒ = Inf`) in which case it the examples follow notation in type theory more closely:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
inferType γ (If (γ :⇒ BoolT) (γ :⇒ τ₁) (γ :⇒ τ₂)) = ...
|
|
|
```
|
|
|
|
... | ... | @@ -431,7 +431,7 @@ inferType γ (If (γ :⇒ BoolT) (γ :⇒ τ₁) (γ :⇒ τ₂)) = ... |
|
|
Given a regular expression operator `(~=) :: String -> String -> Maybe [String]` we can define a pattern:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern Match x regexp <- ((~= regexp) -> Just x)
|
|
|
```
|
|
|
|
... | ... | @@ -439,7 +439,7 @@ pattern Match x regexp <- ((~= regexp) -> Just x) |
|
|
where `regexp` is indexes the `Match` pattern family:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
firstWord (Match words "[a-zA-Z]+") = words
|
|
|
firstWord _ = error "No words found"
|
|
|
```
|
... | ... | @@ -448,7 +448,7 @@ firstWord _ = error "No words found" |
|
|
or
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
vowels (Match vwls "[aeiou]") = length vwls
|
|
|
```
|
|
|
|
... | ... | @@ -456,7 +456,7 @@ vowels (Match vwls "[aeiou]") = length vwls |
|
|
As an operator:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern x :~= regexp <- ((~= regexp) -> Just x)
|
|
|
```
|
|
|
|
... | ... | @@ -469,7 +469,7 @@ pattern x :~= regexp <- ((~= regexp) -> Just x) |
|
|
Indexing patterns with prisms from [Control.Lens.Prism](http://hackage.haskell.org/package/lens-4.2/docs/Control-Lens-Prism.html):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
import Control.Lens.Prism
|
|
|
|
|
|
pattern Match prism a <- (preview prism -> Just a)
|
... | ... | @@ -479,7 +479,7 @@ pattern Match prism a <- (preview prism -> Just a) |
|
|
one can write
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
bar :: Either c (Either a b) -> a
|
|
|
bar (Match (_Right._Left) a) = a
|
|
|
bar _ = error "..."
|
... | ... | @@ -492,7 +492,7 @@ bar _ = error "..." |
|
|
Pattern families can be used to match nested data like JSON, ASTs or XML, here is an example of using it to match on [Data.Aeson.Lens](http://hackage.haskell.org/package/lens-4.2/docs/Data-Aeson-Lens.html):
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
jsonBlob = "[{\"someObject\": {\"version\": [1,0,3]}}]"
|
|
|
|
|
|
-- val = Number 0.0
|
... | ... | @@ -503,7 +503,7 @@ val = jsonBlob ^?! nth 0 . key "someObject" . key "version" . nth 1 |
|
|
Pattern families allow us to say we want to fetch the same value as `val` using patterns:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
foo (Match (nth 0) (Match (key "someObject") (Match (key "version") (Match (nth 1) a)))) = a
|
|
|
```
|
|
|
|
... | ... | @@ -511,7 +511,7 @@ foo (Match (nth 0) (Match (key "someObject") (Match (key "version") (Match (nth |
|
|
Which is terribly verbose, but can be improved by introducing:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
pattern Get i a <- (preview (nth i) -> Just a)
|
|
|
pattern Key str a <- (preview (key str) -> Just a)
|
|
|
|
... | ... | @@ -522,7 +522,7 @@ baz (Get 0 (Key "someObject" (Key "version" (Get 1 a)))) = a |
|
|
or by writing it infix:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
baz (0 `Get` "someObject" `Key` "version" `Key` 1 `Get` a) = a
|
|
|
```
|
|
|
|
... | ... | @@ -530,6 +530,6 @@ baz (0 `Get` "someObject" `Key` "version" `Key` 1 `Get` a) = a |
|
|
or by defining operators `:→ = Get i a` and `:⇒ = Key`:
|
|
|
|
|
|
|
|
|
```
|
|
|
```haskell
|
|
|
baz (a :→ "someObject" :⇒ "version" :⇒ 1 :→ a) = a
|
|
|
``` |
|
|
\ No newline at end of file |
|
|
``` |