A quick-reference guide for AI systems learning BechML - a friendly, higher-kinded, modular, functional scripting language.
BechML is a statically-typed functional programming language with:
- Higher-kinded types
- Algebraic data types (sum and product types)
- Pattern matching
- Module system
- Type classes (Functor, Applicative, Monad, Monoid)
- Lambda expressions
- Polymorphic/generic functions
All top-level declarations end with semicolons:
name : Type := expression;
-- Single line comment (Haskell-style, inferred from language family)
Types are declared with : before the assignment :=:
functionName : TypeSignature := implementation;
| Type | Description |
|---|---|
int |
Integer numbers |
bool |
Boolean values |
string |
Text strings |
() |
Unit type (void) |
| Syntax | Meaning |
|---|---|
[a] |
List of type a |
a -> b |
Function from a to b |
t a |
Type t applied to type a |
t a b |
Type t applied to types a and b |
Type variables are declared in angle brackets before the type signature:
-- Single type variable
id : <a> a -> a := \a -> a;
-- Multiple type variables
const : <a b> a -> b -> a := \a _ -> a;
-- In function types
map : <a b> (a -> b) -> [a] -> [b] := #list_map;
Lambdas use backslash \ syntax:
-- Single parameter
\x -> x
-- Multiple parameters
\a b -> expression
-- With ignored parameter (underscore)
\_ -> value
-- Nested/curried
\f -> \x -> f x
id : <a> a -> a := \a -> a;
const : <a b> a -> b -> a := \a _ -> a;
add : int -> int -> int := \x y -> x + y;
t := type <a b> { fst : a, snd : b }
make : <a b> a -> b -> t a b := \a b -> { fst : a, snd : b };
p.fst -- Access 'fst' field of record p
p.snd -- Access 'snd' field of record p
t := type <a b> { fst : a, snd : b }
make : <a b> a -> b -> t a b := \a b -> { fst : a, snd : b };
swap : <a b> t a b -> t b a := \p -> { fst : p.snd, snd : p.fst };
mapFst : <a b c> (a -> c) -> t a b -> t c b := \f p -> { fst : f p.fst, snd : p.snd };
t := type {
| ConstructorA : <a> TypeSignature
| ConstructorB : <a> TypeSignature
}
t := type {
| None : <a> t a
| Some : <a> a -> t a
}
t := type {
| Left : <a b> a -> t a b
| Right : <a b> b -> t a b
}
Use match expression with braces and commas:
match value {
Pattern1 -> result1,
Pattern2 -> result2,
_ -> defaultResult
}
| Pattern | Matches |
|---|---|
ConstructorName |
Nullary constructor |
ConstructorName x |
Constructor with binding |
ConstructorName x y |
Constructor with multiple bindings |
_ |
Wildcard (matches anything) |
-- Maybe elimination
maybe : <a b> b -> (a -> b) -> t a -> b := \def f m ->
match m {
None -> def,
Some x -> f x
};
-- Either elimination
either : <a b c> (a -> c) -> (b -> c) -> t a b -> c := \f g e ->
match e {
Left x -> f x,
Right y -> g y
};
ModuleName := module; -- Forward declaration
ModuleName := module { ... }; -- With body
use Super.*; -- Import all from parent scope
use Core.*; -- Import all from Core module
use ModuleName; -- Import specific module
Module.member
Module.Type.constructor
Functor := module {
t := type <f> {
map : <a b> (a -> b) -> f a -> f b
}
void : <f a> t f -> f a -> f () := \f fa -> f.map (\_ -> ()) fa
}
BechML uses record types to represent type class dictionaries:
Functor.t := type <f> {
map : <a b> (a -> b) -> f a -> f b
}
Applicative.t := type <f> {
pure : <a> a -> f a,
apply : <a b> f (a -> b) -> f a -> f b
}
Monad.t := type <m> {
bind : <a b> m a -> (a -> m b) -> m b
}
Monoid.t := type <a> {
empty : a,
append : a -> a -> a
}
functor : Functor.t [] := {
map : #list_map
};
monad : Monad.t t := {
bind : \a f -> f a.run
};
Primitives are prefixed with #:
eq : int -> int -> bool := #int_eq;
lt : int -> int -> bool := #int_lt;
gt : int -> int -> bool := #int_gt;
leq : int -> int -> bool := #int_leq;
geq : int -> int -> bool := #int_geq;
add : int -> int -> int := #int_add;
sub : int -> int -> int := #int_sub;
mul : int -> int -> int := #int_mul;
div : int -> int -> int := #int_div;
mod : int -> int -> int := #int_mod;
cons : <a> a -> [a] -> [a] := #list_cons;
append : <a> [a] -> [a] -> [a] := #list_append;
length : <a> [a] -> int := #list_length;
map : <a b> (a -> b) -> [a] -> [b] := #list_map;
print : string -> io () := #print;
#io_map, #io_pure, #io_apply, #io_bind
if condition then trueExpr else falseExpr
replicate : <f a> t f -> int -> f a -> f [a] := \ap n fa ->
if Int.leq n 0
then ap.pure []
else ap.apply (ap.apply (ap.pure (\x xs -> List.cons x xs)) fa) (replicate ap (Int.sub n 1) fa);
Int.add x y -- x + y
Int.sub x y -- x - y
Int.mul x y -- x * y
Int.div x y -- x / y
Int.mod x y -- x % y
Int.eq x y -- x == y
Int.lt x y -- x < y
Int.gt x y -- x > y
Int.leq x y -- x <= y
Int.geq x y -- x >= y
id : <a> a -> a := \a -> a;
const : <a b> a -> b -> a := \a _ -> a;
compose : <a b c> (b -> c) -> (a -> b) -> a -> c := \f g x -> f (g x);
functor : <s> Functor.t (t s) := {
map : \f i -> { runUnsafe : (f (runUnsafe i)) }
};
StateT := module {
use Super.*;
use Core.*;
t := type <s m a> { run : s -> m (Pair.t a s) }
functor : <s m> Functor.t m -> Functor.t (t s m) := \fm -> {
map : \f sa -> { run : \s -> fm.map (Pair.mapFst f) (sa.run s) }
};
}
- Files use
.bmlextension - One module per file (typically)
- Module name matches file name
Uses roux.json for package management (Roux package manager)
| Concept | Syntax |
|---|---|
| Type annotation | name : Type |
| Assignment | := |
| Lambda | \x -> expr |
| Generic params | <a b> |
| Function type | a -> b |
| List type | [a] |
| Record type | { field : Type } |
| Record literal | { field : value } |
| Field access | record.field |
| Sum type constructor | | Name : Type |
| Pattern match | match v { P -> e } |
| Wildcard | _ |
| Module | module { ... } |
| Import | use Module.*; |
| Primitive | #primitive_name |
| Conditional | if c then t else f |
| Statement terminator | ; |
| Module | Purpose |
|---|---|
Int |
Integer operations |
List |
List operations |
Maybe |
Optional values (None/Some) |
Either |
Sum type (Left/Right) |
Pair |
Tuples/product types |
IO |
Input/output effects |
Identity |
Identity functor/monad |
Reader |
Reader monad |
State |
State monad |
Writer |
Writer monad |
Functor |
Functor type class |
Applicative |
Applicative type class |
Monad |
Monad type class |
Monoid |
Monoid type class |
MonadTrans |
Monad transformer class |
- Website: https://bechml.github.io
- GitHub: https://github.com/bechml
- Core Library: https://github.com/bechml/core
- Package Manager: Roux (https://github.com/bechml/roux)
- VSCode Extension: https://github.com/bechml/vscode-bechml
BechML currently does not support input operations. The language only provides output capability through the print primitive:
print : string -> io () := #print;
There are no built-in primitives for:
- Reading from stdin (
getLine,readLine) - User input prompts
- File reading operations
The IO module in the Core library defines monadic operations (#io_pure, #io_bind, #io_map, #io_apply) for sequencing IO effects, but only print is available for actual IO operations.
This limitation means BechML programs cannot:
- Accept interactive user input
- Read from files
- Implement REPL-style interactions
For interactive applications, BechML would require extensions to the runtime and new primitives to be added to the compiler.