Got 15 mins to try LiveScript?

Try typing these out and see what happens (click to insert): fold (+), 0, [1 to 100]

						is-prime = (x) -> 
							x > 1 and all (x %), [2 to sqrt x]
						do -> [1 to 100] |> sum . filter is-prime
						
						html <- $.get window.location.href
						alert "This page has #{$ html .text!.split '' .length} characters!"
						

LiveScript is a language that compiles to JavaScript. It has many features that assist in functional programming. Comparing to JavaScript or CoffeeScript, LiveScript syntax is noise-less and more flexible.

Type next! to go to the next step.

Expressions

Almost everything in LiveScript is an expression:

2 * (10 + 27 / 3)

if 2 * 2 == 4 then "two times two is four" else "impossible"

true and false

String interpolation works with any expression.

						"This window's width is: #outer-width 
						 and this document's title is: #{document.query-selector "title" .text-content}"
						 
The language supports multi-line strings.

Type next! to go to the next step. or back! to go to the previous step.

Variables

We define a variable by x = 42 JavaScript var is not needed.

You can use dashes in the name of your variables and functions: planet-name = 'Saturn'. This is equivalent and will be compiled to planetName = 'Saturn';

Functions

Defining functions is very lightweight: add = (x, y) -> x + y

We don't need paranthesis for invoking functions: add 5, 18

You can also call functions infix using backticks: 5 `add` 18

Some examples:

double = (x) -> x * 2

double-small-number = (x) -> if x < 100 then x * 2 else x

hello = (name) -> "Hello #name"

The result of the last expression inside a function body will be returned as its output. You can use return to force returns earlier:

						fact = (n) ->
							return undefined if n < 1
							return 1 if n == 1
							n * fact n - 1
						

Lists

You can use list comprehension to produce new lists: [1 to 10] or [i * 2 for i in [1 to 10]]

Filtering: [i if odd i for i in [1 to 10]]

Nested comprehensions produce a flat list: [i * j for i in [1 to 10] for j in [-10 to -1]]

For example here is the list of all Pythagorian triples that are less than 10:

						[[a, b, c] for a in [1 to 10] 
							for b in [1 to 10] 
							for c in [1 to 10]
							when a*a + b*b == c*c 
						]
						

Pattern Matching

This is a tuple: color = ['white', 0xffffff]

You can use pattern matching to extract values from a tuple, list or an object.

						[name, hex] = color
						"Hexadecimal value for color #name is: 0x#{hex.to-string 16}"
						

This is a list: list = ['A', 'B', 'C', 'D', 'E', 'F']

You can use pattern matching on the lists: [head, ...rest] = ['A', 'B', 'C', 'D', 'E', 'F']

Destructuring Lists

Two lists can be concatenated using ++ operator (one-to-ten = [1,2,3,4,5] ++ [6,7,8,8,10]).

							[head, ...rest] = ['A', 'B', 'C', 'D', 'E', 'F']
							[head] ++ rest
						
[head] ++ rest is the original list.

Pattern matching is especially useful for self documenting the code.

Using everything we know so far and a little bit of recursion we can implement the all famous quicksort function:

						quicksort = ([x, ...xs]:list) ->
						    return [] if list.length == 0
						    smaller-sorted = quicksort [a for a in xs when a <= x]  
						    bigger-sorted  = quicksort [a for a in xs when a > x]  
						    smaller-sorted ++ [x] ++ bigger-sorted  
						
Try it out: quicksort [7, 91, 22, 5, 2, 1, 83]

Objects

Here's a simple object literal:

						saturn = 
							name: 'saturn'
							mass: 568
							orbital-period: 29
							distance-from-sum: 
								min: 9.05
								max: 10.12
						
Pattern matching also works on objets:
						{distance-from-sun: {min, max}} = saturn
						"Perihelion: #min AU, Aphelion: #max AU"
						

You can use pattern matching to import certain properties from a node module:

							{pi, sin} = require 'prelude-ls'
							sin pi/2
						
prelude.ls is LiveScript standard library, we are goting to explore it a little bit later.

Currying

A curried function can be called with less arguments that defined with, and then it will return a partially applied function. This means that it returns a function whose arguments are those which you didn't supply.

Curried functions are defined by long arrows -->, for example: add = (x, y) --> x + y, now add5 = add 5 is a function that takes a number and adds 5 to it: add5 20

Binary infix operators can also be partially applied: ten-times = (10 *)

devide-by-ten = (/ 10)

If the function takes only one parameter, you can ignore the parameter in arguments list and use the special keyword it to refernece the parameter in the body of the function: square = -> it * it

Currying is named after 20th century mathematician Haskell Curry.

Why currying?

We can truncate a decimal number to different precisions.

							truncate = (precision, i) --> 
								((i * 10**precision) - (i * 10**precision) % 1) / (10**precision) 
						
truncate takes two arguments: precision (that is the number of digits after decimal point) and the actual input that's the number that needs to be truncateed: truncate 2, pi

Now we can easily create different versions of the truncate function:

truncate0 = truncate 0 that returns a whole number.

truncate1 = truncate 1 that truncates with one decimal point; etc.

Now take a look at this example that returns a list of numbers that are π truncateed with different precisions:

[0 to 6].map(-> truncate it).map(-> it pi)
OK what's going on here? Let's break this expression down to understand it:

[0 to 6] creates a list of numbers from 0 to 6.

[0 to 6].map(-> truncate it) creates a list of curried truncate functions. Each item in this list is the truncate function with a different precision. For example [0 to 6].map(-> truncate it)[2] == truncate 2

