This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Lean 75.7%
Rust 15.1%
C 3.6%
Nix 1.5%
Nickel 0.1%
Other 4.0%
5 2 0

Clone this repository

https://git.vm.fail/nandi.uk/mlang https://git.vm.fail/did:plc:l3oztk4bt36dj6gonyqeeza4
ssh://git@knot1.tangled.sh:2222/nandi.uk/mlang ssh://git@knot1.tangled.sh:2222/did:plc:l3oztk4bt36dj6gonyqeeza4

For self-hosted knots, clone URLs may differ based on your setup.


README.md

mlang#

A small functional language prototype written in Lean 4.

Current scope:

  • integer and boolean literals
  • variables
  • let
  • if
  • typed lambdas
  • function application
  • integer arithmetic
  • integer division
  • strings
  • native parsed data values
  • lexical closures
  • hand-written lexer and parser
  • static type checker
  • implicit effect tracking
  • builtin file IO
  • builtin Nickel evaluation

The interpreter lives in Mlang/Interpreter.lean. The parser lives in Mlang/Parser.lean.

Run#

With devenv installed:

devenv shell
build
run

You can also run commands directly without entering the shell:

devenv test
devenv shell -- build
devenv shell -- run

The executable supports a REPL and file execution:

devenv shell -- lake exe mlang
devenv shell -- lake exe mlang --eval "1 + 2"
devenv shell -- lake exe mlang examples/samples.mlg

Current syntax:

expr ::= let name = expr in expr
       | if expr then expr else expr
       | try expr with name => expr
       | pmap name in expr => expr
       | -> expr ident*
       | fun (name : type) => expr
       | expr = expr
       | expr + expr
       | expr - expr
       | expr * expr
       | expr expr
       | [expr, ...]
       | { key = expr, ... }
       | null
       | (expr)
       | true | false | 123 | "text" | name

program ::= expr (';' expr)*

type ::= Int | Bool | String | Data | List | Result | Error | type -> type | (type)

Inside the REPL, there is also a top-level binding form:

repl ::= let name = expr

Bare REPL bindings persist across later entries, so you can write:

let x = 42
x + 1

Expressions are checked as Type ! Effects. Pure expressions get {}; division carries {Error} implicitly. readFile is a builtin with type String -> String ! {IO, Error}. readFileNickel is a builtin with type String -> Data ! {IO, Error}. parseNickel is a builtin with type String -> Data ! {Error} and is backed by the real Nickel Rust crate. Nickel source is evaluated on the host side and converted back into mlang Data. Parsed data inspection is available through:

  • httpGet : String -> String ! {IO, Error}

  • parseJson : String -> Data ! {Error}

  • get : Data -> String -> Data ! {Error}

  • at : Data -> Int -> Data ! {Error}

  • asInt : Data -> Int ! {Error}

  • asString : Data -> String ! {Error}

  • asBool : Data -> Bool ! {Error}

  • toJson : Data -> String

  • toNickel : Data -> String

httpGet is implemented through a thin Rust host library plus a small C shim for Lean FFI. The Rust layer uses a real HTTP client crate rather than spawning an external tool. try ... with name => ... handles Error, binds the caught error as an Error value, and removes Error from the resulting effect set when recovered locally.

The host-side Nickel bridge currently maps evaluated Nickel values back into mlang data for:

  • null
  • booleans
  • integers
  • strings
  • arrays
  • records

Non-integer numeric Nickel values are not supported yet by mlang's Data model.

Native Data literals are also supported directly in mlang:

{ answer = 42, flags = [true, false], note = "ok", empty = null }

Fields and array elements may be Int, Bool, String, or existing Data expressions.

pmap name in expr => body expects expr to evaluate to a native Data array. It evaluates body concurrently for each element bound to name, preserves input order, and returns a native List of native Result values instead of failing the whole traversal.

There is also a thread-first form that desugars statically to nested application:

-> "examples/sample.ncl" readFileNickel (get "answer")

Parallel collection mapping example:

pmap x in parseNickel "[{ answer = 1 }, { missing = 2 }, { answer = 3 }]"
  => get x "answer"

Files and REPL entries may contain multiple expressions separated by ;. Each expression is parsed, typechecked, evaluated, and printed in order. REPL-specific bare let bindings persist across later entries. devenv shell -- run now launches a Rust rustyline frontend for the REPL, while the Lean binary still handles evaluation. Use :quit to exit the REPL.

The checked-in sample program is examples/samples.mlg, which now contains the old demo coverage as semicolon-separated expressions, including file IO, Nickel parsing, threading, pmap, local error handling, and an HTTP fetch. It reads from examples/sample.ncl.

A separate HTTP example is examples/http.mlg.

Next steps#

  • add recursive functions
  • add algebraic data types and pattern matching
  • compile to a bytecode VM or another backend