
Elixir: Simple Object Orientation and charming syntax on top of Erlang - josevalim
https://github.com/josevalim/elixir
======
Stormbringer
From the article:

 _"- 13 x 10 % = > 130 "_

Good grief, I hope not! :D Unless there is some subtle difference in Erlang
between - 13 and -13 (one - might be the negation operator and the other might
be the subtraction operator for instance). Hopefully this is just a typo.

With respect to the atoms, since they are basically a bunch of constants that
never get dereferenced and so cannot be garbage collected, wouldn't a better
way to handle this be to create some kind of out-of-memory look up table,
preferably one with good caching of commonly used values?

My understanding of one of the core reasons that erlang is valuable is because
of its robustness in a multi-threaded environment. It would be a shame to
undermine that robustness by having it blow up because of too many global
constants.

~~~
jerf
"With respect to the atoms..."

The only sane choice here is for Elixir atoms to be Erlang atoms, in the
fullest sense of "to be", so that restriction is coming from Erlang, not
Elixir. Erlang does exactly what you say; the memory leak in question is when
you blow out that table. The Erlang language and runtime permit dynamic atom
construction, which is useful because atoms are also the way functions and
modules are referenced and it's nice to be able to dynamically compute such
things, but if you continuously generate new atoms, you'll run out of memory.

------
jerf
Comments:

I'm not sure of the virtue of rewriting the Erlang atom rules. apos-quote-atom
name-quote seems particular odd, since Erlang already accepts apos-atom name-
apos. Though if your grammar requires it, I'd understand. (Though it would
still be acceptable to just make it apos-delimited at all times, I think.)

You may actually want to consider deliberately _not_ including head and tail
methods on list, as it's usually a sign you're doing something wrong where you
should be pattern matching. I'm not saying this is a totally winning argument,
but you may want to think about it. (It's better to encourage good code
practices than make bad ones easy, and I think when designing languages or
even just APIs that's the better perspective to come from, rather than "number
of features".)

I'm not sure about the wisdom of making a super-special exception for empty
lists to be false. I know the functional heritage behind that, but Erlang is
not as list-centric as Lisp and that seems an invitation to more problems. I
actually like how strict Erlang is with its "true and false are the only
booleans" and if anything think you should be _strengthening_ that, not
weakening it.

In fact, in general, while I approve of the general idea of laying objects
over Erlang in a useful manner, I think you want to distinguish between
extending Erlang, fixing obviously broken things about Erlang (yes, thank you,
default UTF-8 strings-as-binaries!), and merely _tweaking_ Erlang. Tweaks are
very much to taste, and may be very much more expensive than you realize, both
in implementation quirks and the number of people you chase away from one of
the core groups you need to attract to have success, which is current Erlang
users.

In the case construct, IMHO the "match" clauses ought to be on their own
indentation, not matched to the case. Obviously cosmetic. You don't mention
whitespace sensitivity, so I assume you aren't using it.

On try and except: You may want to consider requiring that all exceptions be
objects, required to be inherited from a base class. Consider Python's
experience with exceptions and how they have had to slowly and painfully
deprecate strings as exceptions. Erlang exceptions would have to be marshalled
into a generic class; consider Perl's Error module for examples on how that
works. Also consider giving the user some control over what that automatic
marshalling is, be that setting the class or monkeypatching methods in or what
have you. Exceptions-as-objects are very powerful but that power dissipates
very quickly when you can't count on the exception being an object. (Also that
automatic Erlang error class should be a subclass of the top-level Exception,
it should not itself be that top-level exception. Subclassing is an "isa"
operation, so if the base-level Exception class is also the Erlang-error-
marshalling class, that's a claim that all exceptions is-a exception
marshalled from an Erlang-level error.)

String interpolation: String interpolation is _freaking dangerous_ and few
people treat it with the respect it deserves. String interpolation is
basically an invitation to write an escaping failure of some kind every time
you interpolate anything. (XSS, SQL injection, Javascript injection, all sorts
of things.) Strongly consider some way to make it easy for users to add easy-
to-use encoding functions as part of the interpolation specification, and
consider giving them some way to actually _always_ default to some level of
encoding and requiring an override to do raw interpolation. Or rigidly require
a specification of encoding function with every interpolation spec; I think it
is absolutely worth an extra character per interpolation point, giving the
incredible number of massive security holes interpolation causes. Actually
this has nothing to do with Elixir per se, this is almost universally a
problem, I can count on half-a-hand the number of times I've seen this done
even remotely safely.

