Skip to content

Ela and Haskell

vorov2 edited this page Feb 26, 2020 · 1 revision

Introduction

This article provides a brief overview of differences between Ela and Haskell. It can be used as a 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 Pure 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 i Yes

i 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 declare 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 left-hand side of a type definition. Type variables in constructor definitions are not used by Ela compiler (but can be queried at run-time). 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

Non-strict evaluation

In Haskell evaluation is non-strict 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