This article provides a brief overview of differences between Ela and Haskell. It can be used as a "quick start" guide for those who are familiar with Haskell.
Feature comparison

Ela 
Haskell 
Major programming paradigm 
Functional 
Functional 
Syntax 
ML style, layout based 
ML style, layout based 
Purity 
Impure i 
Pure 
Type system 
No type system, only dynamic typing 
Statically typed with type inference 
Type strength 
Strong 
Strong 
Type safety 
Safe 
Safe 
Evaluation strategy 
Strict by default, lazy by demand 
Lazy by default, string by demand 
Pattern matching 
Yes 
Yes 
Function definition by PM 
Yes 
Yes 
Algebraic types 
Yes 
Yes 
First class modules 
Yes 
No 
OOP support 
Through duck typing and modules 
Through existential types 
Monads 
Yes 
Yes 
Exceptions 
Yes 
Yes 
Curried functions 
Yes, all functions are curried 
Yes, all functions are curried 
Operators as functions 
Yes 
Yes 
Type classes 
Yes iii 
Yes 
i Actually Ela is a pure language. The language itself provides no support for mutation of state  no variables, mutable data structures, etc. However Ela doesn't restrict and/or control side effects in the code therefore mutation of state can be
easily implemented on a library level. For example, module `cell` adds a support for reference cells and module `console` provides a support for classical Cstyle console input/output.
iii Ela (as of 0.11) provides a support for single parameter type classes. However the implementation is quite different from Haskell as long as Ela is dynamically typed and the whole notion of type has a different (runtime) meaning in Ela.
Syntax overview
Global bindings
Both Ela and Haskell do not require any keywords (such as `let`) for global bindings:
Haskell:
x = 0
fib a b 0 = a
fib a b n = fib b (a + b) (n  1)
Ela:
x = 0
fib a b 0 = a
fib a b n = fib b (a + b) (n  1)
Additionally Ela supports attributes for bindings, e.g.:
sum # private
sum x y = x + y
In the example above the function `sum` will not be included in the module export list.
Local bindings
Both Ela and Haskell use `let` and `where` constructs for local bindings:
Haskell:
fib = fib2 0 1
where fib2 a b 0 = a
fib2 a b n = fib2 b (a + b) (n  1)
Ela:
fib = fib2 0 1
where fib2 a b 0 = a
fib2 a b n = fib2 b (a + b) (n  1)
Haskell:
fib = let fib2 a b 0 = a
fib2 a b n = fib2 b (a + b) (n  1)
in fib2 0 1
Ela:
fib = let fib2 a b 0 = a
fib2 a b n = fib2 b (a + b) (n  1)
in fib2 0 1
Prefix, infix and postfix
Haskell supports function declaration in infix and prefix forms. Ela supports declarations of functions in infix, prefix and postfix forms.
Haskell:
x `sum` y = x + y
sum x y = x + y
Ela:
x `sum` y = x + y
sum x y = x + y
x `negate` = x
Unary negation
Haskell uses a `` operator for unary negation. Ela standard library doesn't provide a prefix operator for unary negation (a `negate` function is used instead), however, a `` sign is a part of numeric literals.
Haskell:
x = 2
y = x
fun (2)
Ela:
x = 2
y = negate 2
fun 2
List construction and list pattern matching
By default Ela uses `::` operator for list construction. Haskell uses `:` operator.
Haskell:
xs = 1:2:3:[]
(y:ys) = xs
Ela:
xs = 1::2::3::[]
(y::ys) = xs
Note that `:` is also a standard operator in Ela, however it is used as an indexing operator (a Haskell equivalent is `!!`).
Algebraic data types
Both languages support them, however, Ela additionally provides an ability to declared open algebraic data types, which can be extended after declaration with additional constructors. Also syntax is different.
Haskell:
data Couple a = Foo a  Bar a
unbox (Foo a) = a
unbox (Bar a) = a
Ela:
type Couple = Foo a  Bar a //Closed type
unbox (Foo a) = a
unbox (Bar a) = a
opentype AnyNumber = Foo a  Bar a //Open type
data AnyNumber = Zoo a //Extension of AnyNumber type
Ela doesn't require to specify type variables on the lefthand side of a type definition. Type variables in constructor definitions are not used by Ela compiler (but can be queried at runtime). Also Ela supports constructors in prefix, infix and postfix
forms:
type Complex =
a :+ b
Partial operator application
Both languages support it.
Haskell:
div2 = (/2)
x = div2 10 5.0
Ela:
div2 = (/2)
x = div2 10 //5
Pattern matching
Haskell uses `case` expression, when Ela have `match` expression. Layout rules for both constructs are similar.
Haskell:
xs = 1:2:3:[]
res = case xs of
(x:xs) >x
[] >0
Ela:
xs = 1::2::3::[]
res = match xs with
x::xs = x
[] = 0
Guards
Both Haskell and Ela support guards in a similar way, however `else` clause in Ela is always mandatory.
Haskell:
x = case (1,2) of
(x,y)  x < y > x
 x < y > y
 otherwise > x + y
Ela:
x = match (1,2) with
(x,y)  x > y = x
 x < y = y
 else = x + y
Ranges
Both Ela and Haskell support ranges with similar syntax.
Haskell:
r1 = [1..] infinite range
r2 = [10,9..] infinite range
r3 = [1..10] finite range
r4 = [10,9..1] finite range
Ela:
r1 = [1..] //infinite range
r2 = [10,9..] //infinite range
r3 = [1..10] //finite range
r4 = [10,9..1] //finite range
Comprehensions
Both Ela and Haskell support comprehensions, but syntax in Ela is slightly different. Also comprehensions in Ela are strict by default.
Haskell:
xs = [x+y  x < [1..10], y < [10,9..1], x `mod` y == 0]
Ela:
xs = [x+y \\ x < [1..10], y < [10,9..1]  x % y == 0] //strict
xs' = [& x+y \\ x < [1..10], y < [10,9..1]  x % y == 0] //lazy
Nonstrict evaluation
In Haskell evaluation is nonstrict by default. In Ela one should explicitely mark a certain expression as lazy.
Haskell:
map2 f (x:xs) = f x : map2 f xs
map2 _ [] = []
cycle2 xs = xs ++ cycle xs
Ela:
map2 f (x::xs) = f x :: (& map2 f xs)
map2 _ [] = []
cycle2 xs = xs ++ (& cycle xs)
Function application and composition
Haskell uses `.` operator for function composition (right associative, with applicative order) and `$` operator for function application (right associative, with applicative order). By default Ela has four operators instead (similar to F#). These are forward
pipe `>`, backward pipe `<`, forward composition `>>` and backward composition `<<`. Backward pipe is fully equivalent to `$` and backward composition is equivalent to `.`. They are both right associative and use applicative order. (Remember,
that `.` is a different operator in Ela and is used specifically for "member access").
Haskell:
funk = (negate . abs)
funk x = negate $ abs $ x
Ela:
funk = negate << abs
funk x = negate < abs < x