inlist in the list comprehension is a grammar smell. You may want to consider
backing off the magic there, I'm not sure it's actually winning you anything.
Anywhere a language "hides" something from you is an abstraction that can
leak.

Method visibility is extremely, _extremely_ , almost unspeakably overrated. It
is not a core aspect of object orientation. If you've added that because you
passionately believe in it, ok, go for it. But if you've added it merely
because you think it's how OO is done, I _strongly_ recommend ripping it back
out. It will be a recurring source of pain for very dubious benefit.

One thing that was missing: I'm looking for an OO-based Erlang layer that
allows me to write one thing that matches the gen_server behavior, and then
allows me to subclass from there to obtain yet more things that are also
gen_servers. Perhaps you have that and chose not to show an example, but for
me any OO layer missing that feature is a non-starter. As a general principle,
work with OTP to the extent possible, don't create a parallel implementation,
which I'm just saying so I've explicitly said it.

Also, let me just be clear, this is all my opinion based on relatively rapidly
reading that document, but I thought it worth sharing.

~~~
josevalim
Thanks jerf for your feedback even though it was based on rapidly reading the
document. I will address a few things:

1) In general I am trying my best to not change the syntax for Erlang data
structures. Atoms were one of the few exceptions due to the grammar. I chose
to go with the lisp syntax (and similar to Ruby :symbol). But indeed I could
have chosen it to be apos-delimited at all times to be closer to Erlang, I
have to think more about it.

2) head and tail: good point. The reason they are there though, is if you need
to dynamically dispatch a method to retrieve the head or the tail. But in
general, I don't think we should limit the language to avoid people writing
bad code. We can better solve this problem with good docs (therefore I have
removed such bad examples from the README).

3) Empty lists being considered false: I was not sure at the beginning if they
should evaluate to false or not but after your feedback I have decided to
change so just false evaluates to false. Although in some cases it if feels
very weird that a proper nil is missing, for instance Object.__parent__
returning an empty list that evaluates to true feels weird. Maybe I should add
nil down the road.

4) "Merely tweaking Erlang" can you give examples where we may be merely
tweaking Erlang?

5) case/match: I borrowed the syntax for Ruby and yes, Elixir is not
whitespace sensitive. In Ruby, people indent case/match in both ways, I am
particularly fine with both.

6) try/catch: yes, I also would like exceptions to be objects (like Python and
Ruby). However, the fact I cannot easily have custom guards in Erlang makes
that considerably more complicated to implement. Maybe we can add it in the
future if more and more people start to use Elixir enough to justify putting a
lot of effort in complicated features.

7) String interpolation: I am not aware of such issues. If you could point to
more references or discuss it better, I would really appreciate it!

8) The inlist is because we need to make a difference between list and
binaries when there is an ambiguity. So what exactly do you propose? To always
be explicit here (using inlist or inbin)? Notice that this is an Erlang
limitation, the Erlang syntax uses <\- and <= to make a difference between
them both.

9) Method visibility: hrm, you are actually right. I could have started
without this feature and have added it if I eventually needed it. We need the
private methods (because they map to local not-exported methods in Erlang) but
protected methods could definitely be gone.

10) The GenServer example is not missing, it is in the examples/ folder (too
long for the README):

[https://github.com/josevalim/elixir/blob/master/examples/gen...](https://github.com/josevalim/elixir/blob/master/examples/gen_server.ex)

Elixir has also Code::Server (that stores your load paths) and ExUnit::Server
(that stores your test cases) that could be used as examples. GenServer in
Elixir is very OO'ish and I am particularly happy with the current
implementation after going through a few iterations.

Writing Elixir has been an extremely rewarding experience and reading your
feedback makes it even better. Thanks again for sharing and I hope you can
eventually use it and help us improve it more!

------
CountHackulus
This is really quite nice. Mixing objects with pattern matching is something
that I've always wanted and found lacking in many languages.

