Last active
August 2, 2017 12:06
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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