Skip to content
Rizo Isrof edited this page Dec 22, 2016 · 124 revisions

This document is a quick overview of Fold for the impatient, covering the language syntax and describing the fundamental programming constructs.

Many small but practical examples will demonstrate how the language feels and works. For a detailed step-by-step introduction to the language read the Language Guide or, if you are looking for a formal specification, consult the Language Reference.

Hello

Here is how the classic looks in Fold:

print "hello, world"

It is also a complete program that can be compiled and run.

$ cat > hello.fold
#!/usr/bin/env fold
print "hello, world"
$ chmod u+x hello.fold
$ ./hello.fold
hello, world

Additionally the REPL (read-eval-print loop) interface can be used for the interactive experimentation. To start the REPL simply type fold:

$ fold
     ▗▐▝        ▐▐      ▐▐   |  A modern pragmatic functional language.
    ▐▐     ▗▗   ▐▐    ▗▗▐▐   |
  ▝▝▐▐▝▝ ▐▐  ▐▐ ▐▐  ▐▐  ▐▐   |  Version 0.0.1-alpha+001 (2015-02-12)
    ▐▐   ▐▐  ▐▐ ▐▐  ▐▐  ▐▐   |  http://fold-lang.org
    ▐▐     ▝▝    ▝▝   ▝▝▝    |  Type \? for help
  ▗▐▝
  "Simplicity is prerequisite for reliability."
      — Edsger W. Dijkstra"
->

In the following sections the lines starting with -> simulate the interactive input, followed by the inferred type (annotated with :: operator) and the computed result.

Comments

Both single-line and multi-line comments are supported. Multi-line comments can be nested.

-- This is a line comment.

{- Block can span
   multiple
   lines. -}

Primitives

Booleans

In this section boolean values and logical operators are introduced. Logical operators can be applied both to boolean values and to lists with boolean values.

-> True
:: Bool = True

-> False
:: Bool = False

-> True && not (True || False)
:: Bool = False

-> [True, True, False] && [False, True, True]
:: List Bool = [False, True, False]

-> not [False, True, True]
:: List Bool = [True, False, False]

Numeric Values

Numeric values with different radices are supported. Underscores in the numeric values can improve readability and are ignored by the compiler.

-- Answer to the Ultimate Question of Life, the Universe, and Everything
-> 42
:: Int = 42

-> 4_294_967_296
:: Int = 4294967296

-> 0b0101010
:: Int = 42

-> 0xBADC0DE
:: Int = 195936478

-> 0o377
:: Int = 255

-- The π constant
-> 3.1415926
:: Float = 3.1415926

-> 10.0 * 2 - 10.0 / 2
:: Float = 15.0

Text

Characters, strings and string interpolation.

-> 'a'
:: Char = 'a'

-> "hello"
:: String = "hello"

-- String concatentation.
-> "hello" ++ " " ++ "world" ++ "!"
:: String = "hello world!"

-> """
   Multi-line strings can span multiple lines and
   don't require escapes for "quotation marks".
   """
:: String = "Multi-line strings can span multiple lines and\ndon't require escapes for \"quotation marks\"."

-- The dollar sign is used for string interpolation.
-> name = "world"
name :: String = "world"
-> print "hello, $name!"
hello, world!
-- Complex expressions can also be interpolated as long as they return strings.
-> print "hello, $(String.capitalize name)!"

Regular Expressions

Regular expressions have builtin support in the language. Fold's interpreter shows examples of matched strings.

-> /^[a-z0-9_.+-]+@[a-z0-9-]+\.[a-z]+$/
:: Regex = /^[a-z0-9_.+-]+@[a-z0-9-]+\.[a-z]+$/
-- Examples:
--   - "a@b.c"
--   - "r87.a9w_a+9@ak8-w.aoyz"

Unit ()

Unit, written as (), is a special value for expressions without value. Theoretically it can be seen as nullary tuple or a type with a singleton value (). It is similar to the void type found in imperative languages. Functions that perform side-effect, such as print return ().

-> print
print :: file: File? -> end: String? -> String -> ()

Definitions

-- Value bindings.
-> a = 42
a :: Int = 42
-> b = a
b :: Int = 42

-- Value bindings are immutable, multiple definitions of the same name shadow
-- the previous definition.
-> a = 0
a :: Int = 0
-> assert (b = 42) -- The value of [b] was not changed.

