Differences with Clojure
(This document was built using the one from ClojureScript as a guide).
Rationale
Erlang is a great language for building safe, reliable and scalable systems. It provides immutable, persistent data structures out of the box and its concurrency semantics are unequalled by any other language.
Clojure is a Lisp and as such comes with all the goodies Lisps provide. Apart from these Clojure also introduces powerful abstractions such as protocols, multimethods and seqs, to name a few.
Clojure was built to simplify the development of concurrent programs and some of its concurrency abstractions could be adapted to Erlang. It is fair to say that combining the power of the Erlang VM with the expressiveness of Clojure could provide an interesting, useful result to make the lives of many programmers simpler and make the world a happier place.
State and Identity
The Erlang VM implements the actor model, which means identity is generally bound to a process which maintains an internal state. This doesn't mean that the Clojure identity model can't be implemented, but since the platform favors the actor model, it will always be more efficient using it than the Clojure model.
clojure.spec
Not implemented (yet).
Dynamic Development
There is a REPL available just like Clojure's.
Functional Programming
Since the Erlang VM already includes native immutable data structures, Clojerl includes most of the same immutable data structures present in Clojure. Some of them are implemented based on other immutable data structures (e.g. sorted sets).
Lisp
Same as in Clojure.
Runtime Polymorphism
Protocols and multimethods are available as in Clojure. Everything is
implemented in terms of protocols, there are no interfaces. proxy is
not supported.
Concurrent Programming
TBD
Hosted on the JVM
Hosted on the Erlang VM.
Getting Started
The Reader
- Numbers
- All integer numbers are mapped to Erlang's representation, which are arbitrary precision integers.
- Numbers with a decimal part are mapped to Erlang's representation of floating point numbers.
- Ratio and BigDecimal are not supported.
- Characters are represented as single-character strings.
nilis currently mapped to the:undefinedkeyword. This is because:undefinedis generally used in Erlang to specify 'nothing/no-value'.trueandfalseare equivalent to:trueand:falserespectively.- Lists, Vectors, Maps and Sets are the same as in Clojure. There is no support yet for the Map namespace syntax.
- Macro characters
- Because there is no character type in Erlang,
\produces a single-character string.
- Because there is no character type in Erlang,
The REPL and main
- See Getting started for instructions on the Clojerl REPL.
- The
bin/clojerlandbin/cljescripts can be used the same asbin/clojureandbin/clj.
Evaluation
- Clojerl has the same evaluation rules as Clojure.
Special Forms
The following Clojerl special forms are identical to their
Clojure cousins: if, do, let, letfn, quote and loop.
fn- Compile to an Erlang function and therefore can't have any metadata.
recurrecuris compiled to an actual recursive call that is only allowed in tail position, since the Erlang VM implements tail call optimization.
def- When the init expression is a
fn,defproduces one or more Erlang functions, depending on the arities specified in thefndeclaration. - When not an
fn, the init expression (which is evaluated at compile-time) must return a constant literal, otherwise it's a compile-time error.
- When the init expression is a
throw- There is no error type in Erlang, any value can be thrown.
try..catch..finally- An exception in Erlang consists of three things: the class of the
exception (
throw,errororexit), the exit reason and the stack-trace. There is no specific type for exceptions. - The spec for the
catchclause is(catch error-type error & body), whereerror-typeis one of the keywords:throw,:error,:exitor_. The last one will catch all the error types.
- An exception in Erlang consists of three things: the class of the
exception (
var- Vars are not reified as in Clojure, they are more similar to what ClojureScript does, which returns compile time information about the var, and during runtime the information available is static (i.e. can't be modified without recompiling).
- Vars can't have watches or a validator.
- It's not possible to change the root binding of a Var.
monitor-enter,monitor-exit, andlockingare not supported.
Macros
Macros in Clojerl work the same as in Clojure.
Other Useful Functions and Macros
- Regex support is the one provided by the
reErlang module.
Data Structures
nil's type isclojerl.Nil. It is equivalent to:undefined.- Numbers
- All integers are Erlang integers (arbitrary precision).
- Strings are UTF-8 encoded Erlang binaries.
- Characters are single-char strings.
- Collections
- Clojerl uses the same hash computations as Clojure.
- When possible a native Erlang data structure is used (i.e. map,
list, tuple) in the underlying implementation.
- List uses an Erlang list.
- Vector uses
clj_vector, which is an Erlang implementation of Clojure's persistent vector. - Map uses an Erlang map.
- Sorted Maps use Robert Virding's implementation of red-black trees.
- Sets use Erlang maps.
- StructMap will not be implemented since "most uses of StructMaps would now be better served by records".
- ArrayMap's closest relative in Clojerl is TupleMap.
- Literal Erlang collections can be included in code by using the
#erldispatch reader macro:#erl ()Erlang lists#erl []Erlang tuples#erl {}Erlang maps
Datatypes
defprotocolanddeftype,extend-type,extend-protocolwork as in Clojure.- Protocols are not reified as in Clojure, there are no runtime protocol objects.
- Some reflective capabilities (
satisfies?) work as in Clojure. extendis not implemented.reifyis not implemented.
Sequences
- Seqs are the same as in Clojure.
Transient Data Structures
Transients are currently not implemeted.
Transducers
Stateful transducers are currently not implemeted.
Multimethods and Hierarchies
- Multimethods works as in Clojure.
- Hierarchies are not implemented.
Metadata
Works as in Clojure for all the same data structures, except for
fns.
Anonymous functions are compiled into Erlang closures, for which there is no way to bolt arbitrary information.
Namespaces
Namespaces are compiled into Erlang modules. They are available at runtime for inspection, as in Clojure.
Libs
Existing Clojure libs will have to conform to the Clojerl subset in order to work in Clojerl.
Vars and the Global Environment
def,bindingandset!work as in Clojuredefyields the Var itself as in Clojure.- Root bindings can't be modified (e.g. with
alter-var-rootorwith-redefs). - Vars can't have watches or a validator.
- Atom and Agent implementations are experimental.
- Refs are not currently implemented.
Refs and Transactions
Refs and Transactions are not implemented.
Agents
Agent implementation is experimental.
Atoms
Atom implementation is experimental.
Reducers
Reducers are not implemented.
Host Interop
- Member access
- All types in Clojerl have an implementation module with the same name as the type. "Member access" is translated to a function call to the specified function in the type's module where the first argument is the value.
- Type Hinting
- There are no primitive types in Erlang so there is no use for
int,ints, etc. - Type hints are used to resolve the target's type at compile-time and avoid having to figure out at run-time.
- There are no primitive types in Erlang so there is no use for
-
Calling Clojerl from Erlang
- Since Clojerl namespaces are Erlang modules, calling a function
from the
clojure.coremodule is as simple as:
'clojure.core':inc(1). %%= 2 - Since Clojerl namespaces are Erlang modules, calling a function
from the
Ahead-of-time Compilation and Class Generation
- Each namespace is generally compiled into one Erlang module, except
when
defrecord,deftypeor one of theextend-*functions is used. gen-class,gen-interface, etc. are unnecessary and unimplemented in Clojerl.
Other Included Libraries
clojure.erlang.iois an attempt to provide the same polymorphic I/O utility functions for Erlang.clojure.erlang.erldocs(Missing)clojure.erlang.shell(Missing)clojure.replclojure.setclojure.stringclojure.testclojure.walkclojure.xmlclojure.zipclojure.core.reducers(Missing)clojure.spec(Missing)clojure.pprint