All Projects → andreaferretti → Patty

andreaferretti / Patty

Licence: apache-2.0
A pattern matching library for Nim

Programming Languages

nim
578 projects

Projects that are alternatives of or similar to Patty

Pattern Matching Ts
⚡ Pattern Matching in Typescript
Stars: ✭ 107 (-50%)
Mutual labels:  pattern-matching
Eval
Eval is a lightweight interpreter framework written in Swift, evaluating expressions at runtime
Stars: ✭ 157 (-26.64%)
Mutual labels:  pattern-matching
Symbolicutils.jl
Expression rewriting and simplification
Stars: ✭ 189 (-11.68%)
Mutual labels:  pattern-matching
Bem Xjst
bem-xjst (eXtensible JavaScript Templates): declarative template engine for the browser and server
Stars: ✭ 115 (-46.26%)
Mutual labels:  pattern-matching
Rosie Pattern Language
Rosie Pattern Language (RPL) and the Rosie Pattern Engine have MOVED!
Stars: ✭ 146 (-31.78%)
Mutual labels:  pattern-matching
Egison Ruby
A Ruby gem for non-linear pattern-matching with backtracking
Stars: ✭ 159 (-25.7%)
Mutual labels:  pattern-matching
Nanomatch
Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but without support for extended globs (extglobs), posix brackets or braces, and with complete Bash 4.3 wildcard support: ("*", "**", and "?").
Stars: ✭ 79 (-63.08%)
Mutual labels:  pattern-matching
Zeallot
Variable assignment with zeal! (or multiple, unpacking, and destructuring assignment in R)
Stars: ✭ 204 (-4.67%)
Mutual labels:  pattern-matching
Motif
Scala-like pattern matching for Java 8
Stars: ✭ 149 (-30.37%)
Mutual labels:  pattern-matching
Akar
First-class patterns for Clojure. Made with love, functions, and just the right amount of syntax.
Stars: ✭ 176 (-17.76%)
Mutual labels:  pattern-matching
Grape
🍇 Syntax-aware grep-like for Clojure
Stars: ✭ 132 (-38.32%)
Mutual labels:  pattern-matching
Stumpy
STUMPY is a powerful and scalable Python library for modern time series analysis
Stars: ✭ 2,019 (+843.46%)
Mutual labels:  pattern-matching
Fpgo
Monad, Functional Programming features for Golang
Stars: ✭ 165 (-22.9%)
Mutual labels:  pattern-matching
Matchpy
A library for pattern matching on symbolic expressions in Python.
Stars: ✭ 109 (-49.07%)
Mutual labels:  pattern-matching
Gradoop
Distributed Graph Analytics with Apache Flink
Stars: ✭ 197 (-7.94%)
Mutual labels:  pattern-matching
Qutuf
Qutuf (قُطُوْف): An Arabic Morphological analyzer and Part-Of-Speech tagger as an Expert System.
Stars: ✭ 84 (-60.75%)
Mutual labels:  pattern-matching
Expat
Reusable, composable patterns across Elixir libraries
Stars: ✭ 157 (-26.64%)
Mutual labels:  pattern-matching
Trivia
Pattern Matcher Compatible with Optima
Stars: ✭ 210 (-1.87%)
Mutual labels:  pattern-matching
Hexraystoolbox
Hexrays Toolbox - Find code patterns within the Hexrays AST
Stars: ✭ 202 (-5.61%)
Mutual labels:  pattern-matching
Symja android library
☕️ Symja - computer algebra language & symbolic math library. A collection of popular algorithms implemented in pure Java.
Stars: ✭ 170 (-20.56%)
Mutual labels:  pattern-matching

Patty - A pattern matching library

Build Status nimble

Patty is a library to perform pattern matching in Nim. Make sure to also have a look at Gara, which is a more complete solution. Unlike Patty, Gara misses a macro to generate variant objects, but it is otherwise much more comprehensive. You can follow here about adding a macro for object variants in Gara.

The patterns have to be variant objects, which in Nim are encoded with a field (usually called kind) which varies in an enum, and a different object layout based on the value of this tag. An example would be

type
  ShapeKind = enum
    Circle, Rectangle
  Shape = object
    case kind: ShapeKind
    of Circle:
      r: float
    of Rectangle:
      w, h: float

If you have such an algebraic data type, you can do the following with Patty:

import patty

proc makeRect(w, h: float): Shape = Shape(kind: Rectangle, w: w, h: h)

match makeRect(3, 4):
  Circle(r: radius):
    echo "it is a circle of radius ", radius
  Rectangle(w: width, h: height):
    echo "it is a rectangle of height ", height

This will be translated by the match macro into the following form

let :tmp = makeRect(3, 4)
case :tmp.kind
of Circle:
  let radius = :tmp.r
  echo "it is a circle of radius ", radius
of Rectangle:
  let
    width = :tmp.w
    height = :tmp.h
  echo "it is a rectangle of height ", height

Matching by position is also valid, like this:

match makeRect(3, 4):
  Circle(radius):
    echo "it is a circle of radius ", radius
  Rectangle(width, height):
    echo "it is a rectangle of height ", height

One can also use _ for a variable, in which case it will not be bound. That is, the following

import patty

proc makeRect(w, h: float): Shape = Shape(kind: Rectangle, w: w, h: h)

match makeRect(3, 4):
  Circle(r: radius):
    echo "it is a circle of radius ", radius
  Rectangle(w: _, h: height):
    echo "it is a rectangle of height ", height

