Lexical Structure
This document describes the lexical structure of Keel source code.
Character Set
Keel source files are UTF-8 encoded. Identifiers can contain Unicode letters.
Comments
Line Comments
-- This is a line comment
let x = 42 -- Inline comment
Try itBlock Comments
{- This is a
block comment -}
{- Block comments
{- can be nested -}
like this -}
Doc Comments
Doc comments use {-| ... -} syntax. They are used by tooling (LSP hover, documentation generation) and are distinguished from regular block comments:
{-| Increments a number by one. -}
fn inc : Int -> Int
fn inc x = x + 1
{-| A multiline doc comment
explaining complex behavior. -}
fn double : Int -> Int
fn double x = x * 2
Whitespace and Indentation
Keel uses indentation to define block structure. Indentation is significant for:
- Function bodies
- Case expression branches
- Inline module content
fn example : Int -> Int
fn example x =
let y = 1 -- Must be indented
x + y
Try itIdentifiers
Value Identifiers
Start with lowercase letter or underscore:
name
_private
camelCase
snake_case
x1
Type Identifiers
Start with uppercase letter:
Int
String
Maybe
MyCustomType
Keywords
Reserved words that cannot be used as identifiers:
case of if then else
let in fn type mut
module import exposing as alias
inline passing
True False
Note: mut is reserved for use in file inlining exposing clauses (to mark variables whose mutations propagate back to the caller). It cannot be used for mutable bindings — Keel is a pure functional language where all bindings are immutable.
Literals
Integer Literals
42 -- Decimal
-7 -- Negative
Try itFloat Literals
3.14
Try itDecimal Literals
Decimal literals use the d suffix for arbitrary-precision decimal values:
42d -- Integer decimal
3.14d -- Fractional decimal
-0.001d -- Negative decimal
Try itCharacter Literals
'a'
'\n' -- Newline
'\t' -- Tab
'\\' -- Backslash
'\'' -- Single quote
Try itString Literals
"Hello, World!"
"Line 1\nLine 2"
"Tab\there"
"Quote: \"quoted\""
Try itBoolean Literals
True
False
Try itUnit Literal
()
Try itOperators
Arithmetic Operators
| Operator | Description |
|---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
// | Integer division |
% | Modulo |
^ | Power |
Comparison Operators
| Operator | Description |
|---|---|
== | Equal |
!= | Not equal |
< | Less than |
<= | Less than or equal |
> | Greater than |
>= | Greater than or equal |
Logical Operators
| Operator | Description |
|---|---|
&& | Logical AND |
|| | Logical OR |
not | Logical NOT |
Function Operators
| Operator | Description |
|---|---|
>> | Compose (left to right) |
<< | Compose (right to left) |
|> | Pipe forward |
<| | Pipe backward |
List Operators
| Operator | Description |
|---|---|
:: | Cons (prepend) |
++ | Concatenation |
Delimiters
( ) -- Grouping, tuples, unit
[ ] -- Lists, index access
{ } -- Records, blocks, block comments
, -- Separator
: -- Type annotation
-> -- Function type, case branches
= -- Definition, record fields
| -- Pattern alternatives, enum variants, lambda start
Lambda Syntax
Lambdas use pipe characters:
|x| x + 1
|x y| x + y
|x: Int| x + 1
|(x, y)| x + y
Pattern Matching Syntax
Case expressions with guards:
case value of
pattern -> result
n if n > 0 -> "positive"
_ -> "default"
Record Syntax
Records use = for field assignment:
{ name = "Alice", age = 30 }
Try itRecord update uses |:
{ person | age = 31 }
Module Syntax
-- File-level module
module exposing (foo, bar)
-- Named inline module (content must be indented)
module Math exposing (add)
fn add : Int -> Int -> Int
fn add x y = x + y
-- Parameterized module (callee file for file inlining)
module (x : Int) exposing (result : Int)
File Inlining Syntax
File inlining is resolved at compile time. The inline expression returns a record of exposed values:
-- Inline a file, passing variables and destructuring the result
let { result } = inline "file.kl" passing (x, y)
-- Pass all variables
let { result } = inline "file.kl" passing (..)
-- No variables to pass (passing clause is optional)
let { answer } = inline "file.kl"
Type Definition Syntax
-- Type alias
type alias Name = String
-- Enum type (single line)
type Direction = North | South | East | West
-- Enum type (multi-line)
type Shape
= Circle(Float)
| Rectangle(Float, Float)
-- Enum with record variant
type User
= Guest
| Member { name: String, id: Int }
Enum Construction
Enum variants can be constructed with parenthesized or space-separated syntax (single argument only):
Shape::Circle(5.0) -- parenthesized
Shape::Circle 5.0 -- space-separated (single arg)
Shape::Rectangle(10.0, 20.0) -- multi-arg requires parentheses
Qualified Enum Syntax
Enum variants use :: for qualified access, both in expressions and patterns:
let color = Color::Green
case color of
Color::Red -> "fire"
Color::Green -> "nature"
Color::Blue -> "sky"