Played with this a little during ElixirConf US 2019. Posting some findings 
here.

https://github.com/ericmj/ex2ms 
<https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fericmj%2Fex2ms&sa=D&sntz=1&usg=AFQjCNHv1uf1v1bVT7DLAteeFM_vUgRYEQ>
 works 
well today, but I wanted to see if I could take some of my defguard 
experience and build an implementation that deferred all expansion work to 
the Elixir compiler, to avoid hard-coded translations like these 
elixir-to-erl mappings 
<https://github.com/ericmj/ex2ms/blob/master/lib/ex2ms.ex#L7-L94>. I stole 
the actual AST to MS rewrite logic from ex2ms, but tried to leverage 
:elixir_expand.expand beforehand. I ran into a few challenges that have 
convinced me that first-class support for MS generation would require 
changes to the Elixir compiler and/or erlang to get the inclusion-into-core 
standards I want, mostly evidenced in my test project 
https://github.com/christhekeele/matcha via code comments or commented out 
tests (also stolen from ex2ms).

The test project's macros make three passes on the AST:

- expansion
- conversion
- validation


Expansion

The hard part of expanding Elixir code into matchspecs with the compiler is 
the "tracing problem", which can be best summarized as: trace matchspecs 
support function-like calls to drive the tracing harness, that don't 
actually have real-world compiled function analogs anywhere. This makes it 
difficult for a compiler to verify their correctness.

I started playing around with getting around this by defining :trace and 
:table modules with stub functions. The idea was to alias them into the AST 
before expansion, then strip the module from the call out afterwards. Not 
fully implemented in the test project yet. More on this in a bit.

The other stumbling block I ran into was expanding multiple clauses 
verbatim. I had to wrap the clauses provided to the macro in a fn block for 
the compiler to expand them, which prevents interpolating values anywhere 
into the matchspec via the ^ operator (the way ex2ms allows for).


Conversion

I kept the conversion logic from ex2ms, but assumed all (valid) remaining 
remote calls would have been expanded into :erlang or :trace/:table module 
references, after which I'd strip out the module call. This works more or 
less as expected.


Validation

I ran the results through :erlang.match_spec_test to detect issues at 
compile time rather than runtime, which seems to work fine.


Conclusion

Since full support for the ^ operator would require a new context or 
construct in the compiler, and since erlang does not provide authoritative 
information on what's valid in the matchspec grammar, I think the bespoke 
approach of ex2ms is the most satisfactory for now. I may find some time to 
contribute to it with better arity checking for trace functions, more 
helpful error messages, and an extra validation pass sometime--perhaps even 
before next ElixirConf.

Ideally I'd like to have concrete no-ops for the tracing functions within 
erlang, and some sort of predicate function in 
http://erlang.org/doc/man/erl_internal.html to handle validation of 
local/remote calls in matchspecs, before pursuing the handling of the pin 
operator. But that's more work than I'm willing to put in to this for now.

-- 
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/442565e3-1b96-44e7-867c-bc2d476656a8%40googlegroups.com.

Reply via email to