Cure is a strongly-typed, dependently-typed functional programming language for the BEAM virtual machine. It uniquely combines advanced type system features with native finite state machine support and seamless BEAM ecosystem integration.
# Primitive types
Int # Arbitrary precision integers
Float # Double precision floats
Atom # Interned symbols (like Elixir atoms)
Bool # true | false
String # UTF-8 strings
Binary # Byte sequences
Unit # Unit type for functions with no meaningful return
# Dependent types
Nat # Natural numbers (Int >= 0)
Pos # Positive integers (Int > 0)
Vector(T, n: Nat) # Fixed-length vector
List(T, n: Nat) # List with known length
Range(min: Int, max: Int) # Integer range type
# Simple function
def add(x: Int, y: Int): Int = x + y
# Function with dependent types
def replicate(n: Nat, x: T): List(T, n) =
match n == 0 do
true -> []
false -> [x | replicate(n-1, x)]
end
# Pattern matching function
def length(list: List(T)): Nat =
match list do
[] -> 0
[_|tail] -> 1 + length(tail)
end
# Function with constraints
def safe_divide(x: Int, y: Int): Int when y != 0 = x / y
# Function with Unit return type
def print_message(msg: String): Int =
println("Message: " <> msg)
0
process keyword are a planned feature. Currently, use Erlang's spawn via FFI or standard library functions for process management.
# Process spawning (current approach)
# Use Erlang spawn via curify FFI bindings
curify spawn_process(func: Atom, args: List(Any)): Pid = {erlang, spawn, 2}
# Or use standard library FSM for state management
# See FSM section for state machine-based processes
FSMs use arrow-based transition syntax with record-based payloads:
# FSM definition with payload record
record TcpPayload do
buffer: Binary
seq_num: Int
ack_num: Int
end
# FSM with transitions
fsm TcpPayload{buffer: <<>>, seq_num: 0, ack_num: 0} do
# Arrow-based transitions: State --> |event| NextState
Closed --> |listen| Listen
Closed --> |connect| SynSent
Listen --> |syn_received| SynReceived
Listen --> |close| Closed
SynSent --> |syn_ack_received| Established
SynSent --> |syn_received| SynReceived
SynSent --> |close| Closed
Established --> |fin_received| CloseWait
Established --> |close| FinWait1
Established --> |data| Established # Self-transition for data
end
# Using FSM
import Std.Fsm [fsm_spawn/2, fsm_cast/2, fsm_advertise/2, fsm_state/1]
import Std.Pair [pair/2]
let initial_data = TcpPayload{buffer: <<>>, seq_num: 0, ack_num: 0}
let conn = fsm_spawn(:TcpPayload, initial_data)
let _ = fsm_advertise(conn, :tcp_conn)
# Send events
let event = pair(:listen, [])
let _ = fsm_cast(:tcp_conn, event)
# Query state
let current_state = fsm_state(:tcp_conn) # Returns :Listen
# Module definition
module Math do
# Export declarations
export [add/2, multiply/2, factorial/1]
def add(x: Int, y: Int): Int = x + y
def multiply(x: Int, y: Int): Int = x * y
def factorial(n: Nat): Pos =
match n == 0 do
true -> 1
false -> n * factorial(n - 1)
end
# Helper function
def helper_func(x) = x * 2
end
# 🚀 WORKING Import System!
import Math
import Std [List, Result] # Standard library import
import List [map/2, filter/2] # Selective imports with arity
# Usage - all work correctly!
let result = add(5, 3) # Imported function
let doubled = map([1,2,3], fn(x) -> x * 2 end) # From Std
println("Result: " <> show(result)) # println/1 and show/1 from Std
Cure includes a complete, runtime-verified standard library with essential functions:
# ✅ VERIFIED: The Std module provides working functions
# ✅ Output functions (runtime verified)
print/1 # Print values to console with proper formatting
show/1 # Convert values to string representation (atoms, numbers, lists, tuples)
# ✅ List operations (runtime verified in dependent_types_simple.cure)
map/2 # Transform list elements: map([1,2,3], fn(x) -> x*2 end)
fold/3 # Reduce list with accumulator: fold([1,2,3], 0, fn(x,acc) -> acc+x end)
zip_with/3 # Combine two lists: zip_with([1,2], [3,4], fn(x,y) -> x+y end)
head/1 # Get first element of list
tail/1 # Get list without first element
cons/2 # Prepend element: cons(1, [2,3]) == [1,2,3]
append/2 # Join two lists
length/1 # Get list length
# 🎆 WORKING Example (successfully compiles and runs):
module DependentTypes do
export [demo_all/0]
import Std [List, Result] # ✅ Working import system!
def demo_all(): Int =
let numbers = [1, 2, 3, 4, 5]
let doubled = map(numbers, fn(x) -> x * 2 end) # [2,4,6,8,10]
let sum = fold(doubled, 0, fn(x, acc) -> acc + x end) # 30
println("Sum of doubled numbers: " <> show(sum)) # Output: "Sum: 30"
0
end
# ✅ VERIFIED: Successfully compiles and executes!
# Console Output:
# Sum of doubled numbers: 30
# Lambda expressions
let double = fn(x) -> x * 2 end
let add = fn(x, y) -> x + y end
# Multi-line lambda
let safe_divide = fn(x, y) ->
match y == 0 do
true -> error("Division by zero")
false -> ok(x / y)
end
end
# Pipe operator for function composition
let result = input
|> validate_input()
|> process_data()
|> format_output()
# Lambda with pipe
let processed = numbers
|> filter(fn(x) -> x > 0 end)
|> map(fn(x) -> x * 2 end)
# Record definition
record Person do
name: String
age: Nat
email: String
end
# Creating records
let person = Person{name: "Alice", age: 30, email: "alice@example.com"}
# Pattern matching on records
def greet(person: Person): String =
match person do
Person{name: name, age: age} when age >= 18 ->
"Hello, adult " <> name <> "!"
Person{name: name} ->
"Hello, young " <> name <> "!"
end
# Union types
type Result(T, E) = Ok(T) | Error(E)
type Maybe(T) = Some(T) | None
# 🎆 PRODUCTION READY: Length-indexed vectors with compile-time safety
module DependentTypes do
export [demo_all/0, vector_operations/0]
import Std [List, Result] # ✅ Complete import system integration
# ✅ Vector type parameterized by length and element type
def make_vec3(x: Float, y: Float, z: Float): Vector(Float, 3) =
[x, y, z] # Type system guarantees exactly 3 elements
# ✅ Safe vector operations - length checked at compile time
def dot_product(v1: Vector(Float, n), v2: Vector(Float, n)): Float =
# Type system guarantees v1 and v2 have identical length
zip_with(v1, v2, fn(x, y) -> x * y end)
|> fold(0.0, fn(x, acc) -> acc + x end)
def vector_add(v1: Vector(Float, n), v2: Vector(Float, n)): Vector(Float, n) =
# Type system ensures result has the same length as inputs
zip_with(v1, v2, fn(x, y) -> x + y end)
def demo_all(): Int =
let v1 = make_vec3(1.0, 2.0, 3.0)
let v2 = make_vec3(4.0, 5.0, 6.0)
let dot_result = dot_product(v1, v2) # 32.0
println("Dot product: " <> show(dot_result))
0
end
# 🚀 WORKING: Safe operations with dependent constraints
def safe_head(list: List(T, n)) -> T when n > 0 =
# Type system guarantees list is non-empty
match list do
[x | _] -> x
# No need for empty case - type system prevents it
end
def safe_tail(list: List(T, n)) -> List(T, n-1) when n > 0 =
match list do
[_ | tail] -> tail
# No need for empty case - type system prevents it
end
# ✅ VERIFIED: Successfully compiles and runs!
# Runtime Output from dependent_types_simple.cure:
# === Dependent Types Demonstration ===
# All operations below are compile-time verified for safety!
# === Vector Operations ===
# Dot product: 32.0
# Vector sum: [5.0, 7.0, 9.0]
# Scaled vector: [2.0, 4.0, 6.0]
# Length-indexed lists
def append(xs: List(T, n), ys: List(T, m)): List(T, n + m) =
match xs do
[] -> ys
[x|rest] -> x :: append(rest, ys)
end
# Matrix operations with dimension checking
record Matrix(rows: Nat, cols: Nat, T) do
data: Vector(Vector(T, cols), rows)
end
def matrix_multiply(
a: Matrix(m, n, T),
b: Matrix(n, p, T)
): Matrix(m, p, T) =
# Implementation ensures dimensions match at compile time
...
# Refinement types
type NonEmptyList(T) = List(T, n) when n > 0
def head(list: NonEmptyList(T)): T =
match list do
[x|_] -> x
# No need for empty case - type system guarantees non-empty
end
# Top-level program structure
program ::= module_def | item*
module_def ::= 'module' IDENTIFIER 'do' export_list? item* 'end'
export_list ::= 'export' '[' export_item (',' export_item)* ']'
export_item ::= IDENTIFIER ('/' INTEGER)?
# Top-level items
item ::= function_def | def_erl_def | type_def | record_def | fsm_def
| process_def | import_def | let_binding
# Function definitions
function_def ::= ('def') IDENTIFIER '(' param_list? ')' type_annotation? constraint? '=' expr
def_erl_def ::= 'def_erl' IDENTIFIER '(' param_list? ')' type_annotation? constraint? '=' expr
param_list ::= param (',' param)*
param ::= IDENTIFIER ':' type
type_annotation ::= '->' type | ':' type
constraint ::= 'when' expr
# Type definitions
type_def ::= 'type' IDENTIFIER type_params? '=' type_expr
record_def ::= 'record' IDENTIFIER type_params? 'do' field_list 'end'
type_params ::= '(' type_param (',' type_param)* ')'
type_param ::= IDENTIFIER | IDENTIFIER ':' type
field_list ::= field*
field ::= IDENTIFIER ':' type
# FSM definitions (arrow-based transitions)
fsm_def ::= 'fsm' IDENTIFIER '{' field_init (',' field_init)_ '}' 'do' fsm_transition_ 'end'
field_init ::= IDENTIFIER ':' expr
fsm_transition ::= IDENTIFIER '-->' '|' IDENTIFIER '|' IDENTIFIER # FromState --> |event| ToState
# Process definitions
process_def ::= 'process' IDENTIFIER '(' param_list? ')' 'do' process_body 'end'
process_body ::= item* expr
# Import definitions ✅ WORKING!
import_def ::= 'import' IDENTIFIER import_list?
import_list ::= '[' import_item (',' import_item)* ']'
import_item ::= IDENTIFIER ('/' INTEGER)? | IDENTIFIER 'as' IDENTIFIER # Function name, arity, or alias
# Let bindings
let_binding ::= 'let' IDENTIFIER '=' expr
# Types
type ::= primitive_type | compound_type | dependent_type | function_type
| union_type | refinement_type
primitive_type ::= 'Int' | 'Float' | 'Atom' | 'Bool' | 'String' | 'Binary'
| 'Nat' | 'Pos' | 'Pid' | 'Unit'
compound_type ::= IDENTIFIER type_args?
| '[' type ']' # List type
| '{' type (',' type)* '}' # Tuple type
dependent_type ::= IDENTIFIER '(' type_arg (',' type_arg)* ')'
type_arg ::= type | expr
function_type ::= '(' param_list ')' '->' type
union_type ::= type ('|' type)+
refinement_type ::= type 'when' expr
| '{' IDENTIFIER ':' type '|' expr '}'
# Expressions
expr ::= literal | identifier | function_call | match_expr
| receive_expr | record_expr | list_expr | tuple_expr
| binary_op | unary_op | lambda_expr | spawn_expr | send_expr | fsm_expr
literal ::= INTEGER | FLOAT | STRING | ATOM | BOOLEAN | 'Ok' | 'Error' | 'Some' | 'None'
identifier ::= IDENTIFIER | qualified_identifier
qualified_identifier ::= IDENTIFIER '.' IDENTIFIER
function_call ::= expr '(' arg_list? ')'
arg_list ::= expr (',' expr)*
# Pattern matching
match_expr ::= 'match' expr 'do' match_clause* 'end'
match_clause ::= pattern guard? '->' expr
pattern ::= literal | identifier | constructor_pattern | list_pattern
| tuple_pattern | record_pattern | wildcard
constructor_pattern ::= IDENTIFIER pattern_args?
pattern_args ::= '(' pattern (',' pattern)* ')'
list_pattern ::= '[' ']' | '[' pattern (',' pattern)* ']'
| '[' pattern '|' pattern ']'
tuple_pattern ::= '{' pattern (',' pattern)* '}'
record_pattern ::= IDENTIFIER '{' field_pattern (',' field_pattern)* '}'
field_pattern ::= IDENTIFIER ':' pattern | IDENTIFIER
wildcard ::= '_'
guard ::= 'when' expr
# Process communication
receive_expr ::= 'receive' 'do' receive_clause* 'end'
receive_clause ::= pattern guard? '->' expr
spawn_expr ::= 'spawn' '(' IDENTIFIER ',' '[' arg_list? ']' ')'
send_expr ::= 'send' '(' expr ',' expr ')'
# FSM operations
fsm_expr ::= 'fsm_spawn' '(' IDENTIFIER ')'
| 'fsm_send' '(' expr ',' expr ')'
# Data structures
record_expr ::= IDENTIFIER '{' field_assign (',' field_assign)* '}'
field_assign ::= IDENTIFIER ':' expr
list_expr ::= '[' ']' | '[' expr (',' expr)* ']'
tuple_expr ::= '{' expr (',' expr)* '}'
# Operators
binary_op ::= expr binary_operator expr
unary_op ::= unary_operator expr
binary_operator ::= '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '>'
| '<=' | '>=' | '&&' | '||' | '|' | '<>' | '|>'
# Note: '|' is list cons operator, '<>' is string concatenation
unary_operator ::= '-' | '!'
# Lambda expressions
lambda_expr ::= 'fn' '(' param_list? ')' '->' expr ('end')?
| 'fn' '(' param_list? ')' '->' expr_block 'end'
expr_block ::= expr+
# String interpolation
string_interpolation ::= '"' string_part* '"'
string_part ::= STRING_CHARS | '#{' expr '}'
# Lexical tokens
IDENTIFIER ::= [a-zA-Z_][a-zA-Z0-9_]*
INTEGER ::= [0-9]+
FLOAT ::= [0-9]+ '.' [0-9]+
STRING ::= '"' ([^"\\] | '\\' .)* '"'
ATOM ::= ':' IDENTIFIER | ':"' ([^"\\] | '\\' .)* '"'
BOOLEAN ::= 'true' | 'false'
KEYWORD ::= 'def' | 'def_erl' | 'module' | 'import' | 'export' | 'fsm'
| 'state' | 'states' | 'initial' | 'event' | 'timeout' | 'match' | 'when'
| 'if' | 'then' | 'else' | 'let' | 'in' | 'as' | 'do' | 'end' | 'fn'
| 'process' | 'receive' | 'send' | 'spawn' | 'record' | 'type'
| 'and' | 'or' | 'not' | 'ok' | 'error'
COMMENT ::= '#' [^\n]*
WHITESPACE ::= [ \t\n\r]+
Cure implements a sophisticated dependent type system with SMT-based constraint solving:
Vector(T, n: Nat) # Length-indexed vectors
List(T, n: Nat) # Lists with compile-time known length
Matrix(rows, cols, T) # Matrices with dimension checking
{x: Int | x > 0} # Positive integers
{xs: List(T) | length(xs) > 0} # Non-empty lists
def replicate(n: Nat, x: T): List(T, n) # Return type depends on input
typeclass Ord(T) where
def compare(x: T, y: T): Ordering
end
derive Ord for List(T) when Ord(T)
fsm Counter(max: Int) do
states: [Zero, Counting(n: Int) where 0 < n <= max]
# Compiler verifies all transitions maintain constraints
end
The type checker integrates with SMT solvers for complex constraint verification:
The Cure compiler implements a complete 5-stage pipeline with 100% functional implementation:
cure_lexer.erl) ✅ WORKINGcure_parser.erl) ✅ WORKINGcureast.erl, cureast.hrl) for all constructscure_typechecker.erl) ✅ WORKINGcuresmtsolver.erl) with Z3 integrationcuretypeoptimizer.erl) ✅ WORKINGcurecodegen.erl, curebeam_compiler.erl) ✅ WORKINGgen_statem behaviorsCure provides seamless BEAM ecosystem integration:
gen_statem for supervision tree integration# Cure standard library provides BEAM-compatible modules
import Std [Result, Option, ok, error] # Error handling
import Std.List [map/2, filter/2, fold_left/3] # List operations
import Std.Math [abs/1, sqrt/1, sin/1] # Mathematical functions
import Std.FSM [spawn/2, send_event/2] # FSM utilities
gen_statem integration---
This specification describes the current implementation of Cure version 0.1.0, representing a complete, functional dependently-typed programming language for the BEAM virtual machine.