any-pat: Quasiquoters that act on a sequence of patterns and compiles these view into patterns and expressions.

[ bsd3, library, utils ] [ Propose Tags ]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.2.0.0, 0.3.0.0, 0.4.0.0
Change log CHANGELOG.md
Dependencies base (>=4.7 && <5), haskell-src-exts, haskell-src-meta, template-haskell (>=2.2.0.0) [details]
License BSD-3-Clause
Copyright 2023 HaPyTeΧ
Author Willem Van Onsem
Maintainer hapytexeu+gh@gmail.com
Category utils
Home page https://github.com/hapytex/any-pat#readme
Source repo head: git clone https://github.com/hapytex/any-pat
Uploaded by wvanonsem90 at 2023-07-30T16:57:31Z
Distributions NixOS:0.4.0.0
Downloads 111 total (11 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2023-07-30 [all 1 reports]

Readme for any-pat-0.2.0.0

[back to package description]

any-pat

Build Status of the package by GitHub actions Hackage version badge

Combine multiple patterns in a single pattern and range membership checks.

Usage

This package ships with three QuasiQuoters: anypat, maypat and rangepat.

anypat and maypat

anypat and maypat have the same purpose. Defining multiple possible patterns in a single clause. Indeed, consider the following example:

mightBe :: (Int, a, a) -> Maybe a
mightBe (0, a, _) = Just a
mightBe (1, _, a) = Just a
mightBe _ = Nothing

the first two clauses have some repetitive elements. We can combine the two through the anypat or maypat quasiquoter:

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ViewPatterns #-}

mightBe :: (Int, a, a) -> Maybe a
mightBe [anypat|(0, a, _), (1, _, a)|] = Just a
mightBe _ = Nothing

or with maypat:

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ViewPatterns #-}

mightBe :: (Int, a, a) -> Maybe a
mightBe [maypat|(0, a _), (1, _, a), _|] = a

and that's it. No, there is no need to wrap a in a Just in the last code example.

We can also use the maypat and anypat to generate expressions, with:

{-# LANGUAGE QuasiQuotes #-}

mightBe :: (Int, a, a) -> Maybe a
mightBe = [maypat|(0, a _), (1, _, a), _|]

If it is used as pattern, the ViewPatterns language extension should be enabled. Furthermore the QuasiQuotes extension should of course be enabled for any use of the quasi quoters.

The difference between the two QuasiQuoters (anypat and maypat) are the handling of variable names. Variable names defined in the pattern(s) are used in the body of the function, so it makes sense that if the clause "fires", these have a value. This thus means that a reasonable condition is that all patterns have the same set of variable names and that the variable names have the same type. The anypat requires that all patterns have the same variables, so [anypat|(0, a), (1, _)|] will raise an error: if the second pattern (1, _) would "fire" it would not provide a value for the a variable, and then we have a problem. A possible solution would be to pass a value like undefined, or an infinite loop (i.e. y = let x = x in x for example) as value, but this looks like something that would only cause a lot of trouble.

Therefore maypat comes with a different solution: it performs analysis on the variables used in the different patterns. Variables that occur in all patterns are just passed with the real value, variables that occur only in a (strict) subset of the listed patterns, are passed as a Maybe a value with Just x in case the first pattern that "fires" (left-to-right) for the value has that variable, it will be wrapped in a Just, and otherwise, it will pass Nothing as that variable.

Some functions in the base package, for example, have a simple equivalent with anypat or maypat, for example listToMaybe :: [a] -> Maybe a can be implemented as:

{-# LANGUAGE QuasiQuotes #-}

listToMaybe :: [a] -> Maybe a
listToMaybe = [maypat|(a:_), _|]

rangepat

rangepat defines patterns for range memberships. For example:

isInRange :: Int -> Bool
isInRange [rangepat|0, 5 .. 50|] = True
isInRange _ = False

This will check in constant time if the number is in the given range (here [0, 5 .. 50]). The pattern has however some caveats, especially with floating point numbers, and likely any other type where fromEnum en toEnum are not bijective.

Package structure

The package has only one module: Data.Pattern.Any that exports the two QuasiQuoters named anypat and maypat together with some utility functions to obtain the variables names from a pattern.

Behind the curtains

The package transforms a sequence of patterns to a view pattern, or an expression, depending on where the quasi quoter is used. If we create a pattern [anypat|p1, p2, …, pn], it will create a view pattern that looks like:

\case
  p1 -> Just n⃗
  p2 -> Just n⃗
  ⋮
  pn -> Just n⃗
  _ -> Nothing

with n⃗ the (sorted) tuple of names found in the patterns. It then makes a view pattern e -> n⃗ that thus maps the found values for the variables to the names that can then be used in the body of the function.

There are some (small) optimizations that for example are used if no variable names are used in the patterns, or only one. If a wildcard pattern is used, it can also omit the Maybe data type.

For rangepat, it first converts the range to a RangeObj, and then checks membership in constant time (given we assume that operations on Int run in constant time).

any-pat is inferred safe Haskell

It can not be marked safe, since the modules it depends on are not marked safe, but its safeness can be inferred by the compiler.

Contribute

You can contribute by making a pull request on the GitHub repository.

You can contact the package maintainer by sending a mail to hapytexeu+gh@gmail.com.