Skip to content

Instantly share code, notes, and snippets.

@homam
Last active August 29, 2015 14:12
Show Gist options
  • Select an option

  • Save homam/ef7aff50690d188273bd to your computer and use it in GitHub Desktop.

Select an option

Save homam/ef7aff50690d188273bd to your computer and use it in GitHub Desktop.
LiveScript in 30 minutes

Installation

$ npm install -g LiveScript

Start LiveScript REPL:

$ lsc -d

Syntax

Defining Functions

var double = function(x) {
	return x * 2
}

Functions are fundamental pieces of programming and math. But they're unnecessary verbose in JavaScript.

In LiveScript:

double = (x) -> x * 2

Invoking Functions

In JavaScript:

var twenty = double(10)

In LiveScript:

twenty = double 10

More examples:

evens = [1,2,3,4,5,6,7,8,9,10].filter(function(x) {
	return x % 2 == 0
});
ls> evens = [1 to 10].filter (x) -> x % 2 == 0
[2,4,6,8,10]

Expressions

doubleSmallNumber = function(n) {
	return (n < 100) ? n * 2 : n;
}
double-small-number = (n) -> if n < 100 then n * 2 else n

Arrays

Concatenating two arrays:

ls> [1,2,3,4] ++ [9,10,11,12]  
[1,2,3,4,9,10,11,12]

Empty arrays:

ls> [] ++ [1, 2, 3]
[1, 2, 3]
ls> head [1 to 10]
1

ls> tail [1 to 10]
[2,3,4,5,6,7,8,9,10]
ls> take 3, [5,4,3,2,1] 
[5,4,3]

ls> drop 3, [8,4,2,1,5,6]
[1,5,6]
ls> minimum [8,4,2,1,5,6]
1

ls> maximum [1,9,2,3,4]
9

There are many more functions on lists, including: sum, product, mean, union, intersection and many more. We'll take a look at more list function later.

List Comprehension

ls> [x * 2 for x in [1 to 10]]
[2,4,6,8,10,12,14,16,18,20]
ls> [x * x for x to 10 when (x `mod` 2) != 0]
[1,9,25,49,81]
ls> [x * y for x in [2,5,10] for y in [8,10,11] when x * y > 50]
[55,80,100,110]

Let's write a length function on lists:

ls> length = (xs) -> sum [1 for _ in xs]   
ls> length [100 to 150]
51

Zip

ls> numbers = [1, 2, 3, 4]
ls> words = ['One', 'Two', 'Three', 'Four']
ls> zip numbers, words
[[1,"One"],[2,"Two"],[3,"Three"],[4,"Four"]]

zip takes two lists and produces a list of pairs.

It's more intuitive to infix the zip function (it's zip after all):

ls> numbers `zip` words

Destructuring

ls> [head, ...rest] = ['A', 'B', 'C', 'D', 'E', 'F'] 
ls> [a, b, c, ...rest] = ['A', 'B', 'C', 'D', 'E', 'F'] 
ls> [head, ..., last] = ['A', 'B', 'C', 'D', 'E', 'F'] 
ls> user = {name: 'John', language: 'en', id: 31245}
ls> {language} = user
ls> language
"en"

Functions

Recursion

length = ([x, ...xs]:list) -> match list
	| [] => 0
	| _  => 1 + length xs

length [1 to 10]
sum = ([x, ...xs]:list) -> match list
	| [] => 0
	| _  => x + sum xs

sum [1 to 10]
minimum = ([x, ...xs]:list, min = x) -> match list
	| [] => min
	| _  => minimum xs, (Math.min x, min)

minimum [Math.random! for _ in [1 to 10]]

Parameter min has a default values that is equal to the first element of the list (x) if not provided. Note that in the recursive call we pass the current minimum as for min.

map = (f, [x, ...xs]:list) -> match list
	| [] => []
	| _  => [f x] ++ map f, xs

map (* 2), [1 to 10]
filter = (f, [x, ...xs]:list) -> match list
	| [] => []
	| _  => (if f x then [x] else []) ++ filter f, xs

filter odd, [1 to 10]
reverse = ([x, ...xs]:list) -> match list
	| [] => []
	| _  => (reverse xs) ++ [x]

reverse [1 to 10]
group-by = (f, [x, ...xs]:list, result = {}) -> match list
	| [] => result
	| _  =>  
		key = f x
		result[key] ?= [] # assign result[key] to empty array if it is not defined
		result[key].push x
		group-by f, xs, result

group-by (.length), ['one', 'two', 'three']

General pattern:

foldr = (aggregator, zero, [x, ...xs]:list) -> match list
	| [] => zero
	| _  => aggregator x, (foldr aggregator, zero, xs)
map = (f, xs) -> foldr ((a, acc) -> [f a] ++ acc), [], xs
filter = (f, xs) -> foldr ((a, acc) -> (if f a then [a] else []) ++ acc), [], xs
sum = (xs) -> foldr ((a, acc) -> a + acc), 0, xs
sum = (xs) -> foldr (+), 0, xs
fact = (n) ->
	return undefined if n < 1
	return 1 if n == 1
	n * fact n - 1 

Using match:

fact = (n) -> match n
	| (< 1) => undefined
	| 1 => 1
	| otherwise => n * fact n - 1 

Currying

