points by cursork 12 years ago

100% agreed. I've been looking at Elixir occasionally for a year or so... It's getting better and better. It certainly takes a lot from Clojure, but with a real Erlang / OTP twist. (It also recognises that "homoiconicity" doesn't always mean lisp). Elixir's Design Goals are stated here for those who've missed them: http://elixir-lang.org/blog/2013/08/08/elixir-design-goals/

rvirding 12 years ago

Elixir isn't homoiconic, it never was, and they no longer make that claim.

  • cursork 12 years ago

    Kinda fair - I meant "homoiconic" in the loose sense of having macros that can manipulate expressions as data and which has mechanisms such as quote/unquote to do so.

    That's what many people see as the reason for homoiconicity after all. I put it in quotes, but maybe I should have said something about how languages can support sophisticated Lisp-like meta-programming without being a Lisp at all.

    Nonetheless, as I'm still learning (aren't we all?), I would love to hear what a truly homoiconic language has above something like Elixir with its quote, unquote and unquote_splicing. By truly homoiconic I mean that AST is equivalent to the written form.

    EDIT: I now realise you're not only rvirding, but that you wrote Lisp Flavoured Erlang https://github.com/rvirding/lfe so therefore (I hope) are in an even better position to educate!

    • rvirding 12 years ago

      I would say that one restriction with the type of macros that elixir has, irrespective of whether we call it homoiconic or not, is that they can only manipulate existing forms in the AST, they can't define new syntactic forms. In elixir this means that you basically work with function calls. There is syntactic support for making the function calls look less like function calls but the macros you define are basically function calls.

      In Lisp you are free to create completely new syntactic forms. Whether this is a feature of the homoiconicity of Lisp or of Lisp itself is another question as the Lisp syntax is very simple and everything basically has the same structure anyway. Some people say Lisp has no syntax.

      Trying to build the AST that directly in elixir is quite difficult as the AST is not trivial even if it relatively simple and regular. Quote and unquote in macros do a lot of the work for you. At one level you could do something similar for Erlang but no one has actually gone all the way with it. Erlang uses parse transforms for that. They are more powerful but much more complex to use.

      • alco 12 years ago

        I agree with all of the above.

        I would just like to add that in Elixir meta-programming does not always have to deal with macros.

        For example, this program

            somelist = ... # any kind of data,
                           # could be loaded from file
            defmodule M do
              for {name, value} <- somelist do
                def lookup(unquote(name)), do: unquote(value)
              end
            end
        

        generates a set of functions from arbitrary input. This feature alone greatly improves productivity in certain use cases.

        While it is true that Erlang's parse transforms are more powerful by default, nothing would stop a willing heart from implementing token-level macros for Elixir. It would be a separate program that would parse source code before passing it on to the Elixir compiler.

        But given the straightforwardness of code generation in Elixir (as exemplified by the code above), coming up with such a program might prove to be even easier for the user than dealing with parse transforms in Erlang. This is a speculation on my part, so it'd be curious to see if anyone goes in this direction in the future.

      • cursork 12 years ago

        Thanks for this; pretty much the response I was expecting but very well said.

        My opinion is that functions that take quoted code do actually do the 99% job of macros pretty well. I also think creating completely new syntactic forms is fantastic for a solo project; but not so great when working in a team. So I'm left thinking what difference does it actually make. My general feeling is that I tend to write macros in Clojure just to deal with someone else's macros; i.e. macros 'layer' whereas functions compose.*

        Incidentally, I've been curious about but never looked at Erlang's parse transforms because they have - in my mind at least - a big 'here be dragons' sign above them. Enough articles have warned me off to make me nervous. Would love to know more though.

        * No idea if that makes sense to people. Hope so.

        • alco 12 years ago

          Regarding your point about cleaning up other people's macros.

          In Elixir macros are imported into local scope only when the user explicitly asks for it. Most libraries provide a `__using___` macro that can modify current scope.

          For instance, `use ExUnit.Case` allows you to write tests like this

              test "my test case" do
                assert funcall() == "value"
              end
          

          instead of this

              require ExUnit.Case
              ExUnit.Case.test "my test case" do
                ExUnit.Assertions.assert funcall() == "value"
              end
          

          So it basically imports a bunch of stuff for you, but only when you call `use ExUnit.Case`.

          The consensus in the community right now is to not get carried away with macros and DSLs. Even if your library provides a DSL, it should be based on a functional API that is mostly usable without macros.

          And lastly, remember that `use`, `require`, and `import` modify only the current lexical scope. I wrote this macro[1] that replaces the built-in `|>` operator with a version that prints its output to the console. You can `use PipeInspect` inside one function and only that function's body will get the overriden implementation of `|>`.

          Cheers.

          [1]: https://gist.github.com/alco/9956215

          • cursork 12 years ago

            > The consensus in the community right now is to not get carried away with macros and DSLs. Even if your library provides a DSL, it should be based on a functional API that is mostly usable without macros.

            Yup. But what people say and what people do is often not the same thing :).

            • alco 12 years ago

              Sure. I just wanted to stress the point that macros are local in their scope. It's nothing like monkey-patching in Ruby where importing a module can change all divisions from floats to rational numbers.

        • rvirding 12 years ago

          Erlang parse transforms give you access to the full AST of a whole module and it must return the full AST of a module. It is like a macro which operates on a module not on a form. They are very powerful but easy to get messed up in.