-- Mutable definitions.
-> var a = 42
a :: Ref Int
-- Reference values can be mutated.
-> a := 0
-> b = a
b :: Ref Int
-> a := 100
-- Mutable bindings need to be dereferenced:
assert (*a == *b == 100)

-- Bindings can be defined in a limited scope using `let`.
-> let
     a = 3 * 8,
     b = 4 + 2
   in
     a + b
:: Int = 30

-- The bindings can also follow the expression using `where`.
-> a + b
     where a = 3 * 8,
           b = 4 + 2
:: Int = 30

-- Monadic bindings.
-- Special syntax for monadic binding is supported with the `do!` notation.
-> number = do!
      str <- Console.read_line ()
      int <- Int.parse str
      return int
   end
number :: Int?

-- The previous code creates a monadic bind chain. If you are not familiar
-- with monads just ignore the word and see the next example.
-> number = do
      Console.read_line () >>= str ->
      Int.parse str >>= int ->
      return int
   end

-- In practice the handling of the optional values happens implicitly avoiding
-- the tedious repetitive checks.
-> number =
     match Console.read_line ()
     | Some str ->
       match Int.parse str
       | Some int -> Some int
       | None -> None
       end
     | None -> None
     end

Blocks

Multiple expressions can be grouped into blocks. Blocks evaluate to the value of the last expression.

The compiler will issue a warning if a block contains intermediate non-unit values, these should be ignored explicitly.

-> result = do
     a = 2
     b = 3
     a + b
   end
result :: Int = 5

Polymorphic Variants

-- The inferred type in this case is the symbol itself but in reality it is a union of all possible symbols.
-- This means that *a* in the following example can be either `ok or `no.
-> a = if (x > 4) `ok else `no end
a :: (`ok | `no)

Help Function

-> help
:: a -> ()
-> help help
Help function shows helpful comments for modules functions and types.
-> help List.map
List.map :: (a -> b) -> List a -> List b

List.map f [a1, ..., an] applies function f to [a1, ..., an],
and builds the list [f a1, ..., f an] with the results returned by [f].

List.map is tail-recursive.
List.map has O(n) complexity.

Examples

