Skip to content

Instantly share code, notes, and snippets.

@rizo
Last active August 2, 2017 12:06
Show Gist options
  • Save rizo/a1aa8120a358d7348cf057a16ca3532c to your computer and use it in GitHub Desktop.
Save rizo/a1aa8120a358d7348cf057a16ca3532c to your computer and use it in GitHub Desktop.
A quick overview of Fold programming language (https://github.com/fold-lang)
-- This is a single line comment
{- This is a multi-line comment,
it can span multiple lines -}
{- Multi-line comments can be {- nested -} -}
-- This is a value binding.
val answer = 42
-- The compiler can infer types of values and even complex expressions, but
-- sometimes it is useful to add type annotations with the `::` operator.
val msg :: String = "Hello"
val num :: Float = Math.pi
-- Like all functional programming languages, a key feature of Fold is the
-- function. For instance, the factorial function can be defined as:
def factorial n =
if n == 0 then 1
else n * factorial (n - 1)
-- Being also a declarative language Fold can use pattern matching for control
-- flow. In the previous definition the if-expression can be replaced with
-- patterns for arguments separated by the alternative operator `|`. The
-- patterns are tried sequentially until a match is found.
def factorial 0 = 1
| factorial n = n * factorial (n - 1)
-- The patterns are not limited to arguments and can be used in the function
-- body with the `case` expression as shown here:
def hello name =
case name
| "" -> print "Hey stranger!"
| "world" -> print "Hello to the whole world!"
| other -> print "Hello, %s!" other
end
-- Pattern order is important, always sort the patterns from the most
-- specific to the most generic. Here is an example of a bad ordered case
-- expression:
def print_date date =
case date
| (yyyy, mm, dd) -> print "%04d-%02d-%02d" yyyy mm dd
| (2016, 02, 29) -> print "2016's leap day!"
end
-- The first pattern is too generic and will match all the inputs.
-- But don't worry, the compiler will warn you if you make this mistake.
-- Funcions in Fold are first-class, meaning that they are regular values and
-- can be passed as arguments, stored in lists or composed togather.
-- Here is a function that takes a number and returns its successor.
x -> x + 1
-- This function is anonymous, so to call it we need to write it again followed
-- by the argument.
val result = (x -> x + 1) 4
assert (result == 5)
-- Or alternatively give it a name:
val increment = x -> x + 1
assert (increment 9 == 10)
-- Regular function syntax is preferred:
def increment x = x + 1
-- This is a binary tree defined as a recursive union type. `|` introduces
-- alternate constructors to create a tree. Lower case letters denote generic
-- type parameters.
type Tree a =
| Leaf
| Node (Tree a, a, Tree a)
-- Let's create some trees.
-- Note that the names can contain the prime symbol '.
val tree'1 = Leaf
val tree'2 = Node (Leaf, 42, Leaf)
-- Sums all the values in a integer tree.
def sum Leaf = 0
| sum (Node (l, x, r)) = sum l + x + sum r
-- Automated testing is provided by `fold test` command which will execute all
-- the tests found in the project. To define a test create a function called
-- `test` providing a description as an argument.
def test "sum tree values" =
assert (sum Leaf == 0);
assert (sum (n'1, 10, n'2)) == 19
where
n'1 = Node (Leaf, 4, Leaf),
n'2 = Node (Leaf, 5, Leaf)
-- This functions searches for the maximum element in a list.
-- What is the maximum of an empty list? Fold denotes the absence of values
-- with optional types that need to be unrwaped explictitly before usage.
def max list :: List Int -> Int? =
case list
| [] -> None
| [x] -> Some x
| x & xs ->
let y = max xs in
Some (if x > y then x else y)
end
def test "max function" =
let
list'1 = [],
list'2 = [11, 3, 2, 8, 3, 9, 3],
list'3 = [1, 2, 3, 100]
in
assert (max list'1 == None);
assert (max list'2 == Some 11);
assert (max list'3 == Some 100)
-- Product type defining a 2D coordinate.
type Loc = (Float, Float)
-- This function will calculate the distance between two points.
def dist (x0, y0) (x1, y1) :: Loc -> Loc -> Float =
let
dx = x1 - x0,
dy = y1 - y0
in
Math.sqrt (dx * dx + dy * dy)
-- A data type is defined with the type keyword, as in:
type Shape =
| Circle (Loc, Float) -- center and radius
| Square (Loc, Float) -- upper-left corner and side length; axis-aligned
| Triangle (Loc, Loc, Loc) -- corners
-- Pattern exhaustiveness checking will make sure each case of the datatype has
-- been accounted for, and will produce a warning if not. The following pattern is
-- inexhaustive:
def center (Circle (c, _)) = c
| center (Square ((x, y), s)) = (x + s / 2.0, y + s / 2.0)
-- The set of clauses in the following function definition is exhaustive and
-- not redundant:
def has_corners (Circle _) = False
| has_corners _ = True
-- The pattern in the second clause of the following (meaningless) function is
-- redundant:
def f (Circle ((x, y), r)) = x + y
| f (Circle _) = 1.0
| f _ = 0.0
-- Functions can produce functions as return values:
def const k = _ -> k
-- Useful function application operator
def (x |> f) = f x
-- Using the application operator we can create pipelines.
-- Note: `x |> f |> g` is equivalent to `g (f x)`.
def sum_of_squared_odd_numbers upper =
(1 ...) |> map (n -> n * n) -- Squared
|> take_while (n -> n < upper) -- Below upper limit
|> filter is_odd -- That are odd
|> reduce xs (+) -- Sum them
-- The function List.map from the basis library is one of the most commonly
-- used higher-order functions in Fold:
def map _ [] = []
| map f [x, xs...] = [f x, map f xs...]
-- A better implementation of map would define a tail-recursive inner loop as follows:
def map f xs =
let loop ([], acc) = List.rev acc
| loop ([x, xs...], acc) = loop (xs, [f x, acc...]) in
loop (xs, [])
-- The exception system can be exploited to implement non-local exit, an
-- optimization technique suitable for functions like the following.
exception Zero
-- Product of all numbers in a list.
-- Optimizes the calculation by stopping when finds a zero.
def product numbers =
let loop [] = 1
| loop [0, ...] = raise Zero
| loop [head, tail...] = head * loop tail
in
try loop numbers with Zero -> 0 end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment