{"id":15288015,"url":"https://github.com/witchcrafters/quark","last_synced_at":"2025-12-12T00:30:37.048Z","repository":{"id":3254915,"uuid":"48834998","full_name":"witchcrafters/quark","owner":"witchcrafters","description":"Common combinators for Elixir","archived":false,"fork":false,"pushed_at":"2022-06-22T20:00:38.000Z","size":307,"stargazers_count":320,"open_issues_count":8,"forks_count":15,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-31T05:05:34.235Z","etag":null,"topics":["combinator","curried-functions","elixir","functional-languages","functional-programming","operators","pointfree","ski-combinators"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/quark","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/witchcrafters.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-12-31T05:26:32.000Z","updated_at":"2025-03-15T16:07:59.000Z","dependencies_parsed_at":"2022-09-19T07:30:24.981Z","dependency_job_id":null,"html_url":"https://github.com/witchcrafters/quark","commit_stats":null,"previous_names":["expede/quark","robot-overlord/quark"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Fquark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Fquark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Fquark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Fquark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/witchcrafters","download_url":"https://codeload.github.com/witchcrafters/quark/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247601449,"owners_count":20964864,"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":["combinator","curried-functions","elixir","functional-languages","functional-programming","operators","pointfree","ski-combinators"],"created_at":"2024-09-30T15:43:47.099Z","updated_at":"2025-12-12T00:30:37.009Z","avatar_url":"https://github.com/witchcrafters.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Quark: Common combinators for Elixir\n\n![](https://github.com/expede/quark/blob/main/brand/logo.png?raw=true)\n\n[![Build Status](https://travis-ci.org/expede/quark.svg?branch=master)](https://travis-ci.org/expede/quark) [![Inline docs](http://inch-ci.org/github/expede/quark.svg?branch=master)](http://inch-ci.org/github/expede/quark) [![Deps Status](https://beta.hexfaktor.org/badge/all/github/expede/quark.svg)](https://beta.hexfaktor.org/github/expede/quark) [![hex.pm version](https://img.shields.io/hexpm/v/quark.svg?style=flat)](https://hex.pm/packages/quark) [![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](http://hexdocs.pm/quark/) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/expede/quark/blob/master/LICENSE)\n\n# Table of Contents\n- [Quick Start](#quick-start)\n- [Summary](#summary)\n  - [Includes](#includes)\n- [Functional Overview](#functional-overview)\n  - [Curry](#curry)\n    - [Functions](#functions)\n    - [Macros](#macros-defcurry-and-defcurryp)\n  - [Partial](#partial)\n    - [Macros](##macros-defpartial-and-defpartialp)\n  - [Pointfree](#pointfree)\n  - [Compose](#compose)\n  - [Common Combinators](#common-combinators)\n    - [Classics](#classics)\n      - [SKI System](#ski-system)\n      - [BCKW System](#bckw-system)\n      - [Fixed Point](#fixed-point)\n    - [Sequence](#sequence)\n\n# Quick Start\n\n```elixir\n\ndef deps do\n  [{:quark, \"~\u003e 2.3\"}]\nend\n\ndefmodule MyModule do\n  use Quark\n\n  # ...\nend\n```\n\n# Summary\n\n[Elixir](http://elixir-lang.org) is a functional programming language,\nbut it lacks some of the common built-in constructs that many other functional\nlanguages provide. This is not all-together surprising, as Elixir has a strong\nfocus on handling the complexities of concurrency and fault-tolerance, rather than\ndeeper functional composition of functions for reuse.\n\n## Includes\n\n- A series of classic combinators (SKI, BCKW, and fixed-points), along with friendlier aliases\n- Fully-curried and partially applied functions\n- Macros for defining curried and partially applied functions\n- Composition helpers\n  - Composition operator: `\u003c|\u003e`\n- A plethora of common functional programming primitives, including:\n  - `id`\n  - `flip`\n  - `const`\n  - `pred`\n  - `succ`\n  - `fix`\n  - `self_apply`\n\n# Functional Overview\n\n## Curry\n\n### Functions\n`curry` creates a 0-arity function that curries an existing function. `uncurry` applies arguments to curried functions, or if passed a function creates a function on pairs.\n\n### Macros: `defcurry` and `defcurryp`\nWhy define the function before currying it? `defcurry` and `defcurryp` return\nfully-curried 0-arity functions.\n\n```elixir\n\ndefmodule Foo do\n  import Quark.Curry\n\n  defcurry div(a, b), do: a / b\n  defcurryp minus(a, b), do: a - b\nend\n\n# Regular\ndiv(10, 2)\n# =\u003e 5\n\n# Curried\ndiv.(10).(5)\n# =\u003e 2\n\n# Partially applied\ndiv_ten = div.(10)\ndiv_ten.(2)\n# =\u003e 5\n\n```\n\n## Partial\n\n:crown: We think that this is really the crowning jewel of `Quark`.\n`defpartial` and `defpartialp` create all arities possible for the defined\nfunction, bare, partially applied, and fully curried.\nThis does use up the full arity-space for that function name, however.\n\n### Macros: `defpartial` and `defpartialp`\n\n```elixir\n\ndefmodule Foo do\n  import Quark.Partial\n\n  defpartial one(), do: 1\n  defpartial minus(a, b, c), do: a - b - c\n  defpartialp plus(a, b, c), do: a + b + c\nend\n\n# Normal zero-arity\none\n# =\u003e 1\n\n# Normal n-arity\nminus(4, 2, 1)\n# =\u003e 1\n\n# Partially-applied first two arguments\nminus(100, 5).(10)\n# =\u003e 85\n\n# Partially-applied first argument\nminus(100).(10).(50)\n# =\u003e 40\n\n# Fully-curried\nminus.(10).(2).(1)\n# =\u003e 7\n\n```\n\n## Pointfree\nAllows defining functions as straight function composition (ie: no need to state the argument).\nProvides a clean, composable named functions. Also doubles as an aliasing device.\n\n```elixir\ndefmodule Contrived do\n  import Quark.Pointfree\n  defx sum_plus_one, do: Enum.sum() |\u003e fn x -\u003e x + 1 end.()\nend\n\nContrived.sum_plus_one([1,2,3])\n#=\u003e 7\n```\n\n## Compose\nCompose functions to do convenient partial applications.\nVersions for composing left-to-right and right-to-left are provided\n\nThe operator `\u003c|\u003e` is done \"the math way\" (right-to-left).\nThe operator `\u003c~\u003e` is done \"the flow way\" (left-to-right).\n\nVersions on lists also available.\n\n```elixir\nimport Quark.Compose\n\n# Regular Composition\nsum_plus_one = fn x -\u003e x + 1 end \u003c|\u003e \u0026Enum.sum/1\nsum_plus_one.([1,2,3])\n#=\u003e 7\n\nadd_one = \u0026(\u00261 + 1)\npiped = fn x -\u003e x |\u003e Enum.sum |\u003e add_one.() end\ncomposed = add_one \u003c|\u003e \u0026Enum.sum/1\npiped.([1,2,3]) == composed.([1,2,3])\n#=\u003e true\n\nsum_plus_one = (\u0026Enum.sum/1) \u003c~\u003e fn x -\u003e x + 1 end\nsum_plus_one.([1,2,3])\n#=\u003e 7\n\n# Reverse Composition (same direction as pipe)\nx200 = (\u0026(\u00261 * 2)) \u003c~\u003e (\u0026(\u00261 * 10)) \u003c~\u003e (\u0026(\u00261 * 10))\nx200.(5)\n#=\u003e 1000\n\nadd_one = \u0026(\u00261 + 1)\npiped = fn x -\u003e x |\u003e Enum.sum() |\u003e add_one.() end\ncomposed = (\u0026Enum.sum/1) \u003c~\u003e add_one\npiped.([1,2,3]) == composed.([1,2,3])\n#=\u003e true\n```\n\n## Common Combinators\nA number of basic, general functions, including `id`, `flip`, `const`, `pred`, `succ`, `fix`, and `self_apply`.\n\n## Classics\n\n### SKI System\nThe SKI system combinators. `s` and `k` alone can be combined to express any\nalgorithm, but not usually with much efficiency.\n\nWe've aliased the names at the top-level (`Quark`), so you can use `const`\nrather than having to remember what `k` means.\n\n```elixir\n 1 |\u003e i()\n#=\u003e 1\n\n\"identity combinator\" |\u003e i()\n#=\u003e \"identity combinator\"\n\nEnum.reduce([1,2,3], [42], \u0026k/2)\n#=\u003e 3\n\n```\n\n### BCKW System\nThe classic `b`, `c`, `k`, and `w` combinators. A similar \"full system\" as SKI,\nbut with some some different functionality out of the box.\n\nAs usual, we've aliased the names at the top-level (`Quark`).\n\n```elixir\nc(\u0026div/2).(1, 2)\n#=\u003e 2\n\nreverse_concat = c(\u0026Enum.concat/2)\nreverse_concat.([1,2,3], [4,5,6])\n#=\u003e [4,5,6,1,2,3]\n\nrepeat = w(\u0026Enum.concat/2)\nrepeat.([1,2])\n#=\u003e [1,2,1,2]\n```\n\n### Fixed Point\nSeveral fixed point combinators, for helping with recursion. Several formulations are provided,\nbut if in doubt, use `fix`. Fix is going to be kept as an alias to the most efficient\nformulation at any given time, and thus reasonably future-proof.\n\n```elixir\nfac = fn fac -\u003e\n  fn\n    0 -\u003e 0\n    1 -\u003e 1\n    n -\u003e n * fac.(n - 1)\n  end\nend\n\nfactorial = y(fac)\nfactorial.(9)\n#=\u003e 362880\n```\n\n### Sequence\nReally here for `pred` and `succ` on integers, by why stop there?\nThis works with any ordered collection via the `Quark.Sequence` protocol.\n\n```elixir\nsucc 10\n#=\u003e 11\n\n42 |\u003e origin() |\u003e pred() |\u003e pred()\n#=\u003e -2\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwitchcrafters%2Fquark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwitchcrafters%2Fquark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwitchcrafters%2Fquark/lists"}