Values Module
This module defines the value expressions, literals, patterns, and value definitions for Morphir IR.
Literals
Literal constant values.
// === literal.gleam ===
/// Literal constant values
pub type Literal {
/// Boolean: true, false
BoolLiteral(value: Bool)
/// Single character
CharLiteral(value: String)
/// Text string
StringLiteral(value: String)
/// Integer (arbitrary precision in IR, includes negatives)
IntegerLiteral(value: Int)
/// Floating-point
FloatLiteral(value: Float)
/// Arbitrary-precision decimal (stored as string for precision)
DecimalLiteral(value: String)
}
Migration from v1/v2/v3
| v1/v2/v3 | v4 | Notes |
|---|---|---|
WholeNumberLiteral | IntegerLiteral | Breaking change: Renamed for correctness (whole numbers are non-negative; integers include negatives) |
Decoders should accept both WholeNumberLiteral and IntegerLiteral for backwards compatibility. V4 encoders should output IntegerLiteral.
Patterns
Patterns for destructuring and matching.
// === pattern.gleam ===
/// Patterns for destructuring and matching
pub type Pattern(attributes) {
/// Matches anything, binds nothing: `_`
WildcardPattern(attributes: attributes)
/// Binds a name while matching: `x` or `(a, b) as pair`
AsPattern(
attributes: attributes,
pattern: Pattern(attributes),
name: Name,
)
/// Matches tuple: `(a, b, c)`
TuplePattern(
attributes: attributes,
elements: List(Pattern(attributes)),
)
/// Matches constructor: `Just x`, `Nothing`
ConstructorPattern(
attributes: attributes,
constructor: FQName,
args: List(Pattern(attributes)),
)
/// Matches empty list: `[]`
EmptyListPattern(attributes: attributes)
/// Matches head :: tail: `x :: xs`
HeadTailPattern(
attributes: attributes,
head: Pattern(attributes),
tail: Pattern(attributes),
)
/// Matches literal: `42`, `"hello"`, `True`
LiteralPattern(attributes: attributes, literal: Literal)
/// Matches unit: `()`
UnitPattern(attributes: attributes)
}
Value Expressions
Value expressions form the core computation language.
// === value.gleam ===
/// Value expressions - the core computation language
pub type Value(attributes) {
// ===== Literals & Data Construction =====
/// Literal constant
Literal(attributes: attributes, literal: Literal)
/// Constructor reference: `Just`, `Nothing`
Constructor(attributes: attributes, fqname: FQName)
/// Tuple: `(1, "hello", True)`
Tuple(attributes: attributes, elements: List(Value(attributes)))
/// List: `[1, 2, 3]`
List(attributes: attributes, items: List(Value(attributes)))
/// Record: `{ name = "Alice", age = 30 }`
/// Field order does not affect equality
Record(attributes: attributes, fields: Dict(Name, Value(attributes)))
/// Unit value: `()`
Unit(attributes: attributes)
// ===== References =====
/// Variable reference: `x`, `myValue`
Variable(attributes: attributes, name: Name)
/// Reference to defined value: `List.map`, `MyModule.myFunction`
Reference(attributes: attributes, fqname: FQName)
// ===== Field Access =====
/// Field access: `record.fieldName`
Field(
attributes: attributes,
record: Value(attributes),
field_name: Name,
)
/// Field accessor function: `.fieldName`
FieldFunction(attributes: attributes, field_name: Name)
// ===== Function Application =====
/// Function application: `f x` (curried, one arg at a time)
Apply(
attributes: attributes,
function: Value(attributes),
argument: Value(attributes),
)
/// Lambda: `\x -> x + 1`
Lambda(
attributes: attributes,
argument_pattern: Pattern(attributes),
body: Value(attributes),
)
// ===== Let Bindings =====
/// Single let binding: `let x = 1 in x + 1`
LetDefinition(
attributes: attributes,
name: Name,
definition: ValueDefinitionBody(attributes),
in_value: Value(attributes),
)
/// Mutually recursive let: `let f = ... g ...; g = ... f ... in ...`
LetRecursion(
attributes: attributes,
bindings: Dict(Name, ValueDefinitionBody(attributes)),
in_value: Value(attributes),
)
/// Pattern destructuring: `let (a, b) = tuple in a + b`
Destructure(
attributes: attributes,
pattern: Pattern(attributes),
value_to_destructure: Value(attributes),
in_value: Value(attributes),
)
// ===== Control Flow =====
/// Conditional: `if cond then a else b`
IfThenElse(
attributes: attributes,
condition: Value(attributes),
then_branch: Value(attributes),
else_branch: Value(attributes),
)
/// Pattern match: `case x of ...`
PatternMatch(
attributes: attributes,
subject: Value(attributes),
cases: List(#(Pattern(attributes), Value(attributes))),
)
// ===== Record Update =====
/// Record update: `{ record | field = newValue }`
/// Field order does not affect equality
UpdateRecord(
attributes: attributes,
record: Value(attributes),
updates: Dict(Name, Value(attributes)),
)
// ===== Special Values (v4 additions) =====
/// Incomplete/broken reference (for best-effort generation)
Hole(
attributes: attributes,
reason: HoleReason,
expected_type: Option(Type(attributes)),
)
/// Native platform operation (no IR body)
Native(
attributes: attributes,
fqname: FQName,
native_info: NativeInfo,
)
/// External FFI call
External(
attributes: attributes,
external_name: String,
target_platform: String,
)
}
/// Information about native operations
pub type NativeInfo {
NativeInfo(
hint: NativeHint,
description: Option(String),
)
}
pub type NativeHint {
/// Basic arithmetic/logic operation
Arithmetic
/// Comparison operation
Comparison
/// String operation
StringOp
/// Collection operation (map, filter, fold, etc.)
CollectionOp
/// Platform-specific operation
PlatformSpecific(platform: String)
}
Value Definitions
// === value_definition.gleam ===
/// The body of a value definition (used in let bindings and top-level definitions)
pub type ValueDefinitionBody(attributes) {
/// Normal IR expression body
ExpressionBody(
input_types: List(#(Name, Type(attributes))),
output_type: Type(attributes),
body: Value(attributes),
)
/// Native/builtin operation (no IR body)
NativeBody(
input_types: List(#(Name, Type(attributes))),
output_type: Type(attributes),
native_info: NativeInfo,
)
/// External FFI (no IR body)
ExternalBody(
input_types: List(#(Name, Type(attributes))),
output_type: Type(attributes),
external_name: String,
target_platform: String,
)
/// Incomplete definition (v4 - best-effort support)
IncompleteBody(
input_types: List(#(Name, Type(attributes))),
output_type: Option(Type(attributes)),
incompleteness: Incompleteness,
partial_body: Option(Value(attributes)),
)
}
/// Top-level value definition (in a module)
pub type ValueDefinition(attributes) {
ValueDefinition(
body: AccessControlled(ValueDefinitionBody(attributes)),
)
}
/// Value specification - the public interface (signature only, no implementation)
pub type ValueSpecification(attributes) {
ValueSpecification(
annotations: List(Annotation),
inputs: List(#(Name, Type(attributes))),
output: Type(attributes),
)
}
JSON Serialization Examples
Literal Examples
V4 follows a permissive input, canonical output policy for Literals.
Literal (within LiteralValue or LiteralPattern)
| Literal Type | Canonical | Compact | Notes |
|---|---|---|---|
| BoolLiteral | { "BoolLiteral": { "value": true } } | { "BoolLiteral": true } | |
| StringLiteral | { "StringLiteral": { "value": "hello" } } | { "StringLiteral": "hello" } | |
| IntegerLiteral | { "IntegerLiteral": { "value": 42 } } | { "IntegerLiteral": 42 } | Renamed from WholeNumberLiteral |
| FloatLiteral | { "FloatLiteral": { "value": 3.14 } } | { "FloatLiteral": 3.14 } | |
| DecimalLiteral | { "DecimalLiteral": { "value": "123.456" } } | { "DecimalLiteral": "123.456" } | String for precision |
| CharLiteral | { "CharLiteral": { "value": "A" } } | { "CharLiteral": "A" } |
LiteralValue (Value expression wrapping a Literal)
| Format | Example | Notes |
|---|---|---|
| Canonical | { "Literal": { "IntegerLiteral": 42 } } | Compact literal inside |
| Expanded | { "Literal": { "literal": { "IntegerLiteral": { "value": 42 } } } } | With attributes |
| With attributes | { "Literal": { "attributes": {...}, "literal": {...} } } | Full form |
// Canonical forms (encoders should output these)
{ "BoolLiteral": true }
{ "StringLiteral": "hello world" }
{ "IntegerLiteral": 42 }
{ "FloatLiteral": 3.14159 }
{ "DecimalLiteral": "123456789.987654321" }
{ "CharLiteral": "A" }
// Expanded forms (accepted)
{ "BoolLiteral": { "value": true } }
{ "StringLiteral": { "value": "hello world" } }
{ "IntegerLiteral": { "value": 42 } }
{ "FloatLiteral": { "value": 3.14159 } }
{ "DecimalLiteral": { "value": "123456789.987654321" } }
{ "CharLiteral": { "value": "A" } }
// Legacy (accepted for backwards compatibility)
{ "WholeNumberLiteral": 42 }
{ "WholeNumberLiteral": { "value": 42 } }
Pattern Examples
WildcardPattern
{ "WildcardPattern": {} }
AsPattern (variable binding)
Name becomes the key, pattern is the value:
{ "AsPattern": { "x": { "WildcardPattern": {} } } }
Simple variable binding (most common case):
{ "AsPattern": { "user-name": { "WildcardPattern": {} } } }
TuplePattern
V4 follows a permissive input, canonical output policy for TuplePattern.
| Format | Example | Notes |
|---|---|---|
| Bare array | [pattern1, pattern2, ...] | Compact, unambiguous |
| Canonical | { "TuplePattern": [pattern1, pattern2, ...] } | Wrapper with array |
| Expanded | { "TuplePattern": { "patterns": [pattern1, ...] } } | Wrapper with object |
Bare arrays are unambiguous for TuplePattern because no other pattern type uses bare arrays at the top level.
// Bare array (most compact, accepted)
[
{ "AsPattern": { "a": { "WildcardPattern": {} } } },
{ "AsPattern": { "b": { "WildcardPattern": {} } } }
]
// Canonical (encoders should output this)
{
"TuplePattern": [
{ "AsPattern": { "a": { "WildcardPattern": {} } } },
{ "AsPattern": { "b": { "WildcardPattern": {} } } }
]
}
// Expanded form (accepted)
{
"TuplePattern": {
"patterns": [
{ "AsPattern": { "a": { "WildcardPattern": {} } } },
{ "AsPattern": { "b": { "WildcardPattern": {} } } }
]
}
}
ConstructorPattern
{
"ConstructorPattern": {
"constructor": "morphir/sdk:maybe#just",
"args": [
{ "AsPattern": { "value": { "WildcardPattern": {} } } }
]
}
}
HeadTailPattern
{
"HeadTailPattern": {
"head": { "AsPattern": { "x": { "WildcardPattern": {} } } },
"tail": { "AsPattern": { "xs": { "WildcardPattern": {} } } }
}
}
LiteralPattern
V4 follows a permissive input, canonical output policy for LiteralPattern.
| Format | Example | Notes |
|---|---|---|
| Ultra-compact | { "LiteralPattern": 42 } | Direct primitive value |
| Canonical | { "LiteralPattern": { "IntegerLiteral": 42 } } | Compact literal |
| Expanded | { "LiteralPattern": { "literal": { "IntegerLiteral": { "value": 42 } } } } | Full form |
// Ultra-compact (most ergonomic)
{ "LiteralPattern": 42 }
{ "LiteralPattern": "hello" }
{ "LiteralPattern": true }
// Canonical (encoders should output this)
{ "LiteralPattern": { "IntegerLiteral": 42 } }
{ "LiteralPattern": { "StringLiteral": "hello" } }
{ "LiteralPattern": { "BoolLiteral": true } }
// Expanded form (accepted)
{ "LiteralPattern": { "literal": { "IntegerLiteral": { "value": 42 } } } }
Value Shorthand
V4 supports compact shorthand for value expressions when attributes are empty.
| Type | Shorthand | Canonical | Notes |
|---|---|---|---|
| Bool | true | {"Literal": {"BoolLiteral": true}} | |
| Number | 42 | {"Literal": {"IntegerLiteral": 42}} | |
| Reference | "pkg:mod#val" | {"Reference": "pkg:mod#val"} | If string matches FQName pattern |
| Variable | "name" | {"Variable": "name"} | If string matches Name pattern |
| List | [v1, v2] | {"List": [v1, v2]} |
- Strings are first checked against the FQName pattern.
- If it doesn't match, it's checked against the Name pattern for a
Variable. - String Literals and Tuples must always use explicit wrappers to avoid ambiguity.
Value Expression Examples
Literal
{
"Literal": {
"literal": { "IntegerLiteral": { "value": 42 } }
}
}
Variable
{ "Variable": { "name": "user-name" } }
Reference
{ "Reference": { "fqname": "morphir/sdk:list#map" } }
Constructor
{ "Constructor": { "fqname": "morphir/sdk:maybe#just" } }
Tuple (TupleValue)
V4 follows a permissive input, canonical output policy for TupleValue.
| Format | Example | Notes |
|---|---|---|
| Canonical | { "Tuple": [value1, value2, ...] } | Wrapper with array |
| Expanded | { "Tuple": { "elements": [value1, ...] } } | Wrapper with object |
Bare arrays are NOT allowed for TupleValue. This would be ambiguous with ListValue. The wrapper is required to distinguish tuples from lists.
// Canonical (encoders should output this)
{
"Tuple": [
{ "Literal": { "IntegerLiteral": 1 } },
{ "Literal": { "StringLiteral": "hello" } }
]
}
// Expanded form (accepted)
{
"Tuple": {
"elements": [
{ "Literal": { "IntegerLiteral": 1 } },
{ "Literal": { "StringLiteral": "hello" } }
]
}
}
List (ListValue)
V4 follows a permissive input, canonical output policy for ListValue.
| Format | Example | Notes |
|---|---|---|
| Canonical | { "List": [value1, value2, ...] } | Wrapper with array |
| Expanded | { "List": { "items": [value1, ...] } } | Wrapper with object |
Bare arrays are NOT allowed for ListValue. This would be ambiguous with TupleValue. The wrapper is required to distinguish lists from tuples.
// Canonical (encoders should output this)
{
"List": [
{ "Literal": { "IntegerLiteral": 1 } },
{ "Literal": { "IntegerLiteral": 2 } }
]
}
// Expanded form (accepted)
{
"List": {
"items": [
{ "Literal": { "IntegerLiteral": 1 } },
{ "Literal": { "IntegerLiteral": 2 } }
]
}
}
Record (fields as object keys, sorted alphabetically)
{
"Record": {
"fields": {
"age": { "Literal": { "literal": { "IntegerLiteral": { "value": 30 } } } },
"name": { "Literal": { "literal": { "StringLiteral": { "value": "Alice" } } } }
}
}
}
Field Access
{
"Field": {
"record": { "Variable": { "name": "user" } },
"fieldName": "email"
}
}
FieldFunction
{ "FieldFunction": { "fieldName": "email" } }
Apply (function application)
{
"Apply": {
"function": { "Reference": { "fqname": "morphir/sdk:list#map" } },
"argument": { "Variable": { "name": "transform" } }
}
}
Lambda
{
"Lambda": {
"argumentPattern": { "AsPattern": { "x": { "WildcardPattern": {} } } },
"body": {
"Apply": {
"function": {
"Apply": {
"function": { "Reference": { "fqname": "morphir/sdk:basics#add" } },
"argument": { "Variable": { "name": "x" } }
}
},
"argument": { "Literal": { "literal": { "IntegerLiteral": { "value": 1 } } } }
}
}
}
}
LetDefinition
Name becomes the key:
{
"LetDefinition": {
"x": {
"def": {
"ExpressionBody": {
"outputType": { "Reference": { "fqname": "morphir/sdk:basics#int" } },
"body": { "Literal": { "literal": { "IntegerLiteral": { "value": 42 } } } }
}
},
"inValue": {
"Apply": {
"function": {
"Apply": {
"function": { "Reference": { "fqname": "morphir/sdk:basics#add" } },
"argument": { "Variable": { "name": "x" } }
}
},
"argument": { "Literal": { "literal": { "IntegerLiteral": { "value": 1 } } } }
}
}
}
}
}
LetRecursion
Binding names as keys:
{
"LetRecursion": {
"bindings": {
"is-even": {
"ExpressionBody": {
"inputTypes": [["n", { "Reference": { "fqname": "morphir/sdk:basics#int" } }]],
"outputType": { "Reference": { "fqname": "morphir/sdk:basics#bool" } },
"body": { "Variable": { "name": "..." } }
}
},
"is-odd": {
"ExpressionBody": {
"inputTypes": [["n", { "Reference": { "fqname": "morphir/sdk:basics#int" } }]],
"outputType": { "Reference": { "fqname": "morphir/sdk:basics#bool" } },
"body": { "Variable": { "name": "..." } }
}
}
},
"inValue": { "Variable": { "name": "is-even" } }
}
}
IfThenElse
{
"IfThenElse": {
"condition": { "Variable": { "name": "is-valid" } },
"thenBranch": { "Literal": { "literal": { "StringLiteral": { "value": "yes" } } } },
"elseBranch": { "Literal": { "literal": { "StringLiteral": { "value": "no" } } } }
}
}
PatternMatch
{
"PatternMatch": {
"subject": { "Variable": { "name": "maybe-value" } },
"cases": [
[
{ "ConstructorPattern": { "constructor": "morphir/sdk:maybe#just", "args": [{ "AsPattern": { "v": { "WildcardPattern": {} } } }] } },
{ "Variable": { "name": "v" } }
],
[
{ "ConstructorPattern": { "constructor": "morphir/sdk:maybe#nothing" } },
{ "Literal": { "literal": { "IntegerLiteral": { "value": 0 } } } }
]
]
}
}
UpdateRecord
{
"UpdateRecord": {
"record": { "Variable": { "name": "user" } },
"updates": {
"age": { "Literal": { "literal": { "IntegerLiteral": { "value": 31 } } } }
}
}
}
Hole (v4 - incomplete value)
{
"Hole": {
"reason": {
"UnresolvedReference": { "target": "my-org/project:module#deleted-function" }
},
"expectedType": { "Reference": { "fqname": "morphir/sdk:basics#int" } }
}
}
Native (v4 - platform operation)
{
"Native": {
"fqname": "morphir/sdk:basics#add",
"nativeInfo": {
"hint": { "Arithmetic": {} },
"description": "Integer addition"
}
}
}
Value Definition Examples
ExpressionBody
Input names as keys:
{
"ExpressionBody": {
"inputTypes": {
"x": { "Reference": { "fqname": "morphir/sdk:basics#int" } }
},
"outputType": { "Reference": { "fqname": "morphir/sdk:basics#int" } },
"body": {
"Apply": {
"function": {
"Apply": {
"function": { "Reference": { "fqname": "morphir/sdk:basics#add" } },
"argument": { "Variable": { "name": "x" } }
}
},
"argument": { "Literal": { "literal": { "IntegerLiteral": { "value": 1 } } } }
}
}
}
}
NativeBody (builtin operation)
{
"NativeBody": {
"inputTypes": {
"a": { "Reference": { "fqname": "morphir/sdk:basics#int" } },
"b": { "Reference": { "fqname": "morphir/sdk:basics#int" } }
},
"outputType": { "Reference": { "fqname": "morphir/sdk:basics#int" } },
"nativeInfo": {
"hint": { "Arithmetic": {} }
}
}
}
ValueSpecification (signature only)
{
"ValueSpecification": {
"inputs": {
"x": { "Reference": { "fqname": "morphir/sdk:basics#int" } },
"y": { "Reference": { "fqname": "morphir/sdk:basics#int" } }
},
"output": { "Reference": { "fqname": "morphir/sdk:basics#int" } }
}
}