Lisp-Python vs. Erlang (was MLvtA, was Meta)

Bjorn Gustavsson bjorn@REDACTED
Fri Aug 25 14:12:59 CEST 2006


I find it convenient to import functions from the lists module,
rather than writing "lists:" before every call.

I learned that practice from Robert Virding.

/Bjorn

Sean Hinde <sean.hinde@REDACTED> writes:

> This is fun!
> 
> Here is my Erlang translation of the GPS from Norvig, following the
> lisp code as closely as possible.
> 
> I found it quite kludgy to write in erlang. I ended up using a
> deprecated function from lists.erl, and there were some features of
> lisp like named parameters and extension parameters that seemed quite
> nice - although perhaps not so much until Luke forcefully pointed out
> the benefits :-)
> 
> Heavy higher order function programming certainly seems less elegant
> in Erlang than lisp or any of the modern statically typed functional
> languages.
> 
> Sean
> 
> -module(gps).
> 
> -export([gps/3]).
> 
> -record(op,
>          {
>            action,
>            preconds = [],
>            add_list = [],
>            del_list = []
>           }).
> 
> gps(State, Goals, Ops) ->
>      put(state, State),
>      put(ops, Ops),
>      lists:all(fun achieve/1, Goals).
> 
> achieve(Goal) ->
>      State = get(state),
>      Ops = get(ops),
>      lists:member(Goal, State) orelse
>          lists:any(fun apply_op/1, find_all(Goal, Ops, fun
> appropriate_p/2)).
> 
> appropriate_p(Op, Goal) ->
>      lists:member(Goal, Op#op.add_list).
> 
> apply_op(Op) ->
>      case lists:all(fun achieve/1, Op#op.preconds) of
>          true ->
>              io:format("Executing ~p~n",[Op#op.action]),
>              put(state, get(state) -- Op#op.del_list),
>              put(state, merge(get(state), Op#op.add_list)),
>              true;
>          false ->
>              false
>      end.
> 
> merge(L1, L2) ->
>      lists:merge(lists:sort(L1), lists:sort(L2)).
> 
> find_all(Goal, Ops, Pred) ->
>      lists:filter(Pred, [Goal], Ops).
> 
> 
> 
> On 25 Aug 2006, at 03:05, Jay Nelson wrote:
> 
> > James Hague wrote:
> >
> > > But C# and Java aren't worth worrying about, IMO. The interesting
> > > stuff is happening in the scripting language world, especially with
> > > Python and Ruby. Those languages are a lot closer to Lisp
> > > (see http://www.norvig.com/python-lisp.html).
> >
> >
> > Here's my conversion of the Lisp / Python example Norvig gave:
> >
> > -------------------------------------------------
> >
> > -module(norvig).
> >
> > -export([generate/1, generate_tree/1]).
> >
> > % Constructions...
> > grammar(s)   -> [np, vp];
> > grammar(np)  -> [art, n];
> > grammar(vp)  -> [v, np];
> >
> > % Elements...
> > grammar(art) -> {"the", "a"};
> > grammar(n)   -> {"man", "ball", "woman", "table"};
> > grammar(v)   -> {"hit", "took", "saw", "liked"};
> >
> > % Unknown.
> > grammar(_)   -> none.
> >
> >
> >
> > generate(Phrase) when is_atom(Phrase) ->
> >   case grammar(Phrase) of
> >       none ->
> >           [Phrase];
> >       Words when is_tuple(Words) ->
> >           ChoiceNum = random:uniform(size(Words)),
> >           element(ChoiceNum, Words);
> >       Construct ->
> >           generate(Construct)
> >   end;
> >
> > generate(Phrase) ->
> >   [generate(Word) || Word <- Phrase].
> >
> >
> >
> > generate_tree(Phrase) when is_atom(Phrase) ->
> >   case grammar(Phrase) of
> >       none ->
> >           [Phrase];
> >       Words when is_tuple(Words) ->
> >           ChoiceNum = random:uniform(size(Words)),
> >           {Phrase, element(ChoiceNum, Words)};
> >       Construct ->
> >           [Phrase, generate_tree(Construct)]
> >   end;
> >
> > generate_tree(Phrase) ->
> >   [generate_tree(Word) || Word <- Phrase].
> >
> > ---------------------------------------------------
> >
> >
> > I deliberately left it similar to his so that people unfamiliar with
> > lisp can see the correspondence, but I don't like the repetition
> > of the generate functions.  I would prefer to use a vlad-macro
> > or an annotate function to collapse the 2nd and 3rd branches
> > of the case statement so that there is a single generate function
> > in source code with two variants.
> >
> > [This is an example of when a real macro is more readable than a
> > support function, because the two branches are different in
> > different ways, so the annotate function would have to have
> > two branches or there would have to be two annotate functions,
> > whereas a single macro could handle both cases.  As an exercise
> > to the reader, try writing the annotate approach and you'll see
> > the slight awkwardness.]
> >
> >
> > I think it reads clearer than lisp or python for the following
> > reasons:
> >
> > 1) Prolog-like patterns map to the problem domain as a grammar
> > function
> >     rather than the artificial list/hash map structure.  Also the
> > compiler
> >     can optimize it more easily so clarity plus speed.
> >
> > 2) The list comprehension is just a succinct joy.
> >
> > 3) The use of tuples and lists differentiates intent better.
> >
> > 4) I prefer the case ... of ... end style over (cond ( ) ...) now.
> >     Syntactically there is not much difference but erlang
> >     suggests using similar (and more readable) patterns
> >     in each branch of the case.
> >
> >
> > If I had a month or two to burn I would go through the exercise
> > of porting Norvig's AI code to erlang.  There's no reason why
> > it wouldn't all work -- and possibly discover some new parallel
> > approaches in the process.  I think erlang is a perfectly suited
> > to many problems that lisp was.
> >
> >
> > jay
> 

-- 
Björn Gustavsson, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list