Disclaimer: I've worked around all these with various helpers (that I
really should package up into a library sometime...), but they are
significantly more verbose than if the AST was just regular to begin with,
so this is mostly bikeshedding as I can keep working that way, but these
are annoying irritations that bug me on a near daily basis.
On Tuesday, April 16, 2019 at 11:02:23 AM UTC-6, José Valim wrote:
>
> I am sorry but I cannot understand what you are talking about.
>
> If you are saying that having lists and tuples being literals are weird
> and that they should be regular ASTs (i.e. three elem tuples), then I
> recommend you to try doing those changes in an Elixir fork and see how it
> impacts the language.
>
Considering full forms still compile fine:
```elixir
iex(2)> {:{}, [], [{:__block__, [], [:a]}, 42]} |> Macro.to_string
"{:a, 42}"
```
Then the only thing it would really affect are the consumers of AST, which
would have a few effects that the major of which include though are not
limited to:
1. Matching AST nodes would be more simple as they are always 3-tuples of
the form of `{ASTNodeType, ContextMetadata, ASTNodeData}` instead of
needing to handle a multitude of different forms, especially the loose ones
such as `42`, which you have to match with a guard.
2. Taking keyword lists to macro functions would be more verbose, but a
simple helper such as `Macro.keywordify` or so could take constructs of, as
a pure example, something like `{:list, [], [{:tuple, [], [{:atom, [], "a"},
{:integer, [], 42}]}]}` and return `[a: 42]`, which makes handling
ast-based keyword lists for compile-time data trivial, it could even have
the aspect of expanding binding and other things using the `__CALLER__`'s
environment.
The first is a huge bonus, having the context/metadata information on every
single node is immensely useful in a variety of situations. Giving names
to node such as `{:atom, [], "a"}` and `{:integer, [], 42}` is both more
regular and standard (both great aspects for an AST) but is also safer for
those wanting to parse out user data (like say a language server parser),
while keeping distinct the difference between function calls (which only
has a list in the data field) and bindings (which only have contextual
atoms in the data field as far as I've seen so far, but then I'd argue that
those should be in an `{:binding, [], [{:atom, [], "a"}, {:atom, [],
"AContextAtom"}]}` setup as well, and thus function calls can be kept top
level like `{{:atom, [], "funcname"}, [], [{:utf8, [], "an argument"}]}`).
All of this would be *so* much a huge boon when I'm doing macro work as
well, so much code would be simplified, so much so that I often convert
Elixir's non-regular AST format into a regular format, so I convert things
like `42` into `{:__block__, [], [42]}` so I can attach metadata into the
middle field for later processing, all without needing to process it back
because elixir still consumes it all just fine, and this is a pretty
excessive pattern that I do just because all the loose 'stuff' is so
irritating to handle and that loose 'stuff' has lost all contextual
information, line number, column information if that exists, etc... etc...
etc... I can 'try' to rebuild it when I need but it is impossible to
absolutely and fully reproduce the original because the current AST format
is so lacking.
Compare this to the clang or OCaml AST's, both of which are entirely
regular as the equivalent of the 3-tuple of elixir's AST (although with
ADT's instead, which are conceptually tagged tuples anyway, I.E. like an
erlang record, though I *love* the simplicity of the 3-tuple with one field
being a keywordlist, or better yet a map, of the metadata, and the other
two fields being a tag, and the tag-specific data).
If I were designing Elixir's AST from scratch then it would be
significantly more regular with a number of changes that would make it a
lot easier to work with, but even as it is now it is pretty easy to make
regular (if not properly explicit because of hacks like `{:__block__, [], [
:a]}`, and that really is a hack) without any of this weird special casing
of primitives or 2-tuples or lists or so forth, all of which 'seems' (based
on usage in the elixir codebase itself) to be purely to make compile-time
keyword lists simple, which could easily be fixed with a single function to
do the transformation as-needed (like a `Macro.keywordize/2` function).
Right now macro's are pretty irritating to write because of all these
inconsistencies, and consuming AST for other purposes like language servers
or contextual highlighting is even more irritating.
--
You received this message because you are subscribed to the Google Groups
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/elixir-lang-core/8a5d61af-4daf-40e4-a025-67f1b8a76895%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.