{"id":13735596,"url":"https://github.com/andreaferretti/patty","last_synced_at":"2025-10-06T20:12:30.683Z","repository":{"id":34405422,"uuid":"38334132","full_name":"andreaferretti/patty","owner":"andreaferretti","description":"A pattern matching library for Nim","archived":false,"fork":false,"pushed_at":"2023-04-04T12:56:04.000Z","size":96,"stargazers_count":279,"open_issues_count":5,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-31T18:18:49.994Z","etag":null,"topics":["nim","pattern-matching","variant-objects"],"latest_commit_sha":null,"homepage":"http://andreaferretti.github.io/patty","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andreaferretti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-06-30T21:30:51.000Z","updated_at":"2025-01-31T12:46:08.000Z","dependencies_parsed_at":"2024-01-06T11:44:08.473Z","dependency_job_id":"12bfaed6-3178-41bc-a326-60f8a6560359","html_url":"https://github.com/andreaferretti/patty","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fpatty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fpatty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fpatty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fpatty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andreaferretti","download_url":"https://codeload.github.com/andreaferretti/patty/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247767236,"owners_count":20992548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nim","pattern-matching","variant-objects"],"created_at":"2024-08-03T03:01:08.655Z","updated_at":"2025-10-06T20:12:25.637Z","avatar_url":"https://github.com/andreaferretti.png","language":"Nim","funding_links":[],"categories":["Language Features"],"sub_categories":["Pattern Matching"],"readme":"Patty - A pattern matching library\n==================================\n\n[![Build Status](https://travis-ci.org/andreaferretti/patty.svg?branch=master)](https://travis-ci.org/andreaferretti/patty)\n[![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble_js.png)](https://github.com/yglukhov/nimble-tag)\n\nPatty is a library to perform pattern matching in Nim. Make sure to also have a\nlook at [Gara](https://github.com/alehander42/gara), which is a more complete\nsolution. Unlike Patty, Gara misses a macro to generate variant objects, but it\nis otherwise much more comprehensive. You can follow [here](https://github.com/alehander42/gara/issues/5)\nabout adding a macro for object variants in Gara.\n\nThe patterns have to be\n[variant objects](http://nim-lang.org/docs/manual.html#types-object-variants),\nwhich in Nim are encoded with a field (usually called `kind`) which varies in\nan enum, and a different object layout based on the value of this tag.\nAn example would be\n\n```nim\ntype\n  ShapeKind = enum\n    Circle, Rectangle\n  Shape = object\n    case kind: ShapeKind\n    of Circle:\n      r: float\n    of Rectangle:\n      w, h: float\n```\n\nIf you have such an algebraic data type, you can do the following with Patty:\n\n```nim\nimport patty\n\nproc makeRect(w, h: float): Shape = Shape(kind: Rectangle, w: w, h: h)\n\nmatch makeRect(3, 4):\n  Circle(r: radius):\n    echo \"it is a circle of radius \", radius\n  Rectangle(w: width, h: height):\n    echo \"it is a rectangle of height \", height\n```\n\nThis will be translated by the `match` macro into the following form\n\n```nim\nlet :tmp = makeRect(3, 4)\ncase :tmp.kind\nof Circle:\n  let radius = :tmp.r\n  echo \"it is a circle of radius \", radius\nof Rectangle:\n  let\n    width = :tmp.w\n    height = :tmp.h\n  echo \"it is a rectangle of height \", height\n```\n\nMatching by position is also valid, like this:\n\n```nim\nmatch makeRect(3, 4):\n  Circle(radius):\n    echo \"it is a circle of radius \", radius\n  Rectangle(width, height):\n    echo \"it is a rectangle of height \", height\n```\n\nOne can also use `_` for a variable, in which case it will not be bound.\nThat is, the following\n\n```nim\nimport patty\n\nproc makeRect(w, h: float): Shape = Shape(kind: Rectangle, w: w, h: h)\n\nmatch makeRect(3, 4):\n  Circle(r: radius):\n    echo \"it is a circle of radius \", radius\n  Rectangle(w: _, h: height):\n    echo \"it is a rectangle of height \", height\n```\n\nbecomes\n\n```nim\nlet :tmp = makeRect(3, 4)\ncase :tmp.kind\nof Circle:\n  let radius = :tmp.r\n  echo \"it is a circle of radius \", radius\nof Rectangle:\n  let height = :tmp.h\n  echo \"it is a rectangle of height \", height\n```\n\nThe wildcard `_` can also be used as a stand alone pattern, in which case it\nwill match anything.\nIt is translated into an `else` clause, that is, the following\n\n```nim\nimport patty\n\nproc makeRect(w, h: float): Shape = Shape(kind: Rectangle, w: w, h: h)\n\nmatch makeRect(3, 4):\n  Circle(r: radius):\n    echo \"it is a circle of radius \", radius\n  _:\n    echo \"it is not a circle\"\n```\n\nbecomes\n\n```nim\nlet :tmp = makeRect(3, 4)\ncase :tmp.kind\nof Circle:\n  let radius = :tmp.r\n  echo \"it is a circle of radius \", radius\nelse:\n  echo \"it is not a circle\"\n```\n\nNotice that in the examples, the field you dispatch on is called `kind`, but\nany other name would do. Also, checks are exhaustive: if you miss a case, the\ncompiler will complain.\n\nOne can instead pattern-match on non-variant objects, which essentially amounts\nto deconstructing fields:\n\n```nim\ntype Person = object\n  name: string\n  age: int\nlet p = Person(name: \"John Doe\", age: 37)\nmatch p:\n  Person(name: n, age: a):\n    echo n, \"is \", a, \" years old\"\n```\n\nAgain, this is the same as\n\n```nim\nmatch p:\n  Person(n, a):\n    echo n, \"is \", a, \" years old\"\n```\n\nPattern matching also works as an expression:\n\n```nim\nlet coord = match c:\n  Circle(x: x, y: y, r: r):\n    x\n  Rectangle(w: w, h: h):\n    h\n```\n\nConstructing variant objects\n----------------------------\n\nPatty also provides another macro to create algebraic data types. It looks like\n\n```nim\nvariant Shape:\n  Circle(r: float)\n  Rectangle(w: float, h: float)\n  UnitCircle\n```\n\nand expands to\n\n```nim\ntype\n  ShapeKind {.pure.} = enum\n    Circle, Rectangle, UnitCircle\n  Shape = object\n    case kind: ShapeKind\n    of ShapeKind.Circle:\n      r: float\n    of ShapeKind.Rectangle:\n      w: float\n      h: float\n    of ShapeKind.UnitCircle:\n      nil\n\nproc `==`(a: Shape; b: Shape): bool =\n  if a.kind == b.kind:\n    case a.kind\n    of ShapeKind.Circle:\n      return a.r == b.r\n    of ShapeKind.Rectangle:\n      return a.w == b.w and a.h == b.h\n    of ShapeKind.UnitCircle:\n      return true\n  else:\n    return false\n\nproc Circle(r: float): Shape =\n  Shape(kind: ShapeKind.Circle, r: r)\n\nproc Rectangle(w: float; h: float): Shape =\n  Shape(kind: ShapeKind.Rectangle, w: w, h: h)\n\nproc UnitCircle(): Shape =\n  Shape(kind: ShapeKind.UnitCircle)\n```\n\nNotice that the macro also generates three convenient constructors\n(`Circle` ,`Rectangle` and `UnitCircle`), and in fact the enum is pure to avoid\na name conflict. Also, a proper definition of equality based on the actual\ncontents of the record is generated.\n\n**By default the generated ADT is private to the module**. If you want to\ngenerate a public ADT use the `variantp` macro, which has the same syntax\nas `variant` but makes the types, fields, equality definition and generated\nconstructors public.\n\nA limitation of the `variant` macro is that field names must be unique across\nbranches (that is, different variants cannot have two fields with the same name).\nThis is actually a limitation of Nim.\n\nIn the future, Patty may also add copy constructors. Also, some work needs to\nbe done to make it easier to use the generated contructors with `ref` types,\nin particular for the important case of recursive algebraic data types.\n\nExample\n-------\n\nThe following example uses the `variant` macro to define a linked list type,\nand then uses pattern matching to define the sum of a list of integers:\n\n```nim\nimport patty\n\nproc `~`[A](a: A): ref A =\n  new(result)\n  result[] = a\n\nvariant List[A]:\n  Nil\n  Cons(x: A, xs: ref List[A])\n\nproc listHelper[A](xs: seq[A]): List[A] =\n  if xs.len == 0: Nil[A]()\n  else: Cons(xs[0], ~listHelper(xs[1 .. xs.high]))\n\nproc list[A](xs: varargs[A]): List[A] = listHelper(@xs)\n\nproc sum(xs: List[int]): int = (block:\n  match xs:\n    Nil: 0\n    Cons(y, ys): y + sum(ys[])\n)\n\necho sum(list(1, 2, 3, 4, 5))\n```\n\nVersions\n--------\n\nPatty 0.3.0 works for latest Nim (devel). For older versions of Nim (up to 0.16.0),\nuse Patty 0.2.1.\n\nThings that do not work (yet)\n-----------------------------\n\nOne would expect many forms of pattern matching but, at least for now, the\nsupport in Patty is very limited. Things that would be nice to support but do\nnot work yet include:\n\n* matching a constant\n\n```nim\nmatch c:\n  \"hello\":\n    echo \"the string was hello\"\n```\n\n* matching an existing variable\n\n```nim\nlet x = 5\nmatch c:\n  x:\n    echo \"c == 5\"\n```\n\n* nested pattern matching\n\n```nim\nmatch c:\n  Circle(Point(x: x, y: y), r: r):\n    echo \"the abscissa of the center is \", x\n```\n\n* matching without binding\n\n```nim\nmatch c:\n  Circle:\n    echo \"it is a circle!\"\n```\n\n* binding subpatterns\n\n```nim\nmatch getMeACircle():\n  c@Circle(x, y, r):\n    echo \"there you have \", c\n```\n\n* unification\n\n```nim\nmatch r:\n  Rectangle(w: x, h: x):\n    echo \"it is a square\"\n```\n\n* guards\n\n```nim\nmatch c:\n  Circle(x: x, y: y, r: r) if r \u003c 0:\n    echo \"the circle has negative length\"\n```\n\n* variable-length pattern matching, such as with arrays\n\n```nim\nmatch c:\n  [a, b, c]:\n    echo \"the length is 3 and the first elements is \", a\n```\n\n* custom pattern matchers, such as in regexes\n\n```nim\nlet Email = r\"(\\w+)@(\\w+).(\\w+)\"\nmatch c:\n  Email(name, domain, tld):\n    echo \"hello \", name\n```\n\n* combining patterns with `or`\n\n```nim\nmatch c:\n  Circle or Rectangle:\n    echo \"it is a shape\"\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreaferretti%2Fpatty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreaferretti%2Fpatty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreaferretti%2Fpatty/lists"}