{"id":15519555,"url":"https://github.com/slovnicki/plam","last_synced_at":"2025-04-06T02:09:44.330Z","repository":{"id":53494258,"uuid":"99431375","full_name":"slovnicki/pLam","owner":"slovnicki","description":"An interpreter for learning and exploring pure λ-calculus","archived":false,"fork":false,"pushed_at":"2021-04-11T23:18:57.000Z","size":1153,"stargazers_count":451,"open_issues_count":5,"forks_count":17,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-30T01:09:03.991Z","etag":null,"topics":["church","church-encoding","computability","computability-theory","functional-programming","interpreter","lambda","lambda-calculus","language","logic-programming","numeral-systems","programming-language"],"latest_commit_sha":null,"homepage":"","language":"Haskell","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/slovnicki.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}},"created_at":"2017-08-05T15:32:04.000Z","updated_at":"2025-01-23T20:10:19.000Z","dependencies_parsed_at":"2022-08-19T23:11:44.533Z","dependency_job_id":null,"html_url":"https://github.com/slovnicki/pLam","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slovnicki%2FpLam","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slovnicki%2FpLam/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slovnicki%2FpLam/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slovnicki%2FpLam/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slovnicki","download_url":"https://codeload.github.com/slovnicki/pLam/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247423515,"owners_count":20936626,"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":["church","church-encoding","computability","computability-theory","functional-programming","interpreter","lambda","lambda-calculus","language","logic-programming","numeral-systems","programming-language"],"created_at":"2024-10-02T10:21:59.535Z","updated_at":"2025-04-06T02:09:44.307Z","avatar_url":"https://github.com/slovnicki.png","language":"Haskell","funding_links":["https://www.buymeacoffee.com/slovnicki"],"categories":[],"sub_categories":[],"readme":"[![Version](https://img.shields.io/github/release/sandrolovnicki/pLam.svg)](https://github.com/sandrolovnicki/pLam/releases)\n[![License](https://img.shields.io/github/license/sandrolovnicki/pLam.svg)](https://github.com/sandrolovnicki/pLam/blob/master/LICENSE)\n\n\u003ca href=\"https://www.buymeacoffee.com/slovnicki\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" height=\"30px\" width= \"108px\"\u003e\u003c/a\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width=100% height=auto src=\"https://raw.githubusercontent.com/sandrolovnicki/pLam/master/res/demo-v2.gif\"\u003e\n\u003c/p\u003e\n\npLam (**p**ure **Lam**bda calculus) is a tool to explore, define and evaluate various λ-expressions. Code written in pLam can be executed interactively within pLam's shell or stored in a file with `.plam` extension and run anytime.\n\nInside `import/` directory, many useful λ-expressions are already implemented to be used as libraries.\n\n---\n\n### Table of contents\n\n- [Install a package](#install)\n  - [Arch Linux](#aur)\n  - [Debian](#deb)\n- [Build from source](#build)\n  - [Prerequisites](#prerequisites)\n  - [First time setup](#fts)\n  - [Building](#build)\n  - [Running (locally)](#runl)\n  - [Running (globally)](#rung)\n- [Syntax and Semantics](#synsem)\n  - [λ-expressions](#expr)\n    - [Variable](#var)\n    - [Abstraction](#abs)\n    - [Application](#app)\n  - [Commands](#commands)\n    - [Define](#def)\n    - [Evaluate](#eval)\n    - [Import](#imp)\n    - [Export](#exp)\n    - [Comment](#comm)\n    - [Run](#run)\n    - [Print](#print)\n  - [Syntactic Sugars](#syns)\n    - [Church numerals](#cn)\n    - [Binary numerals](#bn)\n    - [Lists](#lists)\n- [Examples](#examples)\n  - [Fun with booleans](#fwb)\n  - [Fun with arithmetic](#fwa)\n  - [Factorial](#fact)\n    - [Standard way](#sw)\n    - [Primitive recursive way](#prw)\n  - [Binary numerals](#bin)\n  - [Lists](#liex)\n  - [Redex coloring](#rc)\n  - [Running the existing program](#runex)\n    - [Without entering pLam's shell](#runout)\n- [Disclaimer](#disclaimer)\n- [Contributing](#contribute)\n\n\u003ca name=\"install\"/\u003e\n\n# Install a Package\n\n\u003ca name=\"aur\"/\u003e\n\n### Arch Linux\n\npLam's AUR package is at https://aur.archlinux.org/packages/plam thanks to @Xmgplays.   \n\nGit Clone URL: https://aur.archlinux.org/plam.git\n\n\u003ca name=\"deb\"/\u003e\n\n### Debian\n\n(coming soon...)\n\n\u003ca name=\"build\"/\u003e\n\n# Build from source\n\n\u003ca name=\"prerequisites\"/\u003e\n\n## Prerequisites\nThis project builds using Haskell tool stack documented at https://docs.haskellstack.org/en/stable/README/.\n\nOn most Unix systems, you can get stack by typing:\n```\ncurl -sSL https://get.haskellstack.org/ | sh\n```\nor:\n```\nwget -qO- https://get.haskellstack.org/ | sh\n```\nOn Windows, you can download 64-bit installer given at https://docs.haskellstack.org/en/stable/README/.\n\n\u003ca name=\"fts\"/\u003e\n\n## First time setup\n1. clone project repository\n```\ngit clone https://github.com/sandrolovnicki/pLam.git\n```\n2. go to project directory\n```\ncd pLam\n```\n3. setup stack on isolated location\n```\nstack setup\n```\n\u003ca name=\"build\"/\u003e\n\n## Building\n4. use stack to build project\n```\nstack build\n```\n**note:** if build was not successful, it may be due to:\n\n- curses library\n  - the solution is to install it (on Ubuntu: `sudo apt-get install libncurses5-dev libncursesw5-dev`)\n\n\u003ca name=\"runl\"/\u003e\n\n## Running (locally)\n5.a) use stack to run project executable from project's directory\n```\nstack exec plam\n```\n\u003ca name=\"rung\"/\u003e\n\n## Running (globally (Unix systems))\n5.b) use `make_global.sh` script to create a global command 'plam' that can be used to start pLam from anywhere in your system. The script will also change your import path in src/Config.hs so you need to build the project again.\n```\nsudo ./make_global.sh\nstack build\n```\nNow, (and anytime in the future!), you can start pLam from anywhere in your system by just typing\n```\nplam\n```\n---\n\u003ca name=\"synsem\"/\u003e\n\n# Syntax and semantics\n\n\u003ca name=\"expr\"/\u003e\n\n## λ-expressions\n\n\u003ca name=\"var\"/\u003e\n\n### Variable\nλ-variable is required to be lowercase and a single letter. For example, `x` is a good λ-variable for pLam and `X`, `var`,... are not. There are also environment variables (names for defined λ-expressions) which are every string that is not parsed as λ-variable, λ-abstraction or λ-application.\n\n\u003ca name=\"abs\"/\u003e\n\n### Abstraction\nλ-abstraction is written the same as in the language of pure (untyped) λ-calculus, except that pLam treats a symbol `\\` as `λ` and it is required to write a space after `.`. For example, `λx.λy.x` would be written `\\x. \\y. x` in pLam. One can also write λ-abstraction in the \"uncurried\" form: `\\xy. x` or `\\x y. x`.\n\n\u003ca name=\"app\"/\u003e\n\n### Application\nλ-application is written like 2 λ-expressions separated by a space, for example `(\\x. x) (\\xy.x)` or `(\\x. x) MyExpression` or `myexp1 myexp2`. Brackets `(` and `)` are used as usual and are not required to be written for application association; the default association is to the left, so `M N P` is parsed as `(M N) P` and one only needs to specify with brackets if the intended expression should be `M (N P)`.\n\n\u003ca name=\"commands\"/\u003e\n\n## Commands\nA block of code in pLam is a line, and possible lines (commands) are the following:\n\n\u003ca name=\"def\"/\u003e\n\n### Define\n\n- syntax: `\u003cstring\u003e = \u003cλ-expression\u003e`\n- semantics: let the `\u003cstring\u003e` be a name for `\u003cλ-expression\u003e`.\n- examples: `T = \\x y. x`, `myexpression = T (T (\\x. x) T) T`\n- restriction: `\u003cstring\u003e` needs to be of length\u003e1 or starting with uppercase letter\n\n\u003ca name=\"eval\"/\u003e\n\n### Evaluate\n\n- syntax: `?\u003cevop\u003e ?\u003cevop\u003e \u003cλ-expression\u003e` where `\u003cevop\u003e`s are evaluation options; `:d` and/or `:cbv`\n- semantics: reduce the `\u003cλ-expression\u003e` to β-normal form. If `:d` is chosen as option, all the reduction steps will be shown. If `:cbv` is chosen as option, reductions will be performed in a call-by-value manner, first reducing the argument before substituting it for bound variable. That is, call-by-name is the default reduction option if `:cbv` is not chosen.\n- example: `\\x y. x`, `:d T (T (\\x. x) T) T`, `:d :cbv T (T (\\x. x) T) T`, `:cbv and (or T F) T`\n- example 2: `F omega T` will reduce to `T`, but `:cbv F omega T` will run forever\n- restriction: none\n\n\u003ca name=\"imp\"/\u003e\n\n### Import\n\n- syntax: `:import \u003cstring\u003e`\n- semantics: put all the expressions defined in the file `import/\u003cstring\u003e.plam` into the list of environment variables.\n- example: `:import std`\n- restriction: `\u003cstring\u003e.plam` has to be inside `import/` directory within the pLam project directory\n\n\u003ca name=\"exp\"/\u003e\n\n### Export\n\n- syntax `:export \u003cstring\u003e`\n- semantics: put all the expressions in the list of environment variables into the file `import/\u003cstring\u003e.plam`\n- example: `:export test`\n- restriction: `\u003cstring\u003e.plam` cannot already exist\n\n\u003ca name=\"comm\"/\u003e\n\n### Comment\n\n- syntax: `--\u003cstring\u003e`\n- semantics: a comment line\n- example: `-- this is a comment`\n- restriction: none\n\n\u003ca name=\"run\"/\u003e\n\n### Run\n\n- syntax: `:run \u003cstring\u003e`\n- semantics: runs a `.plam` file with relative path `\u003cstring\u003e.plam`\n- example: `:run \u003crelative-path-to-plam\u003e/examples/2.5.2`\n- restrictions: `~` for home does not work\n\n\u003ca name=\"print\"/\u003e\n\n### Print\n\n- syntax: `:print \u003cstring\u003e`\n- semantics: prints `\u003cstring\u003e` to a new line. It mostly makes sense to use it in .plam programs to be executed, not in interactive mode where a comment should do the job better.\n- example: `:print this is a message`\n- restrictions: none\n\n\u003ca name=\"syns\"/\u003e\n\n## Syntactic Sugars\n\npLam is equipped with some (optional) shortcuts to work with often used expressions.  \n\n\u003ca name=\"cn\"/\u003e\n\n### Church numerals\n\nChurch numerals can be typed as `0`, `1`, `2`,... and pLam parses those integers as `λfx. x`, `λfx. f x`, `λfx. f (f x)`, ...\n\n\u003ca name=\"bn\"/\u003e\n\n### Binary numerals\n\nSimilar to handling Church numerals, pLam also handles binary numerals from `binary.plam` library. You can type them as `0b`, `1b`, `2b`, ...  which is them parsed as `λp. p (λxy. y) (λexy.x)`, `λp. p (λxy. x) (λexy.x)`, `λp. p (λxy. y) (λp. p (λxy. x) (λexy.x))`, ...  \nNote that binary numerals are nothing standard, but something I implemented, so I suppose the only documentation for them is here.\n\n\u003ca name=\"lists\"/\u003e\n\n### Lists\n\nList encoding is pretty standard; `empty = T`, `append = λhtfl. l h t`, and you can use syntact sugar which parses `[1,2]` into `λfl. l 1 (λfl. l 2 empty)`, `[T,\\x.x]` into `λfl. l T (λfl. l (λx.x) empty)` and so on...\n\n\n---\n\n\u003ca name=\"examples\"/\u003e\n\n# Examples\n\n**NOTE:** Output might be slightly different due to constant fixes and changes. Fully updated examples will be put each time they diverge too far from current.  \nAll the examples can be found in `examples/` directory.\n\n\u003ca name=\"fwb\"/\u003e\n\n### Fun with booleans\n```\npLam\u003e :import booleans\npLam\u003e \npLam\u003e and (or F (not F)) (xor T F)\n|\u003e reductions count               : 18\n|\u003e uncurried β-normal form        : (λxy. x)\n|\u003e curried (partial) α-equivalent : T\n```\n\n\u003ca name=\"fwa\"/\u003e\n\n### Fun with arithmetic\n```\npLam\u003e :import std\npLam\u003e \npLam\u003e mul (add 2 (S 2)) (sub (exp 2 3) (P 8))\n|\u003e reductions count               : 762\n|\u003e uncurried β-normal form        : (λfx. f (f (f (f (f x)))))\n|\u003e curried (partial) α-equivalent : 5\n```\n\n\u003ca name=\"fact\"/\u003e\n\n### Factorial\n\n\u003ca name=\"sw\"/\u003e\n\n#### \"standard\" way\n```\npLam\u003e :import std\npLam\u003e \npLam\u003e fFact = \\f. \\x. (isZ x) 1 (mul x (f (P x)))\npLam\u003e Fact = Y fFact\npLam\u003e \npLam\u003e Fact 3\n|\u003e reductions count               : 646\n|\u003e uncurried β-normal form        : (λfx. f (f (f (f (f (f x))))))\n|\u003e curried (partial) α-equivalent : 6\n```\n\n\u003ca name=\"prw\"/\u003e\n\n#### primitive recursive way\n```\npLam\u003e :import std\npLam\u003e :import comp\npLam\u003e \npLam\u003e fact = PR0 1 (C22 mul (C2 S I12) I22)\npLam\u003e fact 3\n|\u003e reductions count               : 898\n|\u003e uncurried β-normal form        : (λfx. f (f (f (f (f (f x))))))\n|\u003e curried (partial) α-equivalent : 6\n```\n\n\u003ca name=\"bin\"/\u003e\n\n### Binary numerals\n```\npLam\u003e :import binary\npLam\u003e \npLam\u003e 0b\n|\u003e reductions count               : 2\n|\u003e uncurried β-normal form        : (λp.((p (λxy. y)) (λexy.x)))\n|\u003e curried (partial) α-equivalent : 0b\npLam\u003e \npLam\u003e 2048b\n|\u003e reductions count               : 24\n|\u003e uncurried β-normal form        : (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. y)) (λp.((p (λxy. x)) (λexy.x)))))))))))))))))))))))))\n|\u003e curried (partial) α-equivalent : (λp. ((p F) 1024b))\npLam\u003e \npLam\u003e addB 7b (subBs 2b 3b)\n|\u003e reductions count               : 9458\n|\u003e uncurried β-normal form        : (λp.((p (λxy. x)) (λp.((p (λxy. x)) (λp.((p (λxy. x)) (λexy.x)))))))\n|\u003e curried (partial) α-equivalent : 7b\n```\n\n\u003ca name=\"liex\"/\u003e\n\n### Lists\n\n```\npLam\u003e :import list\npLam\u003e \npLam\u003e list = Merge [3,1] [2]\npLam\u003e rlist = Reverse list\npLam\u003e \npLam\u003e Get 0 rlist\n|\u003e reductions count               : 243\n|\u003e uncurried β-normal form        : (λfx. f (f x))\n|\u003e curried (partial) α-equivalent : 2\npLam\u003e Get 0 list\n|\u003e reductions count               : 50\n|\u003e uncurried β-normal form        : (λfx. f (f (f x)))\n|\u003e curried (partial) α-equivalent : 3\npLam\u003e \npLam\u003e QSort list\n|\u003e reductions count               : 459\n|\u003e uncurried β-normal form        : (λfl. (l (λfx. f x)) (λfl. (l (λfx. f (f x))) (λfl. (l (λfx. f (f (f x)))) (λfl. f))))\n|\u003e curried (partial) α-equivalent : (λf. (λl. ((l 1) (λf. (λl. ((l 2) (λf. (λl. ((l 3) empty)))))))))\n\n```\n\n\u003ca name=\"rc\"/\u003e\n\n### Redex coloring\n![redex_coloring.png](https://raw.githubusercontent.com/sandrolovnicki/pLam/master/res/redex_coloring.png \"Redex Coloring\")\n\n\n\u003ca name=\"runex\"/\u003e\n\n### Running the existing program:\n```\npLam\u003e :run examples/2.5.2\n=================================\n\u003c zero\n=================================\n|\u003e reductions count               : 114\n|\u003e uncurried β-normal form        : (λfx. f (f x))\n|\u003e curried (partial) α-equivalent : 2\n```\n\n\u003ca name=\"runout\"/\u003e\n\n#### Without entering pLam's shell:\n```\nplam ~/Projects/pLam/examples/2.5.2.plam\n=================================\n\u003c zero\n=================================\n|\u003e reductions count               : 114\n|\u003e uncurried β-normal form        : (λfx. f (f x))\n|\u003e curried (partial) α-equivalent : 2\nDone.\n```\n\n---\n\n\u003ca name=\"disclaimer\"/\u003e\n\n#### Disclaimer for Haskell experts\n\nI am not a Haskell expert. In fact, this is my first and only Haskell project. It is inevitable that existing code could be written better and I wish to do it in the upcoming future.  \nThe goal of the project was to create an environment for easy building of new expressions from previously defined ones, so that one could explore λ-calculus. It was a helper tool so I could define and test a new numeral system in λ-calculus, for my master thesis. Now, when this all went well, the time is coming for me to get back to Haskell code.\n\n\u003ca name=\"contribute\"/\u003e\n\n### Contributing\n\nIf you would like to see some improvements or new features, you can open an issue. I will sort issues into milestones and you will know pretty quickly when to expect it to be done. If you can implement your ideas yourself, great! Pull requests are welcome.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslovnicki%2Fplam","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslovnicki%2Fplam","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslovnicki%2Fplam/lists"}