with (file mode: `w "/tmp/hello.txt") do f ->
  IO.read_line ()
  |> String.split by: (not << Char.is_alphanumeric)
  |> Iter.map String.capitalize
  |> Iter.join by: (a b -> a ++ " " ++ b)
  |> print to: f end: "!!!\n"
end

This code will create a new file for writing, read a line from the standard input, capitalize all the words, print the resulting string appending three exclamation marks and finally close the file.

Collections

-- Lists
-> [1, 2, 3, 4, 5]
:: List Int = [1, 2, 3, 4, 5]

-- Sets
-> {1, 2, 3, 4, 5, 4, 2}
:: Set Int = {1, 2, 3, 4, 5}

-- Vectors
-> Vec [1, 2, 3, 4, 5]
:: Vec Int = Vec [1, 2, 3, 4, 5]

-- Ranges
-> 0 .. 9
:: Range Int = 0 .. 9

-- Ranges also work with dates and other types.
-> Date "2016-01-01" .. Date "2016-01-31"
:: Range Date = 2016-01-01 .. 2016-01-31

-- Ranges up to a limit (excluding the limit) can be created with the `^` operator.
-> a = ^4
a :: Range Int = 0 .. 3

-- Lists can be created with ranges.
-> List (1 .. 5)
:: List Int = [1, 2, 3, 4, 5]

-- Get the first, the nth and the last element of the list.
-> List.first a    -- May return None if the list is empty.
:: Int? = Some 0
-> a # 3           -- Equivalent to `List.nth a`
:: Int? = Some 3
-> a # -1          -- Equivalent to `List.last a`
:: Int? = Some 4

-- Finding elements and indices.
-> 2 in? a
:: Bool = True

-- The `in` operator can also be used to test a list of elements.
-- For every element in `^5`, tell if it exists in `[0, 3, 4]`.
-> ^5 in [0, 3, 4]
:: [Bool] = [True, False, False, True, True]

-- Create pairs by zipping two lists.
-> zip (^5) (^5 in [0, 3, 4])
:: List (Int, Bool) =
     [(0, True),
      (1, False),
      (2, False),
      (3, True),
      (4, True)]

-- Lists support pattern matching.
-> head & tail = 1 .. 5
head :: Int = 1
tail :: List Int = [2, 3, 4, 5]

-> [1, 2, 3] ++ [4, 5, 6]
:: List Int = [1, 2, 3, 4, 5, 6]

-> [1, 2, 3, 4, 5, 6] \\ [1, 3, 5]
:: List Int = [2, 4, 6]

-- Add indices to all elements in `a`.
-> a = [10, 20, 30, 9]
-> zip (^ #a) a
:: List (Int, Int) = [
     [(0, 10),
      (1, 20),
      (2, 30),
      (3, 9)]

-- Find indices in `a` that exist in `b`.
-> a = [10, 20, 30, 9]
-> b = [2, 50, 10, 30]
-> select (a in? b) (^ #a)
:: List Int = [2, 3]

-- Numeric functions work with collections too.
-> [4, 2, 3] + [8, 5, 7]
:: List Int = [12, 7, 10]

-> [4, 2, 3] - [8, 5, 7]
:: List Int = [-4, -3, -4]

-> [4, 2, 3] * [8, 5, 7]
:: List Int = [32, 10, 21]


--
-- Tuples
--

-- Tuples are heterogeneous fixed-size data containers.
-> (1, 'a', "hello", 1..10)
:: (Int, Char, String, Range Int) = (1, 'a', "hello", 1..10)

-- Tuples can be used to bind multiple values at once. Also parenthesis are optional.
-> a, b = 42, 'λ'
a :: Int  = 42
b :: Char = 'λ'

-> a, head & tail = "Свобода", [1, 2, 3, 4]
a    :: String   = "Свобода"
head :: Int      = 1
tail :: List Int = [2, 3, 4]


--
-- Types
--

-- Type annotations can be used anywhere, even in-lined with expressions.
-> "Hello " :: String + "World!"
 = "Hello World!" :: String

--
-- Records
--

-- A record is a tuple with labeled values.
-> a = { x = 2, y = 3 }
:: { x :: int, y :: int } = { x = 2, y = 3 }

-- The previous record was an example of an anonymous record type.
-- The record can be defined as a type first.
-> type Point = { x :: int, y :: int }
-> p = { x = 2, y = 3 }
:: Point = { x = 2, y = 3 }

-- Record update syntax: change the value of one of the fields.
-> { p with x = p.x + 100 }

--
-- Functions
--

-- Function calls start with a function name and arguments separated with spaces.
-> print "Hello"
"Hello"

-> sum 2 (1 + 1)
:: Int = 4

-- Function application has precedence over operators.
-> sum 1 1 * 2    -- Will be parsed as `(sum 1 1) * 2`.
:: Int = 4

-- Lambda functions are created with `->` operator.
-> n -> n * n
:: Int -> Int
-> (n -> n * n) 2
:: Int = 4
-> sum = x y -> x + y
:: Int -> Int -> Int

-- A function that computes the average of a list of integers.
-> avg = xs -> reduce (+) xs / #xs
:: List Int -> Float

-- Implicit lambda function:
-> 2 + \x      -- Is equivalent to `x -> 2 + x`
:: Int -> Int

-- Filter even numbers
-> List.filter (\n % 2 == 0) [1, 2, 3, 4, 5, 6]
:: List Int = [2, 4, 6]


--
-- Optionals
--

-- To express a possible absence (and therefore presence) of values, an
-- optional type can be used.
-> Some 42
:: Int? = Some 42

-> None
:: a? = None

-- Optional types end with an interrogation point `a?`.
-- To force an optional value the exclamation point can be used `a!`.
-- Warning: if the value is None an exception will be raised.

-> (Some 42)!
:: Int = 42
-> None!
 * NoValue (@32, 0/0): optional has no value

-> List.head
:: [a] -> a?
-> List.head ^10
:: Int? = Some 0
-> (List.head ^10)!
:: Int = 0

-- You can provide a default value in case the optional has no value.
-> 2 + List.head [] ? 0
:: Int = 2

-> List.map
List.map :: (a -> b) -> [a] -> [b]

-> map (1 + \x) (1 .. 5)

-- Pattern matching lambda.
-> hello = None -> "Hello World" | Some name ->  "Hello " + name
 = hello :: String? -> String

-> hello!

-- String interpolation is supported with `%` operator.
function hello name (lang = `en)
  print "%greeting, %(String.capitalize name)!"
    where greeting =
      case lang
        | `en -> "Hello"
        | `pt -> "Olá"
        | `ru -> "Привет"
      end
    end
end

hello :: String -> lang: (`en | `pt | `ru)? -> ()

