willow-0.1.0.0: An implementation of the web Document Object Model, and its rendering.
Copyright(c) 2020 Sam May
LicenseMPL-2.0
Maintainerag.eitilt@gmail.com
Stabilityprovisional
Portabilityportable
Safe HaskellSafe-Inferred
LanguageHaskell98

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

Documentation

data SwitchCase test m out Source #

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 Source #

Run a block of SwitchCases, 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