map(-> it pi) applies every curried truncate functions to π.

Note that in each step it refers to one element of the list that was produced in the previous step. it in [0 to 6].map(-> truncate it) is a number (0 to 6) and it in map(-> it pi) is a function.

Once we get used to LiveScript and its standard library (prelude.ls), we will omit these its using the curried form of map from prelude.ls:

[0 to 6] |> map truncate |> map (<| pi)

Function Invocation

Here's a function that takes no argument and always returns 5: five = -> 5. Invoke this function by: five!

Use do to invoke an anonymous closure automatically:

						do ->
							area = screen.width * screen.height
							alert "Your screen area is #area pixels"
						
Note the scope of area variable: it is not accesible outside the anonynous closure.

This is a colsure that remembers the number of times that it was invoked:

						say-hello = do ->
							i = 0
							(greetings) ->
								i := i + 1
								"#i - Hello #{greetings}"
						
We use := to modify a variable that is defined in the outer scope.

Function Composition

Composing allows you to create new functions by composing them out of a series of functions. If f and g are two functions then f . g or f << g is equivalent to f(g(x)) and f >> g is equivalent to g(f(x))

For example, given: f = (3 *) and g = (10 +)

(f << g) 5 is equivalent to f(g(5)) or (3 *)(10 + 5) = (3 *) 15 = 45

(f >> g) 5 is equivalent to g(f(5)) or (10 +)(3 * 5) = (10 +) 15 = 25

For an example take the length function: length = (s) -> s.length that returns the length of its input string. We can write its type with this notation:

length :: String -> Number

And odd function that returns true if its input is an odd number: odd = (i) -> i % 2 == 1; its type is:

odd :: Number -> Boolean

Now the type of is-length-odd = (length >> odd) is:

is-length-odd :: String -> Boolean
Its input is the input of the first function and its output is the output of the second function.

Generally:

						f        :: a -> b
						g        :: b -> c
						(f >> g) :: a -> c
						

Piping

We grew up learning that the arguments of a function are palced at its right side: Sin(θ); but in LiveScript we can pipe an argument to a function by |> from its left side:

[1 to 10] |> sum

<| evaluates its right side and pipes the result to the function at its left:

sum <| map sqrt, [1 to 10]

For example, the following snippet generates a list of 10 random integers between 0 and 100:

[1 to 10] |> map (-> Math.round <| Math.random! * 100)

We pipe the list of numbers between 1 to 10 to map function.

map invokes the anonynous function (-> Math.round <| Math.random! * 100) for each item in the list.

The function ignores its input (the number), produces a random floating point number between 0 and 100 (Math.random! * 100) and rounds it.

Backcalls

Backcalls allow you to unnest callbacks. They are defined using arrows pointed to the left.

The content length of this page:

html <- $.get window.location.href ; alert html.length

Using backcalls is a way for avoiding callback hell in serial asynchronous sequences:

						html <- $.get window.location.href
						$script = $ '<html/>' .html html .find "script[src*='libs/']:first"
						content <- $.get ($script .attr 'src')
						alert "The content size of the first external script is #{content.length}"
						

perlude.ls

prelude.ls is the recommended base library for LiveScript. It defines many utility functions for working with lists and objects. We've already seen map and filter:

[1 to 10] |> map (* 2) |> filter (> 10)
Here we are doubling each integer between 1 and 10 and then filtering the result for numbers that are greater than 10.

concat-map produces a flattened list; The below snippet produces a list of lists using map:

[1 to 5] |> map (i) -> [1 to i]
But it will produce a list of just numbers using concat-map:
[1 to 5] |> concat-map (i) -> [1 to i]

Zipping

zip zips two lists together! Producing a list of tuples:

						alphabet = [65 to 90] |> map String.from-char-code
						alphabet `zip` [1 to 26]
						

You can pipe the output of the previous zip to a filter function to only select the even letters in the alphabet:

						alphabet `zip` [1 to 26] |> (filter ([_, i]) -> even i) |> (map ([a, _]) -> a)
						
You might go fancier and use a shorter expression that does the same job:
						alphabet `zip` [1 to 26] |> filter (.1) >> even |> map (.0) 
						
(.1) >> even is a function composition. (.1) is a function that returns the second element of its input tuple (or array): (.1) [1, 2, 3]

Remember that alphabet `zip` [1 to 26] is a list of tuples like [['A', 1], ['B', 2], ...]. filter (.1) >> even applies (.1) >> even function on every element of this list. (.1) >> even takes a tuple in its input, extracts its second element, and checks if it's even: for example: (.1) >> even <| ['B', 2]

Folding

Fold has many names, JavaScript calls it reduce, C# programmers know it as aggregate. Fold is very powerful, every other function that operates on lists in prelude.ls can be written using folds.

For example here's how we sum all the elements inside an array:

						fold ((acc, a) -> acc + a), 0, [1 to 10]
						
fold takes three parameters:
  • A function that takes the accumulated result and the next element to be folded
  • The seed (initial value, must have the same type as the expected output)
  • List to be folded
In our type notation:
						fold :: ((acc, a) -> acc), acc, [a]
						

Since (+) is a function, we can shorten our sum expression to:

						fold (+), 0, [1 to 10]
						
So we can define sum function like:
						sum = fold (+), 0
						
sum takes an array, sums it elements and returns the sum: sum [1 to 100]

It takes some time to fully appreciate the power of fold, if you have not used functional programming before. prelude.ls comes with a tons of other utility functions!

Excited?

We just scratched the surface of LiveScript in this tutorial. Check out http://livescript.net for full documentation.

Cheers!