Basic Syntax

Top-level Expressions

Definition

You can bind an expression to a variable by connecting them with :=. If you want to declare it at the top level of a program, start with a def keyword. When defining functions, the argument variable can be placed in the left hand side of the :=.

def x := 1

-- The following two definitions are identical.
def f := \x -> x + 1
def f x := x + 1

You can decompose data at the argument position as follows.

def uncurry f (x, y) := f x y
uncurry (+) (1, 2) -- 3

Expression

You can also write arbitrary expressions at the top level of programs. These expressions are evaluated only when the -t option (see -t / --test) is specified.

-- definitions
def x := 1
def y := 2

-- A top-level expression which will evaluate to 3.
x + y

load and loadFile

We can load Egison libraries with load. To load your own program, call loadFile with a full-path or a relative path to the file.

-- Load Egison library.
load "lib/core/number.egi"

-- Load your program.
loadFile "myfile.egi"

Infix Declaration

(From version 4.0.4)

You can define your own infixes (binary operators) for expressions and patterns. In Egison, infix declaration consists of the following 4 parts.

  • Associativity … infix (non associative), infixl (left associative) or infixr (right associative)
  • Infix type … expression (for functions) or pattern (for pattern constructors)
  • Priority of the infix
  • Representation of infix
-- Define a right-associative infix '&&' of priority 5.
infixr expression 5 &&

-- Definition of the semantics of '&&'.
def (&&) a b := match (a, b) as (eq, eq) with
              | (#True, #True) -> True
              | _              -> False

-- Define a left-associative infix '<>' of priority 7.
infixl pattern 7 <>

def exampleMatcher := matcher
  | $ <> $ as (integer, integer) with
    | $x :: $y :: [] -> [(x, y)]
    | _              -> []

match [1, 2] as exampleMatcher with $x <> $y -> x + y
---> 3

Basic Expressions

Anonymous function

An anonymous function consists of two parts: arguments and a body. The arguments are written between \ and ->, and the body is written at the right of ->.

-- A function of one argument.
\x -> x + 1

-- A function of two arguments.
\x y -> x + y

-- A function of no arguments.
\() -> 1

-- Function application
(\x y -> x + y) 3 7  ---> 10

You can also decompose data at the argument position as follows. Unlike the pattern-matching with match expressions, you can write only one pattern for each argument. Also, this decomposition works the same way as the ‘pattern matching’ in OCaml or Haskell, since you cannot specify matchers.

(\(x, y) -> x + y) (3, 7)  ---> 10

Anonymous parameter function

Egison has a shorthand notation for the anonymous function. In this syntax, the function body is prefixed with a n#, where n indicates the arity of the function. There must not be any spaces between the arity number n and the #. Also, the arguments are specified by numbers, where %i refers to the i-th argument.

This syntax is inspired by the anonymous function syntax of Clojure.

-- The followings are identical.
2#(%1 + %2)
\x y -> x + y

Section

Egison has a special syntax for the partial application of infix operators, which is inspired by the section notation of Haskell.

  • (+) is desugared into \x y -> x + y
  • (+ 1) is desugared into \x -> x + 1
  • (1 +) is desugared into \x -> 1 + x

letin expression

A letin expression (or simply a let expression) locally binds expressions to variables. Bindings defined in a let expression cannot be referred to outside of the let expression.

let x := 1 in x + 1 ---> 2

You can write multiple bindings in a single let expression. Note that the head of the binding must be aligned vertically in order to be parsed correctly.

let x := 1
    y := 2
 in x + y
---> 3

The above expression can be written in a single line as follows. The bindings must be wrapped with { } and separated with ;.

let { x := 1 ; y := 2 } in x + y

Bindings in the same let expression can depend on each other. The bindings do not necessarily be aligned in the order of dependency.

let y := x  -- 'x' is defined in the next binding
    x := 1
 in y
---> 1

-- We can even define mutual-recursive functions.
let isOdd n := if n = 0 then False else isEven (n - 1)
    isEven n := if n = 0 then True else isOdd (n - 1)
 in isOdd 5
---> True

As a result, note the following behavior.

def x := 3

let y := x
    x := 1
 in y
---> 1 (not 3)

You can also decompose data as follows. This decomposition does not require matchers and you can only specify one pattern. To support multiple patterns, use the match expression.

let (x, y) := (1, 2) in x + y -- 3
let (x :: _) := [1, 2, 3] in x -- 1
let (x :: _) := [] in x -- pattern match failure

The patterns must be syntactically atomic. If you want to use infix patterns (ex. ::) or data constructor patterns with arguments, surround them with parentheses.

let (x :: _) := [1, 2, 3] in x -- 1
-- let x :: _ := [1, 2, 3] in x -- parse error!

let (Just x) := Just 1 in x -- 1
-- let Just x := Just 1 in x -- parse error!

where expression

where is a syntax sugar for the above let expression. Unlike the let expression, the bindings in where expressions come after the body expression.

For example, the following two expressions are identical.

-- local bindings with `where`
expression
  where
    x1 := expr1
    x2 := expr2

-- local bindings with `let`
let x1 := expr1
    x2 := expr2
 in expression

if expression

It is the ordinary if expression. The guard expression (the one right after if) must be evaluated to a boolean (True or False).

if True then "Yes" else "No"  ---> "Yes"
if False then "Yes" else "No" ---> "No"

do expression

A do expression can group several IO functions into one IO function. You can bind expressions to values with let in the do expression as well. Every lines in the do block must either be an expression that evaluates to an IO function or a let binding. Note that all the lines in the do block must be aligned vertically.

def repl := do
  write ">>> "
  flush ()
  let line := readLine ()
  write line
  flush ()
  repl

A do expression can be written in one line as follows. The expressions needs to be wrapped with { } and separated by ;.

do { print "foo" ; print "bar" ; print "baz" }

The last statement in a do block must be an expression. The last expression in a do block is interpreted as the evaluation result of the do expression.

> io (do { return 1; return 2; return 3 })
3

seq expression

This expression is inspired by the seq function in Haskell.

A seq expression takes two arguments. The first argument of seq is strictly evaluated. The most popular use case of seq is in the definition of the foldl function.

def foldl $fn $init $ls :=
  match ls as list something with
    | [] -> init
    | $x :: $xs ->
      let z := fn init x
       in seq z (foldl fn z xs)