-- Named keyword application.
-> hello "Alice" lang: "pt"
"Olá, Alice!"

-- Regular positional application can also be used if the types match.
-> hello "Alice" `en
"Hello, Alice!"

-- The order of the keyword arguments does not have to be the same as in
-- the definition. The remaining positional arguments will be applied by
-- the provided order.
-> map (hello lang: `pt) ["Bob", "Ana", "Tom"]
:: List String = ["Hello, Bob!", "Hello, Ana!", "Hello, Tom!"]

-> map (hello "Ana" \l) [`pt, `en, `ru]
:: List String = ["Olá, Bob!", "Hello, Ana!", "Привет, Tom!"]

-- Named keyword application with scoped variable.
-> lang = `en
-> hello "Alice" ~lang
"Hello, Alice!"

-- The previous call is equivalent to:
-> hello "Alice" lang: lang
"Hello, Alice!"

-- Naive recursive factorial implementation.
-> factorial = 0 -> 1
             | n -> n * factorial (n - 1)
factorial :: Int -> Int

-- Function composition and reverse application.
-> 1 .. 10 |> filter even |> reverse |> take 3
 = [6, 4, 2]

-> iterate (+ "a") "" |> take 5 |> tail |> filter (\s -> length s < 4)
 = ["a", "aa", "aaa"] :: string list

-- Operators are functions.

-> (+)
 = (+) :: {Num a} -> a -> a -> a

-> help (+)
 = (+) :: {Number a} -> a -> a -> a

   Produces a sum of two numerical values.
   The parameters of the sum function must implement the Number interface.

-> help help
 = help :: a -> a

   Help function.
   Acts as an identity function and prints expression docstring.


-- Sum all numbers in a list:
-> reduce (+) [1 .. 5]
 = 15 :: Int

-- Function definition.
-- Finds the maximum number in a list.
-> max list = reduce (a b -> if (a > b) then: a else: b) list
max :: List Int -> Int

-> max [2, 10, 3, 6, 9, 9]
:: Int = 10

-- `for` macro iterates on a collection without producing values.
-> for i <- (1 .. 3)
     print ("Number: " + (i + 2)::String)
   end

Number: 3
Number: 4
Number: 5

-> last = do
     | []     -> None
     | [x]    -> Some x
     | x & xs -> last xs
   end
last :: List a -> a?

-- Some HTML

-> div class: "menu-container"
    ul id: "menu" class: "default-menu":
        map (li class: "menu-item") ["Home" "Products" "Contacts"]
   end

-- `:` is a right associative function application operator, similar to Haskell's `$`.

--
-- Modules
--

-> interface Comparable a
     type Ordering = LT | EQ | GT
     compare :: a -> a -> Ordering
   end

-> module Comparable {int}
     compare x y =
         cond
           | x == y -> EQ
           | x >  y -> GT
           | x <  y -> LT
         end
   end

 -- Function definition with docstring.

-> "Quick-sort sorting algorithm."
   function quicksort list
     case list
       | [] -> []
       | x & xs -> head ++ [x] ++ tail
         where head = quicksort (filter (e ->      e < x)  xs)
               tail = quicksort (filter (e -> not (e < x)) xs)
     end
   end

quicksort :: {Ord a} -> List a -> List a


-- Print function definition.

-> print :: {Show a} -> [a] -> end: String -> sep: String -> Unit
-> macro print args... (end = "\n") (sep = " ")
     quote
       IO.write ((String.join (map show %args) with: %sep) + %end) to: IO.stdout
     end
   end

-> print "Hello" "World!" sep: ", "
Hello, World!


-- Function definition with meta-data.

function range (from start = 0) (to end) (by step = 1)
  start == end ? [] : (start & (range (start + step) end step))
end

range :: from: Int? -> to: Int -> by: Int? -> List Int

-> range from: 10 to: 20 by: 5
:: List Int = [10, 15, 20]

-> range to: 5
:: List Int = [0, 1, 2, 3, 4]
Clone this wiki locally