{"id":16733537,"url":"https://github.com/chessai/theseus","last_synced_at":"2025-03-17T01:31:45.905Z","repository":{"id":41380840,"uuid":"123754510","full_name":"chessai/theseus","owner":"chessai","description":"theseus, functional programming language with fully reversible computation","archived":false,"fork":false,"pushed_at":"2019-06-03T23:33:42.000Z","size":2062,"stargazers_count":121,"open_issues_count":3,"forks_count":9,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-07T21:40:52.582Z","etag":null,"topics":["functional-programming","programming-language","reversible-computation","theseus"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chessai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-04T03:48:27.000Z","updated_at":"2025-01-10T23:59:13.000Z","dependencies_parsed_at":"2022-09-12T04:01:31.805Z","dependency_job_id":null,"html_url":"https://github.com/chessai/theseus","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chessai%2Ftheseus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chessai%2Ftheseus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chessai%2Ftheseus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chessai%2Ftheseus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chessai","download_url":"https://codeload.github.com/chessai/theseus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243836015,"owners_count":20355615,"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":["functional-programming","programming-language","reversible-computation","theseus"],"created_at":"2024-10-12T23:50:13.582Z","updated_at":"2025-03-17T01:31:45.098Z","avatar_url":"https://github.com/chessai.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Credit:\nCredit for design and original implementation of Theseus goes to\nRoshan P. James and Amr Sabry of Indiana University. This is my fork\nof their implementation, which I intend to keep as a pet project.\n\nThe original project can be found [here](https://bitbucket.org/roshanjames/theseus).\n\n# Theseus, the programming language.\n\nWhy create another programming language in this time and age? And why\nmake such an obscure finicky one wherein it's not obvious how one can\nwrite a web server or an iphone app?  Theseus exists due to a certain\nphilosophical point of view on computation. What follows is a casual\noverview.\n\nThe paradox of the Ship of Theseus was originally proposed by Plutarch\nin the setting of the ship that the Greek hero Theseus used for his\nadventures. Since the Athenians admired Theseus they took on the task\nof preserving the ship in his memory. Over time, as parts of the ship\ngot spoiled, they replaced them with equivalent new parts. Thus the\nship was always in good shape.\n\nHowever one day, many years later, someone makes the observation that\nthe ship no longer had any original parts. The entirety of the ship\nhad been replaced piece by piece. This raises the question: is this\nship really still the ship of Theseus? \n\nAnd, if not, when does the change take place? When the first part is\nreplaced? When the last one is replaced? When about half the parts are\nreplaced?\n\nFurther, to compound the question, Thomas Hobbes added the following\ncorollary: Imagine a junk yard outside Athens where all the discarded\nparts of the ship had been collected. If the proprietor of the junk\nassembled the parts into a ship, which ship is now the real ship of\nTheseus?\n\nThis is a philosophical paradox about the nature of equality and\nidentity. The question also applies to people. Since we are constantly\nchanging, cells and thought patterns continuously being replaced, is\nany person the same person they were some time ago? Can anyone ever\nnot change? Is a lactose free, sugar free, gluten free cupcake still a\ncupcake?\n\nOur programming language is named Theseus because, like in the\nparadox, the main computation step involves replacing a value by an\nisomorphic value. Since isomorphic things really are the same, have we\nchanged the value? And if not, what have we computed? Have we computed\nat all? As we keep doing these replacement in our program we will have\ntransformed the value that was the input to the value that is the\noutput.\n\nThere are many answers to the paradox of the ship of Theseus. The\nobvious answers of saying either 'yes, it is the same ship' or 'no\nit's not' have their justifications. Other answers exist too. One is\nto say that the question is wrong and that it meaningless to ask this\nsort of question about identity. Like a river whose very nature is to\nflow and change from moment to moment, so is nature of the ship. Over\ntime the ship changes, just like the river, and it is meaningless to\nask if is the same ship. It is our flawed notion of time and identity\nthat makes us assume that the ship is not like the river and to expect\none to have static identity and the other one to not.\n\nTheseus owes its design to a certain philosophical point of view about\ncomputation. Turing machines and the lambda-calculus are abstract\nmodels of computation. They were invented by people as mental models\nof how computation may be expressed. These are meant to serve as\nconceptual entities and we may think of them as being free of physical\nconstraints, i.e. it does not matter if you have the latest laptop, a\nwhole data center or no computer at all, the ideas underlying the\nabstract models apply equally well. However there is something wrong\nwith this view. \n\nAbstract models of computation are meant to apply to our physical\nreality. We might be constrained by the resources we have at hand to\ndo some computations. For instance, we might not have enough memory to\nrun some programs. However, we do expect that models are compatible\nwith our physics. For example, if we imagined that each step of a\nTuring machine took only half the amount of time the previous step\ntook to execute, after 2 units of time time all Turing machines would\nhave observable results. Such a machine is impossible to build (except\nmaybe in the movie Inception where you could fall asleep causing time\nto speed up in each recursive dream step). Its not just the difficulty\nof building it thats at issue here.  This violates the our notion of\nspace and time in a deep way. Futher it gives rise to issues in our\nmathemaics and formal logics since it resolves the halting problem. We\nthink of such models of computations that violate physics merely as\ncuriosities and not as modeling computation in the physical world.\n\nThe line of work that Theseus comes from stems from the idea that\nabstract models of computation, must in their essence be compatible\nwith our physics. Mordern physics at the level of quantum mechanics\ndescribes a universe where every fundamental action is reversible and\nfundamental quantities such as energy, matter and information are\nconserved. The notion of conservation of information stems from a line\nof thought originating from Maxwell's paradox of the \"demon\" that\nseemed to violate the Second law of Thermodynamics and its current\naccepted resolution set forth by Ralph Landauer. Landauer argued\nthat the demon must do thermodynamic work to forget the information\nthat its learns about the speed of each particle. This act of\nforgetting information implied that information should be treated as a\nphysical quantity subject to conservation laws. In more recent years,\nPasquele Malacaria and others wrote about the entropy of computation\nand Eric Lutz and others experimentally verified Landauer's\nprinciple. (\"Maxwell's Demon 2\" by Leff and Rex is a pretty good read\non the general topic; the first part of the book is pretty accessible\nand the later part is largely a collection of historically relevant\npapers.)\n\nSo the physics that Theseus is concerned with is this strange new\nphysics where information is not longer a concept of the human mind,\nlike love and peace, but a physical entity. This has happened to\ncomputation before; before Turing worked out that computation itself\nhad a formal notion that can be cpatured by abstract computational\nmodels, computation itself was considered an activity of the human\nmind not subject to the rigors of mathematics and logic.\n\nThe model of computation that was originally devised in this regard\ncame out under the longish name of 'dagger symmetric traced bimonoidal\ncategories'. The name was too long, was somewhat imprecise and didn't\nreally say much at all. The underlying idea was that we could look for\ncomputation in the rules of equality. \n\nIf every allowed operation is a transformation of one quantity for\nanother equivalent quantity, then computation should preserve\ninformation. However, can we even compute like this? After all, our\nconventional models of computation allows us to do \"logical or\" and\n\"logical and\" reducing two bools into one bool. Changing the value of\na variable means loosing the information in that variable\nforever. Conditional statements and loops seem to inherently be lossy\noperations because it is unclear how to execute them in reverse\nwithout knowing which banches were originally taken. And more\nimportantly, in the words of Barry Mazur, when is one thing equal to\nsome other thing?\n\nWe settled on simplest notions of equality, those familiar from\narithmetic. Rules like: \n\n```\n0 + x       = x\nx + y       = y + x\n(x + y) + z = x + (y + z)\n\n1 * x       = x\nx * y       = y * x\n(x * y) * z = x * (y * z)\n\nx * 0       = 0\nx * (y + z) = x * y + x * y\n\nif x = y and y = z, then x = z\nif x = y and w = z, then x + w = y + z\nif x = y and w = z, then x * w = y * z\nif x + y = x + z, then y = z\n```\n\nWe then took the numbers represented by `x`, `y` etc to be a measure\nof the \"amount\" of information and we only allowed operation that\ncorresponded to these equalities. Thus each operation preserved the\namount of information.\n\nFor a while it was unclear that what we had was even a model of\ncomputation, i.e. it took us a while to learn to express programs in\nit. Figuring out how to do the equivalent of a conditional took a long\ntime in that setting. The resulting model of computation was one where\nevery primitive operation was a sort of type isomorphism. When we add\nrecursive types to the mix, the model becomes Turing\ncomplete. Programs in this early model were easier to represent as\ndiagrams. Each program was essntially a complex wiring diagram where\neach wire had a type and process of \"running\" the program was the\nprocess of tracing the flow of particles through these wires. In the\nreferences below you can find lots of details about all this.\n\n## A Gentle Introduction to Theseus\n\nWhile all of this worked out in theory, it was tedious constructing\nprograms. For attempts of programming directly in the above model see\n[PDF](http://www.cs.indiana.edu/~sabry/papers/cat-rev.pdf),\n[PDF](http://dl.acm.org/citation.cfm?id=2103667\u0026dl=ACM\u0026coll=DL\u0026CFID=370820997\u0026CFTOKEN=65718506)\nand [PDF](http://link.springer.com/chapter/10.1007%2F978-3-642-36315-3_5).\nThis was when Theseus happened and we realized that we could express\nthe computations of the above information preserving model in a format\nthat looked somewhat like a regular functional programming\nlanguage. Much like the situation of replacing ship parts with other\nequivalent ship parts, Theseus computes by replacing values with\nequivalent values.\n\nAll the programs in Theseus are reversible and the type system will\nprevent you from writing anything that isnt. You can program in\nTheseus without knowing anything about the body of theory that\nmotivated it. \n\n```haskell\n-- booleans\ndata Bool = True | False\n\n-- boolean not \niso not :: Bool \u003c-\u003e Bool\n| True  \u003c-\u003e False\n| False \u003c-\u003e True\n```\n\nTheseus has algebraic data types. It has no GADTs or polymorphism yet,\nbut those are boring and can be added later. For the purpose of this\npresentation I will pretend that we do have polymorphism though. The\nequivalent of functions in Theseus are the things called `iso`. The\n`iso` called `not` maps `Bool` to `Bool`. In a coventional functional\nlanguage the part on the left hand side of the `\u003c-\u003e` is called the\npattern and the part on the right is called the expression. In\nTheseus, both the LHS and the RHS of the `\u003c-\u003e` are called patterns. In\nTheseus patterns and expressions are the same thing.\n\nNow here is the interesting bit: The patterns on the right hand side\nand the patterns on the left hand side should entirely cover the\ntype. On the RHS we have\n\n```\n:: Bool \u003c-\u003e\n| True  \u003c-\u003e\n| False \u003c-\u003e\n```\n\nwhich do indeed cover all the cases of the type `Bool`. On the LHS we\nhave\n\n```\n:: \u003c-\u003e Bool\n|  \u003c-\u003e False\n|  \u003c-\u003e True\n```\n\nwhich also covers the type bool. Patterns cover a type, when every\nvalue of the type is matched by one and only one pattern. So the\nfollowing single pattern does not cover the type `Bool` since there\nthe value `True` is unmatched.\n\n```\n:: Bool\n| False\n```\n\nThe following also does not cover the type `Bool` since the value\n`False` can be matched by both patterns. Here the variable `x` is a\npattern that matches any value.\n\n```\n:: Bool\n| False\n| x\n```\n\nThe following does cover the type `Bool`:\n\n```haskell\n:: Bool\n| x\n```\n\nHere is another type definition and another `iso`:\n\n```haskell\ndata Num = Z | S Num \n\niso parity :: Num * Bool \u003c-\u003e Num * Bool\n| n, x                   \u003c-\u003e lab $ n, Z, x\n| lab $ S n, m, x        \u003c-\u003e lab $ n, S m, not x\n| lab $ Z, m, x          \u003c-\u003e m, x\nwhere lab :: Num * Num * Bool\n```\n\nHere the `lab` is called a label and labels are followed by a `$`\nsign. All the patterns that have no label should cover the type of the\ncorresponding side of the function.\n\n```haskell\n:: Num * Bool \u003c-\u003e Num * Bool\n| n, x        \u003c-\u003e \n|             \u003c-\u003e \n|             \u003c-\u003e m, x\n```\n\nHere `n, x` on the LHS do cover the type `Num * Bool`. The variable\n`n` matches any `Num` and the variable `x` matches any `Bool`. The RHS\nis covered similalry by `m, x`.  The label `lab` has the type `Num *\nNum * Bool` and the intention is that the patterns of the label on the\nLHS and the RHS must each should cover the type of label.\n\n```haskell\n:: Num * Num * Bool \u003c-\u003e\n|                   \u003c-\u003e \n| lab $ S n, m, x   \u003c-\u003e \n| lab $ Z, m, x     \u003c-\u003e \n```\n\nOn the LHS we see that every value of the type `Num * Num * Bool` is\nmatched by one of the two patterns. The same applies to the RHS\npatterns of the `lab` label.\n\nLabels are a way of doing loops. When a pattern on the LHS results in\na label application on the RHS, control jumps to the LHS again and we\ntry to match the resulting value against a pattern of teh label on the\nRHS. This continues till we end up in a non-labelled pattern on the\nright. So lets trace the execution of `parity` when we given it the\ninput `S S S Z, True` of the type `Num * Bool`.\n\n```\nS S S Z, True           -\u003e lab $ S S S Z, Z, True \nlab $ S S S Z, Z, True\t-\u003e lab $ S S Z, S Z, False\nlab $ S S Z, S Z, False -\u003e lab $ S Z, S S Z, True\nlab $ S Z, S S Z, True\t-\u003e lab $ Z, S S S Z, False\nlab $ Z, S S S Z, False -\u003e S S S Z, False \n``` \n\nSo parity of `S S S Z, True` applied `not` to `True` three times,\nresulting in `S S S Z, False`.  Here is one more `iso`:\n\n```haskell\niso add1 :: Num \u003c-\u003e Num \n| x            \u003c-\u003e ret $ inR x\n| lab $ Z      \u003c-\u003e ret $ inL () \n| lab $ S n    \u003c-\u003e lab $ n\n| ret $ inL () \u003c-\u003e Z\n| ret $ inR n  \u003c-\u003e S n\nwhere ret :: 1 + Num\n      lab :: Num\n```\n\nThis `iso` has two labels `lab` and `ret` and one can verify that the\nsame coverage contraints hold. Here the type `1 + Num` would be\nwritten as `Either () Num` in Haskell, i.e. `1` is the unit type that\nhas only one value. The value is denoted by `()` and is read as\n\"unit\".  One can run `add1` on any `n` of type `Num` and verify that\nwe get `S n` back as the result.\n\nNow here is the next interesting bit: For any Theseus iso, we can get\nits inverse iso by simply swapping the LHS and RHS of the\nclauses. This also get at the essence of the idea of information\npreservation: if we have a program and an input to it, we can run the\ninput through the program and get the program and the output.  Using\nthe program and the output, we can recover the input.  \n\nSome programs may not terminate on some inputs and in such cases it is\nmeaningless to ask about reverse execution. Given that Theseus is a\nTuring complete language it is not surprising that some executions are\nnon-terminating. What however may be surprising that information\npreservation holds in the presence of non-termination.\n\nHere the reverse execution of `add1`, lets call it `sub1` does indeed\ndiverge on the input `Z`. For every other value of the form `S n` it\nreturns `n`. Its worth tracing this execution and thinking about why\nthis comes about in Theseus.\n\nTheseus also supports the notion of parametrizing an iso with another\none. For example:\n\n```haskell\niso if :: then:(a \u003c-\u003e b) -\u003e else:(a \u003c-\u003e b) -\u003e (Bool * a \u003c-\u003e Bool * b)\n| True, x  \u003c-\u003e True, then x\n| False, x \u003c-\u003e False, else x\n```\n\nHere the iso called `if` takes two arguments, `then` and `else`, and\ndecides which one to call depending on the value of the boolean\nargument. Theseus doesn't really have higher-order or first-class\nfunctions. The parameter isos are expected to be fully inlined before\ntransofrmation of the value starts. Theseus will only run a value\n`v:t1` on an iso of type `t1 \u003c-\u003e t2`, and the `-\u003e` types in the above\nshould be fully instantiated away. \n\nRunning `if ~then:add1 ~else:sub1` on the input `True, S Z` gives us\nthe output `True, S S Z` and running the same function with input\n`False, S Z` gives us `False, Z`. The syntax of labelled arguments is\nsimilar to that used by OCaml.\n\nReverse evaluation works by flipping LHS and RHS in the same way as\nbefore. However, you wonder, what happens when there is a function\ncall in a left hand side pattern? Here is a simple example:\n\n```haskell\niso adjoint :: f:(a \u003c-\u003e b) -\u003e (b \u003c-\u003e a)\n| f x \u003c-\u003e x\n```\n\nHere is the interesting bit again: An iso call on the left side\npattern is the dual of an iso call on the right hand side. When `f x`\nappears on the right we know that we have an `x` in hand and the\nresult we want is the result of the application `f x`.  When `f x`\nappears on the left it means that we have the result of the\napplication `f x` and we want to determine `x`. We can infer the value\nof `x` by tracing the flow of the given value backwards through `f`\nand the result of the this backward execution of `f` is `x`. We can do\nthis because isos represent information preserving transformations.\n\nSo if we had `add1` and its inverse `sub1`, then `adjoint ~f:add1` is\nequivalent to `sub1` and `adjoint ~f:(adjoint ~f:add1)` is equivalent\nto `add1`. \n\nSome references for additional reading.\n\n* Roshan P. James and Amr Sabry. Theseus: A High Level Language for\n  Reversible Computing. Work-in-progress report in the Conference on\n  Reversible Computation, 2014.\n  [PDF](http://www.cs.indiana.edu/~sabry/papers/theseus.pdf)\n\n  This is the paper that introduces Theseus.  The syntax of Theseus as\n  presented here differs somewhat from what is in the paper, but most\n  of the these syntactic differences are superficial and largely an\n  artifact of the difficulty of having Parsec understand location\n  sensitive syntax.  Please see below for some notes on how the\n  implementation of Theseus differs from that in the paper. For more\n  of the academically relevant citations, you can chase the references\n  at the end of the paper.\n\n* Harvey Leff and Andrew F. Rex. Maxwell's Demon 2 Entropy, Classical\n  and Quantum Information, Computing. CRC Press, 2002. \n  [Amazon](http://www.amazon.com/Maxwells-Entropy-Classical-Information-Computing/dp/0750307595)\n\n  This book is a survey of about a century and a half of thought about\n  Maxwell's demon with significant focus on Landauer's principle and\n  the work surrounding it.\n\n# Running Theseus Programs\n\nFor now Theseus does not have a well developed REPL and is still work\nin progress. We use the Haskell REPL to run it. Roughly:\n\n```shell\n$ cd examples/\n$ ghci ../src/Theseus.hs \n[...]\nOk, modules loaded: Theseus.\n*Theseus\u003e run \"peano.ths\"\n[...]\n-- {Loading bool.ths}\nTypechecking...\nEvaluating...\neval toffoli True, (True, True) = True, (True, False)\neval toffoli True, (True, False) = True, (True, True)\n[...]\n*Theseus\u003e \n```\n\nLike the `run` function above, we also have `echo` which parses the\ngiven file and echoes it on screen and `echoT` which prints out all\nthe definitions after parsing the given file and inlining all the\nimports. `echoT` also checks for violations of non-overlapping and\nexhaustive pattern coverage and reports these. For instance:\n\n```shell\n*Theseus\u003e echoT \"test.ths\"\n[...]\niso f :: Bool \u003c-\u003e Bool\n| True \u003c-\u003e False\n| x \u003c-\u003e True\n \nError: LHS of f: Multiple patterns match values of the form : True\n*Theseus\u003e \n```\n\n# Differences between the implementation and the RC 2014 paper\n\nThe current implementation of Theseus is of experimental status. There\nare many niceties and tools required for a proper programming language\nthat are not available as yet. When editing Theseus programs turn on\nHaskell mode in Emacs and that tends to work out ok. \n\nHere are some notable differences from that of the paper.\n\n* The type checker is not yet implmented. However, Theseus will check\n  strict coverage and complain if there are clauses that are\n  overlapping or non-exhaustve. It does not check types of the\n  variables or that they are used exaclty once. The strict coverage of\n  function call contexts is also not checked.\n\n* We need to specify a keyword called `iso` when defining maps. \n\n* The import and file load semantics currently creates one flat list\n  of definitions. This can violate the expected static scope of top\n  level definitions.\n\n* The paper allows for dual definitions as shown below. The current\n  implementation does not handle simulataneous definition of the\n  inverse function `:: sub1`\n\n```\n  add1 :: Num \u003c-\u003e Num :: sub1\n  | ... \n```\n   \n* All constructors take only one type or value arugment, similar to\n  OCaml constructor definitions. Hence one has to write\n\n```\ndata List = E | Cons (Num * List)\n\nf :: ...\n| ... Cons (n, ls) ...  \n```\n\ninstead of \n```\ndata List = E | Cons Num List\n\nf :: ...\n| ... (Cons n ls) ...  \n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchessai%2Ftheseus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchessai%2Ftheseus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchessai%2Ftheseus/lists"}