Copyright | (c) 2020 Sam May |
---|---|
License | MPL-2.0 |
Maintainer | ag.eitilt@gmail.com |
Stability | provisional |
Portability | portable |
Safe Haskell | Safe-Inferred |
Language | Haskell98 |
Web.Willow.Common.Parser.Switch
Description
Alternative
instances can provide a form of pattern matching if given a
fail-on-false combinator (e.g. when
), however the exact
behaviour isn't guaranteed; an underlying Maybe
does provide a greedy match,
but []
will match later overlapping tests even if they are intended to be
masked; compare the masking to standard, cascading pattern guards. This module
provides a means of formalizing that behaviour into a predictable form, no
matter which Alternative
winds up being used.
Synopsis
- data SwitchCase test m out
- switch :: Alternative m => [SwitchCase test m out] -> test -> m out
Documentation
data SwitchCase test m out #
The building blocks for predictable pattern matches over Alternative
.
The constructors are distinguished along three axes (see also the examples
in the documentation for switch
):
- "masking" vs. "non-masking": only the first "masking" case fulfilled will be returned, while every "non-masking" one is returned
- "matching" vs. "catchall": whether the output is gated by a predicate test or not
- "piped" vs. "static": whether the output is passed the original test token
Constructors
If (test -> Bool) (test -> m out) | Masking, matching, and piped |
If_ (test -> Bool) (m out) | Masking, matching, and static |
Else (test -> m out) | Masking, catchall, and piped |
Else_ (m out) | Masking, catchall, and static |
When (test -> Bool) (test -> m out) | Non-masking, matching, and piped |
When_ (test -> Bool) (m out) | Non-masking, matching, and static |
Always (test -> m out) | Non-masking, catchall, and piped |
Always_ (m out) | Non-masking, catchall, and static |
switch :: Alternative m => [SwitchCase test m out] -> test -> m out #
Run a block of SwitchCase
s, collapsing any masking cases so that only
the first matched test remains. This is strictly more powerful than pattern
matching, as it allows interspersing non-masking tests alongside masking
ones; for compatibility with refactoring to single-return Alternative
instances, however (i.e. Maybe
), it's best to order everything as if
every case could mask the ones after it. Note that the masking only affects
the output; the tests themselves may still be run, so expensive computations
are best put elsewhere.
Only the first overlapping (maskable) case is selected:
>>>
uppercase = If_ isUpper $ return "uppercase"
>>>
one = When_ (== '1') $ return "single '1'"
>>>
alpha = If_ isAlpha $ return "ASCII letter" -- Matches
>>>
catchall = Else_ $ return "none of the above" -- Matches
>>>
switch [uppercase, one, alpha, catchall] 'a' :: [String]
["ASCII letter"]
Non-masking cases don't interact with the masking calculations:
>>>
uppercase = If_ isUpper $ return "uppercase"
>>>
one = When_ (== '1') $ return "single '1'" -- Matches
>>>
alpha = If_ isAlpha $ return "ASCII letter"
>>>
catchall = Else_ $ return "none of the above" -- Matches
>>>
switch [uppercase, one, alpha, catchall] '1' :: [String]
["single '1'", "none of the above"]
Maybe
always takes the earliest successful test:
>>>
uppercase = If_ isUpper $ return "uppercase"
>>>
one = When_ (== '1') $ return "single '1'" -- Matches
>>>
alpha = If_ isAlpha $ return "ASCII letter"
>>>
catchall = Else_ $ return "none of the above" -- Matches
>>>
switch [uppercase, one, alpha, catchall] '1' :: Maybe String
Just "single '1'"
Always
and Always_
function as a standard Alternative
computation:
>>>
switch [Always a, Always b, Always_ c] tok == a tok <|> b tok <|> c
True