{"id":24609664,"url":"https://github.com/bjoernloetters/noocleus-one","last_synced_at":"2025-03-18T12:37:56.410Z","repository":{"id":88394248,"uuid":"177576687","full_name":"BjoernLoetters/noocleus-one","owner":"BjoernLoetters","description":"A minimal functional programming language with type inference, algebraic data types and pattern matching","archived":false,"fork":false,"pushed_at":"2019-03-27T17:22:27.000Z","size":30,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-24T18:16:17.510Z","etag":null,"topics":["algebraic-data-types","functional-programming","hindley-milner","let-polymorphism","pattern-matching","polymorphism","scala","type-check","type-inference","type-system"],"latest_commit_sha":null,"homepage":null,"language":"Scala","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/BjoernLoetters.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-25T11:53:02.000Z","updated_at":"2024-06-30T18:18:52.000Z","dependencies_parsed_at":"2023-03-04T03:15:19.974Z","dependency_job_id":null,"html_url":"https://github.com/BjoernLoetters/noocleus-one","commit_stats":null,"previous_names":["bjoernloetters/noocleus-one"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BjoernLoetters%2Fnoocleus-one","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BjoernLoetters%2Fnoocleus-one/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BjoernLoetters%2Fnoocleus-one/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BjoernLoetters%2Fnoocleus-one/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BjoernLoetters","download_url":"https://codeload.github.com/BjoernLoetters/noocleus-one/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244225728,"owners_count":20419142,"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":["algebraic-data-types","functional-programming","hindley-milner","let-polymorphism","pattern-matching","polymorphism","scala","type-check","type-inference","type-system"],"created_at":"2025-01-24T18:16:30.854Z","updated_at":"2025-03-18T12:37:56.369Z","avatar_url":"https://github.com/BjoernLoetters.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# noocleus-one\n\n\"noocleus-one\" is meant to be a minimal example for a functional programming language where type inference meets algebraic data types and pattern matching. \n\nI've developed this project to understand the mechanics of type inference in the presence of algebraic data types, pattern matching and recursive let-expressions. The \"one\" of \"noocleus-one\" stands for \"rank-1 polymormphism\" since I intend to apply the ideas of this project to higher-rank polymorphism. The name \"noocleus\" itself is a pun on my own language called \"noo\" (which is still under construction), since this project is somewhat of a small kernel for \"noo\".\n\n## Index\n\n1. [Terms](#1-Terms)\n    1. [Extended Backus-Naur-Form](#11-Extended-Backus-Naur-Form)\n    2. [Examples](#12-Examples)\n        1. [Booleans](#121-Booleans)\n        2. [Natural Numbers](#122-Natural-Numbers)\n        3. [Options](#123-Options)\n        4. [Lists](#124-Lists)\n        5. [Eithers](#125-Eithers)\n2. [Types](#2-Types)\n    1. [Sigma and Tau Types](#21-Sigma-and-Tau-Types)\n        1. [Sigma Types](#211-Sigma-Types)\n        2. [Tau Types](#212-Tau-Types)\n3. [References](#3-References)\n\n### 1. Terms\n\n#### 1.1 Extended Backus-Naur-Form\n```\nTerm                ::= Let | Data | Abstraction ;\n\nLet                 ::= \"let\", Annotation, \"=\", Term, \"in\", Term ;\n\nData                ::= \"data\", TypeConstant, { TypeVariable }, [ \"=\", Constructors ], \"in\", Term ;\nConstructors        ::= Constructor, { \"|\", Constructor } ;\nConstructor         ::= UpperCaseIdentifier, { SimpleType } ;\n\nAbstraction         ::= Match, [ \"=\u003e\", Term ] ;\n\nMatch               ::= Annotation, [ \"match\", \"{\", { Case }, \"}\" ] ;\nCase                ::= \"case\", Annotation, \"=\u003e\", Term ;\n\nAnnotation          ::= Application, [ \":\", Tau ] ;\n\nApplication         ::= SimpleTerm, { SimpleTerm } ;\n\nSimpleTerm          ::= Natural | Tuple | Variable ;\n\nNatural             ::= \"[0-9]+\" ;\n\nTuple               ::= \"(\", [ Term, { \",\", Term } ], \")\" ;\n\nVariable            ::= Identifier ;\n\nTau                 ::= FunctionType ;\n\nFunctionType        ::= TypeApplication, [ \"-\u003e\", Tau ] ;\n\nTypeApplication     ::= SimpleType, { SimpleType } ;\n\nSimpleType          ::= TupleType | TypeConstant | TypeVariable ;\n\nTupleType           ::= \"(\", [ Tau, { \",\", Tau } ], \")\" ;\n\nTypeConstant        ::= UpperCaseIdentifier ;\n\nTypeVariable        ::= Identifier ;\n\n(* note: an upper case identifier may not match any terminal-symbol of this grammar *)\nUpperCaseIdentifier ::= \"[A-Z][a-zA-Z0-9]*'*\" ;\n\n(* note: an identifier may not match any terminal-symbol of this grammar *)\nIdentifier          ::= \"[a-zA-Z][a-zA-Z0-9]*'*\" | Symbol, { Symbol } ;\n\nSymbol              ::= \"+\" | \"~\" | \"*\" | \"#\" | \"-\" | \".\" | \":\" \n                      | \",\" | \";\" | \"\u003c\" | \"\u003e\" | \"|\" | \"@\" | \"^\" \n                      | \"!\" | \"$\" | \"%\" | \"\u0026\" | \"/\" | \"?\" ; \n```\n\n#### 1.2 Examples\n\n##### 1.2.1 Booleans\n```\ndata Boolean = True | False in\n\nlet if = test =\u003e then =\u003e else =\u003e test match {\n  case True  =\u003e then\n  case False =\u003e else\n} in\n\nlet negate = boolean =\u003e if boolean False True in\n\nnegate True\n```\n\n##### 1.2.2 Natural Numbers\n```\ndata Nat = S Nat | Z in\n\nlet + = a =\u003e b =\u003e a match {\n  case Z   =\u003e b\n  case S n =\u003e + n (S b)\n} in\n\nlet - = a =\u003e b =\u003e (a, b) match {\n  case (a, Z)     =\u003e a\n  case (Z, b)     =\u003e Z\n  case (S a, S b) =\u003e - a b\n} in\n\nlet * = a =\u003e b =\u003e a match {\n  case S Z =\u003e b\n  case Z   =\u003e Z\n  case S n =\u003e + (* n b) b\n} in\n\nlet \u003e = a =\u003e b =\u003e (a, b) match {\n  case (Z, Z)     =\u003e False\n  case (a, Z)     =\u003e True\n  case (S a, S b) =\u003e \u003e a b\n} in\n\nlet factorial = n =\u003e\n  if (\u003e n Z)\n    (* (factorial (- n (S Z))) n)\n    (S Z)\nin \n\nfactorial 5\n```\n\n##### 1.2.3 Options\n```\ndata Option a = Some a | None in\n\nlet map = f =\u003e option =\u003e option match {\n  case Some value =\u003e Some (f value)\n  case None       =\u003e None\n} in\n\nlet flatMap = f =\u003e option =\u003e option match {\n  case Some value =\u003e f value\n  case None       =\u003e None\n} in\n\nflatMap (n =\u003e map (n =\u003e + n n) (Some n)) (Some 10)\n```\n\n##### 1.2.4 Lists\n```\ndata List a = Cons a (List a) | Nil in\n\nlet reverse = xs =\u003e \n  let helper = xs =\u003e acc =\u003e xs match {\n    case Cons head tail =\u003e helper tail (Cons head acc)\n    case Nil            =\u003e acc\n  } in helper xs Nil\nin\n\nlet fold = z =\u003e f =\u003e xs =\u003e xs match {\n  case Cons head tail =\u003e fold (f z head) f tail\n  case Nil            =\u003e z\n} in\n\nfold 0 + (Cons 0 (Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 Nil))))))\n```\n\n##### 1.2.5 Eithers\n```\ndata Either a b = Left a | Right b in\n\nlet either = f =\u003e g =\u003e e =\u003e e match {\n  case Left a  =\u003e f a\n  case Right b =\u003e g b\n} in\n\neither (+ 1) (- 1) (Right 1)\n```\n\n### 2. Types\n\nThe type inference used in this project is that of [Hindley-Milner](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system) (sometimes also called Damas-Milner). That means types can be completely (i.e. without user supplied hints) inferred. The algorithm itself is somewhat of a combination of the [algorithm J](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Algorithm_J) and the one of [S. P. Jones, et al.](#3-References) for arbitrary-rank types. I have extended it by algebraic data types and pattern matching on my own, meaning that a formal proof for the correctness of those features is missing.\n\n#### 2.1 Sigma and Tau Types\n\nThe types of the underlying type system are divided into monomorphic (also called tau types) and polymorphic (also called sigma types) types. \n\n##### 2.1.1 Sigma Types\n\nA sigma (or polymorphic) type is quantified by an universal quantifier. For example, `forall x . x -\u003e x` is the type of the polymorphic identity function. The implemented algorithm always yields a sigma type as the result of the type inference.\n\n##### 2.1.2 Tau Types\n\nA tau (or monomorphic) type is one without an universal quantifier. For example, `Nat` is the (monomorphic) type of a natural number. A tau type may also be interpreted as a sigma type, i.e. `Nat` may also be interpreted as `forall . Nat`. In this case we quantify over the type `Nat` without specifying type variables at all.\n\nIn case of an annotation (e.g. `let f: a -\u003e a = x =\u003e x in f`) free type variables (in the preceding example only `a`) are assumed to be quantified by a top-level `forall`.\n\nWhile type checking the algorithm switches between sigma and tau types (depending on the scope) by instantiating and generalizing them. For further explanation consider the previous example `let f: a -\u003e a = x =\u003e x in f`. While checking the equation (`f: a -\u003e a = x =\u003e x`) we talk about `a -\u003e a` as a monomorphic (tau) type, because we do not know anything about `x` on the right hand side, besides that it is of type `a`. When checking the body of the `let`-expression (i.e. the part to the right of `in`) we talk about `a -\u003e a` as a polymorphic (sigma) type (as already mentioned, we assume that the free type variables are quantified by a `forall`), since we can call `f` for any value of any type.\n\n#### 2.2 Limitations\n\nThere are two main limitations of the implemented algorithm: On the one hand, I have introduced recursive `let`-expressions which leads to a type system whose logic is not consistent. On the other hand, the type system disallows universal quantifiers at arbitrary positions.\n\n##### 2.2.1 Consistency\n\nWhen interpreting the types of the presented type system as logical formulas (see [Curry-Howard Isomorphism](https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence)) we can deduce the absurdity from something that is true (exploiting recursion).\n\nConsider the following definitions for bottom (absurdity) and top:\n```\ndata False    in\ndata True = T in\n...\n```\n\nSince we always want to be able to deduce `True` there is exactly one constructor (namely `T`) that takes no arguments. The other way round, we never want to deduce the absurdity. For this reason there is no constructor for `False` at all. Unfortunately we can still deduce `False` when we introduce recursion:\n\n```\ndata False    in\ndata True = T in\n\nlet f: True -\u003e False in n =\u003e f n in\n\n(f T): False\n```\n\nThe program above is well-typed in the sense of the implemented algorithm, i.e. the type checker will not complain about anything. The result of the program is a value of type `False`. In terms of the Curry-Howard isomorphism, we have found a proof for the absurdity.\n\n##### 2.2.2 Rank-N Polymorphism\n\nAs mentioned before, the \"one\" of \"noocleus-one\" stands for rank-1 polymorphism. An intentional limitation of this project is that it is not possible to type programs like the following:\n\n```\nlet id = x =\u003e x in \nlet map = f =\u003e (f 1, f ()) in map id\n```\n\nIn a more powerful type system the program above would lead to a type, where `f` is polymorphic in the body of the abstraction `f =\u003e (f 1, f ())`, i.e. `map: (forall x . x -\u003e x) -\u003e (Nat, ())`. In the present project, this program is ill-typed: The type checker would complain with an error message like \"type mismatch between 'Nat' and '()'\", since it assumes that `f` is monormphic (i.e. either accepts `Nat` or `()` but not both).\n\n#### 3. References\n\n1. Pratical type inference for arbitrary-rank types, S. P. Jones, et al., 31.07.2007\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbjoernloetters%2Fnoocleus-one","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbjoernloetters%2Fnoocleus-one","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbjoernloetters%2Fnoocleus-one/lists"}