becomes

let :tmp = makeRect(3, 4)
case :tmp.kind
of Circle:
  let radius = :tmp.r
  echo "it is a circle of radius ", radius
of Rectangle:
  let height = :tmp.h
  echo "it is a rectangle of height ", height

The wildcard _ can also be used as a stand alone pattern, in which case it will match anything. It is translated into an else clause, that is, the following

import patty

proc makeRect(w, h: float): Shape = Shape(kind: Rectangle, w: w, h: h)

match makeRect(3, 4):
  Circle(r: radius):
    echo "it is a circle of radius ", radius
  _:
    echo "it is not a circle"

becomes

let :tmp = makeRect(3, 4)
case :tmp.kind
of Circle:
  let radius = :tmp.r
  echo "it is a circle of radius ", radius
else:
  echo "it is not a circle"

Notice that in the examples, the field you dispatch on is called kind, but any other name would do. Also, checks are exhaustive: if you miss a case, the compiler will complain.

One can instead pattern-match on non-variant objects, which essentially amounts to deconstructing fields:

type Person = object
  name: string
  age: int
let p = Person(name: "John Doe", age: 37)
match p:
  Person(name: n, age: a):
    echo n, "is ", a, " years old"

Again, this is the same as

match p:
  Person(n, a):
    echo n, "is ", a, " years old"

Pattern matching also works as an expression:

let coord = match c:
  Circle(x: x, y: y, r: r):
    x
  Rectangle(w: w, h: h):
    h

Constructing variant objects

Patty also provides another macro to create algebraic data types. It looks like

variant Shape:
  Circle(r: float)
  Rectangle(w: float, h: float)
  UnitCircle

and expands to

type
  ShapeKind {.pure.} = enum
    Circle, Rectangle, UnitCircle
  Shape = object
    case kind: ShapeKind
    of ShapeKind.Circle:
      r: float
    of ShapeKind.Rectangle:
      w: float
      h: float
    of ShapeKind.UnitCircle:
      nil

proc `==`(a: Shape; b: Shape): bool =
  if a.kind == b.kind:
    case a.kind
    of ShapeKind.Circle:
      return a.r == b.r
    of ShapeKind.Rectangle:
      return a.w == b.w and a.h == b.h
    of ShapeKind.UnitCircle:
      return true
  else:
    return false

proc Circle(r: float): Shape =
  Shape(kind: ShapeKind.Circle, r: r)

proc Rectangle(w: float; h: float): Shape =
  Shape(kind: ShapeKind.Rectangle, w: w, h: h)

proc UnitCircle(): Shape =
  Shape(kind: ShapeKind.UnitCircle)

Notice that the macro also generates three convenient constructors (Circle ,Rectangle and UnitCircle), and in fact the enum is pure to avoid a name conflict. Also, a proper definition of equality based on the actual contents of the record is generated.

By default the generated ADT is private to the module. If you want to generate a public ADT use the variantp macro, which has the same syntax as variant but makes the types, fields, equality definition and generated constructors public.

A limitation of the variant macro is that field names must be unique across branches (that is, different variants cannot have two fields with the same name). This is actually a limitation of Nim.

In the future, Patty may also add copy constructors. Also, some work needs to be done to make it easier to use the generated contructors with ref types, in particular for the important case of recursive algebraic data types.

Example

The following example uses the variant macro to define a linked list type, and then uses pattern matching to define the sum of a list of integers:

import patty

proc `~`[A](a: A): ref A =
  new(result)
  result[] = a

variant List[A]:
  Nil
  Cons(x: A, xs: ref List[A])

proc listHelper[A](xs: seq[A]): List[A] =
  if xs.len == 0: Nil[A]()
  else: Cons(xs[0], ~listHelper(xs[1 .. xs.high]))

proc list[A](xs: varargs[A]): List[A] = listHelper(@xs)

proc sum(xs: List[int]): int = (block:
  match xs:
    Nil: 0
    Cons(y, ys): y + sum(ys[])
)

echo sum(list(1, 2, 3, 4, 5))

Versions

Patty 0.3.0 works for latest Nim (devel). For older versions of Nim (up to 0.16.0), use Patty 0.2.1.

Things that do not work (yet)

One would expect many forms of pattern matching but, at least for now, the support in Patty is very limited. Things that would be nice to support but do not work yet include:

  • matching a constant
match c:
  "hello":
    echo "the string was hello"
  • matching an existing variable
let x = 5
match c:
  x:
    echo "c == 5"
  • nested pattern matching
match c:
  Circle(Point(x: x, y: y), r: r):
    echo "the abscissa of the center is ", x
  • matching without binding
match c:
  Circle:
    echo "it is a circle!"
  • binding subpatterns
match getMeACircle():
  c@Circle(x, y, r):
    echo "there you have ", c
  • unification
match r:
  Rectangle(w: x, h: x):
    echo "it is a square"
  • guards
match c:
  Circle(x: x, y: y, r: r) if r < 0:
    echo "the circle has negative length"
  • variable-length pattern matching, such as with arrays
match c:
  [a, b, c]:
    echo "the length is 3 and the first elements is ", a
  • custom pattern matchers, such as in regexes
let Email = r"(\w+)@(\w+).(\w+)"
match c:
  Email(name, domain, tld):
    echo "hello ", name
  • combining patterns with or
match c:
  Circle or Rectangle:
    echo "it is a shape"
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].