ls> add = (x, y) --> x + y
ls> add5 = add 5
ls> add5 7 # == 12 ; add5 = (y) -> 5 + y

Eta Reduction

double = (x) -> x * 2
double = (* 2)
map ((x) -> double x), [1 to 10]
map (-> double it), [1 to 10]
map double, [1 to 10]

Function Composition

f = (x) -> sin x
g = (x) -> abs x

f . g # == (x) -> f (g x) == sin (abs x)

Eta reduced:

sin . abs = (x) -> sin (abs x)
sumf = (f, [x, ...xs]:list) -> match list
	  | empty => 0
	  | _     => (f x) + sumf f, xs
sumf = (f, xs) --> sum (map f, xs)
sumf = sum . map
sumf = sum << map
sumf = map >> sum
list = [{x: 2}, {x: 11}, {x: 13}, {x: 4}]
sumf (.x), list # == 30

By currying foldr function:

foldr = (aggregator, zero, [x, ...xs]:list) --> match list
	| empty      => zero
	| otherwise  => aggregator x, (foldr aggregator, zero, xs)
sum = (xs) -> foldr ((a, acc) -> a + acc), 0, xs
sum = (xs) -> foldr (+), 0, xs
sum = foldr (+), 0

Pipes

The argument of a function can be piped to it from left

2 |> double # == double 2

Or right:

double <| sin pi/2 # == double (sin pi/2) 
[1 to 10]
	|> map double
	|> sum
	|> (* -1)
# == -110

Church Numerals

zero  = (f, z) --> z
one   = (f, z) --> z |> f
two   = (f, z) --> z |> f . f
three = (f, z) --> z |> f . f . f
four  = (f, z) --> z |> f . f . f . f

Natural number n representation in Church numeral is equivalent to n application of a function (f) starting from (f z) for one.

church-to-int function converts a Church numeral to its integer representation:

church-to-int = (c) -> c (+ 1), 0

Try it out: church-to-int four

We can think of four as:

four  = (f, z) --> z |> f . f . f . f
four  = (f, z) --> f (f . f . f) z
four  = (f, z) --> f (z |> (f . f . f))
four  = (f, z) --> f (three f, z)

Similarly:

three  = (f, z) --> f (two,  f, z)
two    = (f, z) --> f (one,  f, z)
one    = (f, z) --> f (zero, f, z)

a We can abstract away function incr that takes a Church number c and produces its successor:

incr = (c) ->
	(f, z) --> f (c f, z)

incr applies f one more time to the value that was produced by c; hence incrementing the Church number.

It can be written in this form:

incr = (c) ->
	(f, z) --> z |> f . c f

c f is c times application of f

incr is a function that takes a Church numeral n and returns its successor that is also a Church numeral:

incr :: Church -> Church

Using incr we have can define our numerals this way:

zero  = (f, z) --> z
one   = incr zero
two   = incr one
three = incr two
four  = incr three

A Church numeral, whether it's zero, one or four or any other number, is a function with two parameters:

  1. A function (of type a -> a, for any type a)
  2. A default value (of type a that is the value of the zero).
Church :: (a -> a) -> a    -> a
two    =  (  f   ,    z )  -> f (f z)

Church numeral (for example two) applies f as many times as the value of the numeral (here two), each time passing the result of the previous application to f, starting from zero application of f (that is constant z).

We can define algebra using Church numerals:

add = (a, b) --> 
	(f, z) --> (b incr, a) f, z

First note the type of add function:

add :: Church -> Church -> Church

(f, z) --> (b incr, a) f, z is a new Church numeral. b incr, a: b is a Church numeral, hence a function of two parameters:

  1. incr that is a function of type a -> a, here a itself is a Church numeral (a :: Church)
  2. A default value (of type a or here Church) that is Church numeral b

(b incr, a) means: call incr function as many times as the value of b, starting with default value a.

If b is zero, (b incr, a) is a If b is one, (b incr, a) is incr a If b is two, (b incr, a) is incr (incr a)

We can define multiplication as:

mult = (a, b) -->
	(f, z) --> (b (add a), zero) f, z

In (b (add a), zero), b is a Church numeral that takes two parameters:

  1. (add a) that is a function from Church -> Church
  2. A default value zero

(b (add a), zero) applies add a b times, starting from add a, zero, hence it is equivalent to b × a.

Similarly:

pow = (a, b) -->
	(f, z) --> (b (mult a), one) f, z

Using eta rule, the definition of add, mult and pow can be reduced to:

add = (a, b) --> b incr, a

mult = (a, b) --> b (add a), zero

pow = (a, b) --> b (mult a), one

Let's give them a try:

church-to-int <| three `add` two
church-to-int <| (three `mult` two) `pow` (incr three)

Decrement

Begin with zero, and keep counting until n, but storing the last two numbers. Then when you get n, the previous one is its predecessor.

We first need a utility function suc which takes a tuple, ignores the first item in the tuple and returns a new tuple whose values are the second value of the input tuple and ints successor:

suc = ([_, b]) -> [b, incr b]

Now:

dec = (b) -> 
	[x, _] = b suc, [undefined, zero]
	x

Similar to add subtraction can be defined like this:

sub = (a, b) --> b dec, a

For example:

suc [undefined, one] |> map church-to-int
church-to-int <| dec four
church-to-int <| (four `pow` two) `sub` three

Combinators

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment