From:
"andrew cooke" <andrew@...>
Date:
Sun, 15 Apr 2007 12:18:37 -0400 (CLT)
I have been playing around with Erlang this weekend.
While I haven't yet used the threading stuff I have written a parser
combinator library (although Erlang does include some kind of yacc-like
tool).
Some random observations:
- Apart from the message sending and infrastructure, it's a very simple,
pure functional language. There's no mutable state (no Monads, no mutable
references) except for (i) global process namespace (thanks to Kragen for
pointing this one out) and (ii) an integrated "object" database.
- The hot code replacement stuff makes working in the shell a pain (I
couldn't work out how to force recompiled code to load!), so the best
thing to do is use the emacs mode fully (which includes an embedded shell
and forces reload on recompile).
- Dependencies aren't recompiled automatically. There is a make-like
tool, but I haven't used it and don't know how well it's integrated with
the emacs edit/compile/run cycle.
- Error messages could be a little less cryptic. Syntax errors in
particular give you just the line number to work with.
- For a functional language the handling of higher order functions is very
clumsy. While functions can be invoked on arguments via the usual syntax:
foo(...)
module:foo(...)
they cannot be referred to as such:
lists:map(foo, [...]) % error
lists:map(module:foo, [...]) % error
instead you need to use a {module, name} tuple;
lists:map({module, foo}, [...])
And if an expression evaluates to a function it cannot be applied
directly. Instead, the whole thing must be enclosed in parens:
hof(...)(...) % error
(hof(...))(...)
- In general the docs are good. However I couldn't find anything that
described inline operators (++, --, arithmetic, others?). Partly because
these are difficult to search for...
- For some reason (strange compatibility with shell stadards) the regexp
(which is much more limited than perl5) will not match " *" against the
empty string(!).
- There are a couple of language extensions that seem a bit "ad-hoc" and
poorly integrated. One is support for records (named access to tuple
fields). The other is a kind of ADT (called "behaviours"). Don't have
more specific criticism of these yet as I haven't used them.
Andrew
Datalog Parser in Erlang
From:
"andrew cooke" <andrew@...>
Date:
Sun, 15 Apr 2007 15:18:23 -0400 (CLT)
It's probably not complete (although I'm not sure there is a formal def
for Datalog, it doesn't support extra conditions like equality and
certainly doesn't support negation), but here's an example of the parser
output:
Eshell V5.5.4 (abort with ^G)
1> parse_datalog:rule("forefather(X, Y) :- forefather(X, Z), father(Z, Y)").
{rule,{predicate,{name,"forefather"},[{var,"X"},{var,"Y"}]},
[{predicate,{name,"father"},[{var,"Z"},{var,"Y"}]},
{predicate,{name,"forefather"},[{var,"X"},{var,"Z"}]}]}
2> parse_datalog:fact("father(\"Rodney\", \"Andrew\")").
{fact,{name,"father"},[{name,"Rodney"},{name,"Andrew"}]}
And here's the code (just the top level, not the libraries):
-module(parse_datalog).
% testing only(?)
-export([space/0, space/1, list/1, list_tail/1, args/1]).
-export([var/0, var/1, name/0, name/1, fact/1, rule/1]).
-import(parse, [empty/0, label/2, function/2, either/2, cons/2, drop/1,
star/1, both/3, append/1, all/1, seq/1, result/2, export/3,
parser_lift/1, eos_/0]).
-import(parse_string, [string/1, regexp/1, quote/2]).
var() -> label(var, regexp("[A-Z][0-9a-zA-Z_]*")).
name() ->
label(name, either(regexp("[a-z][0-9a-zA-Z_]*"), quote("\\", "\""))).
% erlang lib fails to match empty string with " *"
space() -> drop(either(regexp(" *"), empty())).
space(Parser) -> append([space(), Parser, space()]).
open() -> drop(space(string("("))).
close() -> drop(space(string(")"))).
entail() -> drop(space(string(":-"))).
comma() -> drop(space(string(","))).
list_tail(Parser) -> seq([comma(), Parser]).
list(Parser) -> cons(Parser, star(list_tail(Parser))).
% ouch - list reversal needed here :o(
args(Parser) ->
(parser_lift({lists, reverse}))
(append([open(), list(Parser), close()])).
fact() -> both(fact, name(), args(name())).
predicate() -> both(predicate, name(), args(either(name(), var()))).
rule() -> both(rule, predicate(), append([entail(), list(predicate())])).
complete(Parser, Stream) ->
export(seq([Parser, space(), eos_()]), Stream, fail).
var(Stream) -> export(var(), Stream, fail).
name(Stream) -> export(name(), Stream, fail).
fact(Stream) -> complete(fact(), Stream).
rule(Stream) -> complete(rule(), Stream).
Re: Learning Erlang
From:
"Joe Armstrong" <joearms@...>
Date:
Mon, 16 Apr 2007 09:43:31 +0200
- The hot code replacement stuff makes working in the shell a pain (I
couldn't work out how to force recompiled code to load!), so the best
thing to do is use the emacs mode fully (which includes an embedded shell
and forces reload on recompile).
> c(foo).
{ok, foo}
foo is loaded and ready to run - you don't have to do anything
(well you might have to if foo was spawned by large number of parallel
process and
made funny tail calls that we can't handle - the point is that this
should just work
even when you have very large systems wih loads of different modules
involved
- Dependencies aren't recompiled automatically. There is a make-like
tool, but I haven't used it and don't know how well it's integrated with
the emacs edit/compile/run cycle.
Source code dependencies must be handled with something "external"
- I just use make - run-time dependencies are handled automatically.
ie if foo calls bar and you reload bar then all the calls to bar from
foo will be
changed to the new versions without you having to do anything to foo.
- For a functional language the handling of higher order functions is very
clumsy. While functions can be invoked on arguments via the usual syntax:
foo(...)
module:foo(...)
they cannot be referred to as such:
lists:map(foo, [...]) % error
lists:map(module:foo, [...]) % error
instead you need to use a {module, name} tuple;
lists:map({module, foo}, [...])
And if an expression evaluates to a function it cannot be applied
directly. Instead, the whole thing must be enclosed in parens:
hof(...)(...) % error
(hof(...))(...)
You need a fun keyword :-)
lists:map(fun foo/2, [...]) %% ok a reference to the local function foo
lists:map(fun remote:var/4, ...) %% a remote reference
Records are poorly integrated - we always said we know this sucks but
can anybody come
up with a better syntax - nobody has (well not quite true, there is a
better syntax but
compiling it is tricky)
Behaviours are much more than ASTs - think of them as HOFS working with parallel
processes to provide things like fault-tolerance or scalability
Erlang is not a functional language with "added concurrency" it's the other
way around.
The important thing is that Erlang is a concurrent language where you happen
to do things in a functional language.
It was not designed for an edit/compile/run cycle.
it was designed for startOnce/evovle ....
ie for never-stop evolving systems ( we have some systems that have
run for six years
and evolved thought dozens of major upgrades)
The "threading stuff" is what it's all about - this what makes Erlang
differ from
most other languages - Because our threading stuff does not mutate state we can
run on a multicore - with no locks and no sharing - so when the 100+ core CPUs
come our very slow and pure code will run much faster than all
these silly things that mutate state and crash each other quickly.
Cheers
/Joe Armstrong
Thanks for comment and apologies for ignoring/formatting
From:
"andrew cooke" <andrew@...>
Date:
Tue, 17 Apr 2007 11:52:43 -0400 (CLT)
Hi,
I just saw that you replied to something I posted on my blog about Erlang.
Thank-you for taking the time to do so. I was reading the page because I
was going to add an update myself, having found the "fun" keyword.
Sorry for the poor formatting and apparent attribution to me, not you. At
some point (maybe this weekend) I will fix the code (just some bash
scripts that put emails into web pages) and regenerate the site. It's not
a popular site - I think you are my second reply ever - and lacks a
certain polish, I'm afraid.
Also sorry if my comments came across as a criticism of Erlang without
mentioning the importance of concurrency. The reason I am using the
language is because I want the concurrency and I can't see how to do it
reasonably with any other approach. However I am just starting out so
thought it better to get a grip on the base language first.
Incidentally, I realise I am guilty of "not using the language as it was
designed to be used", but if I could add one thing it would be, at the
moment, not a nicer syntax for records, but currying. That and not (even)
needing "fun" for function references... :o)
Cheers,
Andrew
Thanks for comment and apologies for ignoring/formatting
From:
"andrew cooke" <andrew@...>
Date:
Wed, 18 Apr 2007 09:14:33 -0400 (CLT)
Hi,
I just saw that you replied to something I posted on my blog about Erlang.
Thank-you for taking the time to do so. I was reading the page because I
was going to add an update myself, having found the "fun" keyword.
Sorry for the poor formatting and apparent attribution to me, not you. At
some point (maybe this weekend) I will fix the code (just some bash
scripts that put emails into web pages) and regenerate the site. It's not
a popular site - I think you are my second reply ever - and lacks a
certain polish, I'm afraid.
Also sorry if my comments came across as a criticism of Erlang without
mentioning the importance of concurrency. The reason I am using the
language is because I want the concurrency and I can't see how to do it
reasonably with any other approach. However I am just starting out so
thought it better to get a grip on the base language first.
Incidentally, I realise I am guilty of "not using the language as it was
designed to be used", but if I could add one thing it would be, at the
moment, not a nicer syntax for records, but currying. That and not (even)
needing "fun" for function references... :o)
Cheers,
Andrew