{"id":13787726,"url":"https://github.com/elpinal/bright-ml","last_synced_at":"2026-01-12T12:51:18.890Z","repository":{"id":143007770,"uuid":"242469582","full_name":"elpinal/bright-ml","owner":"elpinal","description":"A statically-typed programming language based on \"F-ing modules\"","archived":false,"fork":false,"pushed_at":"2020-07-25T12:01:39.000Z","size":223,"stargazers_count":80,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-18T01:39:12.450Z","etag":null,"topics":["f-ing-modules"],"latest_commit_sha":null,"homepage":null,"language":"Standard ML","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/elpinal.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":"2020-02-23T06:38:56.000Z","updated_at":"2024-10-06T15:02:15.000Z","dependencies_parsed_at":"2023-08-30T20:31:12.264Z","dependency_job_id":null,"html_url":"https://github.com/elpinal/bright-ml","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elpinal%2Fbright-ml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elpinal%2Fbright-ml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elpinal%2Fbright-ml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elpinal%2Fbright-ml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elpinal","download_url":"https://codeload.github.com/elpinal/bright-ml/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253659370,"owners_count":21943627,"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":["f-ing-modules"],"created_at":"2024-08-03T21:00:28.943Z","updated_at":"2026-01-12T12:51:18.862Z","avatar_url":"https://github.com/elpinal.png","language":"Standard ML","readme":"# Bright ML\n\nBright ML is a statically-typed programming language, based on \"F-ing modules\".\nThis repository contains an interpreter of Bright ML, written in Moscow ML.\n\n## Features\n  - Type inference\n  - Mutually-recursive datatypes\n  - Mutually-recursive functions\n  - \"F-ing modules\"-based module system\n  - First-class packaged modules\n  - Destructive substitution\n  - No value restriction\n  - Bottom type (empty datatypes)\n  - Exhaustivity and redundancy check of patterns\n  - Binding operators\n\n## Getting started\n\nYou need the [Moscow ML](https://mosml.org) compiler and [cmtool](http://www.cs.cmu.edu/~crary/cmtool/).\nMake sure that `mosmlc`, `cmlex` and `cmyacc` are in your `$PATH`.\n\n```bash\n$ git clone --recursive https://github.com/elpinal/bright-ml\n$ cd bright-ml\n$ make\n\n$ ./bright-ml -h\n```\n\n## Overview\n\n```\n// This is a comment.\n\n// Bind `x` to 1.\nval x = 1\n\n// Bind `f` to a function.\nval f x y = (x + y, x - y)\n\n// Bind `fact` to a recursive function.\nval rec fact n =\n  if 0 \u003c n\n  then n * fact (n - 1)\n  else 1\n\n// Type synonym. We can use `t` and `int` interchangeably.\ntype t = int\n\n// Datatype definition.\ndatatype t = A of t\n\n// Signature binding.\nsignature S = sig\n  type t\nend\n\n// Module binding.\nmodule M = struct\n  val x = [1, 2, 3]\nend\n\n// Functor binding.\nmodule F (X : S) = struct\n  type t = X.t\n  val v x = print_endline x\nend\n\n// Include a module.\ninclude M\n\n// Open a module.\nopen M\n\n// Like Haskell's `$`, `$` can be used to omit parentheses for function applications.\nval _ = succ $ succ n   // Syntactically the same as `succ (succ n)`.\n\n// We can use an alternative syntax `{ ... }` to write\n// structures (`struct ... end`) or structure signatures (`sig ... end`).\nmodule M : {type t type u = t val x : t * u} = {type u = int type t = u val x = (5, 4)}\n```\n\n### Mutually-recursive definitions\n\nThe following example is adopted from [Nakata and Garrigue 2006](http://www.math.nagoya-u.ac.jp/~garrigue/papers/nakata-icfp2006.pdf).\n\n```\n// Mutually-recursive datatypes.\ndatatype tree =\n  | Leaf of int\n  | Node of int * forest\n\nand forest =\n  | Forest of list tree\n\n// Mutually-recursive functions.\nval rec max t = match t with\n  | Leaf n     -\u003e n\n  | Node(n, f) -\u003e Int.max n $ max_forest f\nend\n\nand max_forest f = match f with\n  | Forest []       -\u003e 0\n  | Forest(t :: ts) -\u003e Int.max (max t) $ max_forest $ Forest ts\nend\n```\n\n### F-ing modules\n\nBright ML's module system is almost the same as [F-ing modules](https://people.mpi-sws.org/~rossberg/f-ing/f-ing-jfp.pdf) with first-class packaged modules, without applicative functors.\n\n```\nsignature S = sig\n  type t\n  val f 'a : 'a -\u003e 'a\nend\n\n// Higher-order (generative) functor.\nmodule F (X : (Y : S) -\u003e S) = struct\n  val v =\n    let module M = X struct\n      type t = bool\n      val f x = x\n    end\n    in M.f \"a\"\nend\n\n// Signature refinement: `where type`.\nsignature T = S where type t = int\n\n// `let` can be used inside types.\ntype t =\n  let signature S = sig\n    type t = int\n    val x : t -\u003e t\n  end in\n  pack S\n```\n\nBright ML's `open` is very general, like [OCaml's generalized `open`](https://www.cl.cam.ac.uk/~jdy22/papers/extending-ocamls-open-draft.pdf).\n\n```\n// We can open a module identifier.\nopen List\n\n// We can open the resulting structure of functor application.\nopen F M\n\ninclude struct\n  // The scope of `t` and `C` is the rest of the surrounding module.\n  open struct\n    datatype t = C of int\n  end\n\n  val x = C 8\n  val y = x\n  val f = function\n    | C n -\u003e C $ n + 1\n  end\nend\n\n// We cannot access `C`.\n// val _ = C\n\nval _ = f $ f y\n```\n\nThe following ascribed signature is expressible only via generalized `open` or destructive substitution.\n\n```\nmodule M : sig\n  type t\n\n  // This is important.\n  open struct\n    type t' = t\n  end\n\n  module N : sig\n    type t\n\n    val f : t' -\u003e t\n  end\nend = struct\n  datatype t = A1\n\n  module N = struct\n    datatype t = A2\n\n    val f = function\n      | A1 -\u003e A2\n    end\n  end\nend\n```\n\n### Destructive substitution\n\nLike OCaml, Bright ML supports [destructive substitution](http://www.math.nagoya-u.ac.jp/~garrigue/papers/ml2010.pdf).\nThe syntax is `where type \u003cqualified-identifier\u003e := \u003ctype\u003e`.\nIn the following example, `Show` and `Eq` both have a type component `t`.\nUsing destructive substitution, we can combine these signatures into one.\n\n```\nsignature Show = sig\n  type t\n\n  val show : t -\u003e string\nend\n\nsignature Eq = sig\n  type t\n\n  val `==` : t -\u003e t -\u003e bool\nend\n\nsignature Int = sig\n  type t\n\n  include Show where type t := t\n  include Eq where type t := t\nend\n```\n\n### Explicit universal quantification\n\nIn Bright ML, all type variables must be bound explicitly,\nnot only because Standard ML's implicit scoping rules are brittle,\nbut also because Bright ML is much similar to System Fω, rather than Hindley–Milner type system,\nthanks to the liberal and expressive module system.\n(Note that the core language itself is Hindley–Milner type system.)\n\n```\nsignature S = sig\n  type t 'a\n\n  // Type variables `'a` and '`b` must be quantified explicitly.\n  val map 'a 'b : ('a -\u003e 'b) -\u003e t 'a -\u003e t 'b\nend\n```\n\n```\n// Implicit polymorphism is one of the virtues of ML.\n// This function has type `∀α. α -\u003e α`.\nval id x = x\n// If we want to annotate `x` with a type which contains type variables,\n// we need to explicitly bind them.\nval id 'a (x : 'a) = x\n\n// This form is ill-typed because `'a` is an unbound type variable.\n// val id (x : 'a) = x\n```\n\nBright ML's explicit scoping of type variables dispenses with GHC's \"scoped type variables\" or OCaml's \"locally abstract types\".\n\n```\nval f 'a (x : 'a) (size : 'a -\u003e int) =\n  let val g (y : 'a) = size y in\n  g x\n```\n\n### First-class packaged modules\n\nAny modules can be turned into first-class values.\nAn expression of the form `pack M : S` is a first-class value encapsulating a module expression `M`\nwhich matches a signature expression `S`.\nSuch an expression, or a package, has type `pack S`.\n\nUnpacking of packages is done via a construct `unpack E : S`,\nwhich project out a module of signature `S` inside an expression `E`.\n\n```\nmodule List : sig\n  val fold 'a : pack (Monoid.S where type t = 'a) -\u003e list 'a -\u003e 'a\nend = struct\n  val fold 'a m xs =\n    let module M = unpack m : Monoid.S where type t = 'a in\n    fold_left M.`\u003c\u003e` M.empty xs\nend\n```\n\n### No value restriction\n\nBright ML is an impure language.\nHowever, unlike Standard ML or OCaml, it doesn't have *polymorphic* mutable references or something alike,\nthus in Bright ML, any expression can be generalized at `val`-bindings.\n\n```\nmodule M : sig\n  val id 'a : 'a -\u003e 'a\n  val v 'a : list ('a -\u003e 'a)\nend = struct\n  val id x = x\n\n  // This expression can be given a polymorphic type while it is not a syntactic value.\n  val v = id [fun x -\u003e x]\nend\n```\n\nThat said, we sometimes want mutable references, so Bright ML supports *monomorphic* mutable references.\nMore precisely, we can generate monomorphic mutable reference **types** at any given types using a generative functor `Ref.Make`\nin the standard library.\nWe can use mutable references without compromising type soundness,\nwhile keeping every expression polymorphic.\n\n```\nmodule IntListRef = Ref.Make {type t = list int}\nval r = IntListRef.make [] // This expression has abstract type `IntListRef.t`.\nval xs = 1 :: IntListRef.get r\nval _ = IntListRef.set r $ List.map (fun n -\u003e n * 2) xs\nval _ = IntListRef.get r // This expression has type `list int`.\n```\n\nOne drawback of this approach is that we cannot write recursive datatypes in terms of their references.\nFor example, the following, valid Standard ML code cannot be written in Bright ML.\n\n```sml\ndatatype t =\n    A\n  | B of t ref\n```\n\n### Empty datatypes and exhaustivity\n\nEmpty datatypes can be used to indicate that values of such types never appear at run-time.\n\n```\ndatatype result 'e 'a =\n  | Err of 'e\n  | Ok of 'a\n\nsignature FromStr = sig\n  type t\n  type err\n\n  val from_str : string -\u003e result err t\nend\n\n// `bottom` is an empty datatype.\ndatatype bottom = |\n\n// The type of `X.from_str` means it never fails.\nmodule F (X : FromStr where type err := bottom) = struct\n  val _ =\n    // This pattern matching is exhaustive since the `bottom` type cannot be inhabited.\n    match X.from_str \"hello\" with\n      | Ok v -\u003e v\n    end\nend\n```\n\nAlso, empty datatypes can be used as phantom types.\n\n```\nmodule M :\u003e sig\n  type t 'a\n\n  datatype int' = |\n  datatype bool' = |\n\n  val int : int -\u003e t int'\n  val bool : bool -\u003e t bool'\n  val succ : t int' -\u003e t int'\nend = struct\n  datatype int' = |\n  datatype bool' = |\n\n  datatype t 'a =\n    | Int of int\n    | Bool of bool\n    | Succ of t int'\n\n  val int = Int\n  val bool = Bool\n  val succ = Succ\nend\n\nopen struct\n  open M\n\n  val x = int 4\n  val y = bool true\n\n  val z = succ x\n  // We cannot apply `succ` to `y` because `y` has type `M.t bool'`.\n  // val z = succ y\nend\n```\n\n### Binding operators\n\nBright ML supports binding operators [like OCaml](https://caml.inria.fr/pub/docs/manual-ocaml-4.10/bindingops.html),\nmaking monadic operations handy.\n\n```\n// Define `val+` as a binding operator.\nval `val+` x f =\n  match x with\n    | None   -\u003e None\n    | Some x -\u003e f x\n  end\n\ninclude struct\n  val f a b =\n    // `a` has type `option int` while `x` has type `int`.\n    val+ x = a in\n    val+ y = b in\n    Some $ x * y\nend : sig\n  val f : option int -\u003e option int -\u003e option int\nend\n```\n\n## Test\n\nTo run test, [`go`](https://golang.org) is required.\n\n```bash\n$ make test\n```\n","funding_links":[],"categories":["Functional"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felpinal%2Fbright-ml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felpinal%2Fbright-ml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felpinal%2Fbright-ml/lists"}