[erlang-questions] Parametric modules and Dialyzer
Fred Hebert
mononcqc@REDACTED
Tue Aug 14 14:40:55 CEST 2012
I really, really want to recommend option 5: not using parametrized modules.
Dialyzer is the first of your concerns. Next up, you'll be having
trouble with code tracing, which is also messed up due to the arity
issues. Then what about function guards?
There are a few valid use cases for parametrized modules, and Richard
Carlsson can demonstrate them well, but I would generally advise against
it (a lot of it is personal preferences, I got to admit). Shadowing,
messed up arities, different semantics, etc. all annoy me a bit.
Here's a quick list of the problems, based on the following module:
-module(pmod,[Arg]).
-compile(export_all).
f() -> Arg.
Using it in a shell started as:
1> c(pmod).
{ok,pmod}
2> M = pmod:new(hello).
{pmod,hello}
1. function building is broken:
3> fun M:f/0.
** exception error: bad argument
in function erlang:make_fun/3
called as erlang:make_fun({pmod,hello},f,0)
You can't get the above to work without either forgetting about
parametrized modules (passing the argument to the function) or wrapping
it in an anonymous function as fun() -> M:f() end, although this one has
the problem of potentially being purged during code reload, which a
proper pmod anonymous function wouldn't.
2. Tracing is broken due to changes in arity:
4> dbg:tp({M, f, 0}, []).
** exception error: no case clause matching {pmod,hello}
in function dbg:do_tp/3 (dbg.erl, line 141)
5> dbg:tp({pmod, f, 0}, []).
{ok,[]}
6> dbg:tp({pmod, f, 1}, []).
{ok,[]}
The first one plainly doesn't work, the second one will never match
anything, and the third one is actually the valid one.
3. Tricky pattern matching.
If I add the following function definition to the pmod module:
id(Arg) -> Arg.
And then try to use it, see what can happen:
7> c(pmod).
{ok,pmod}
8> pmod:id(4).
** exception error: undefined function pmod:id/1
9> M:id(4).
** exception error: no function clause matching
pmod:id(4,{pmod,hello}) (pmod.erl, line 6)
10> M:id(hello).
hello
The way you name the variable will impact pattern matching. Although it
*is* likely the best behaviour available, it is weird to have this
behaviour happening, you need to watch yourself. This kind of stuff
won't happen if you use pmods with great care, but I'm a pessimist on
these questions.
Related to this, this isn't the most intuitive kind of error to debug in
the middle of a larger file. If you add:
y() -> fun(Arg) -> Arg end.
You get compiler warnings telling you /pmod.erl:8: Warning: variable
'Arg' shadowed in 'fun'/, still a scoping issue for the distracted
programmer. Imagine using the same variable in a case ... of match
without thinking of it. Some programmers get it wrong with the normal
scoping rules, nevermind with parametrized arguments on top of it.
4. They don't work well with tools like Dialyzer, as you found out.
Those are the ones I got from the top of my head. I know people will
defend Parametrized modules, that they have valid use cases (I can admit
to a few!), that a lot of these issues are implementation details that
can be fixed, but as of now, they aren't, and they mess stuff up.
Sorry, I know this isn't the kind of answer you want to such a question.
On 12-08-14 4:41 AM, VegaS wrote:
> Hello all!
>
> We are using parametric modules in our project and were quite happy
> with them unless we have started using Dialyzer.
> Dialyzer fails on parametric modules because functions in parametric
> modules are expanded with module params argument on compilation stage
> - so function's specs are unmatched.
>
> I know that parametric modules not the official part of Erlang, but
> I'd like to get both parametric modules and dialyzer working. I have
> tried to do some work on this:
> 1. Try to fix specs on parametric module's functions with
> parse_transform - simply extend specs on this stage, but this won't
> work, code just won't compile (because of specs for unknown functions)
> 2. More complicated approach - try to implement parametric modules
> with parse_transform (create "new" function and extend all module
> functions and specs). This looks working unless some code like
> SomeFun = local_fun/1, SomeFun(Arg) and some variations like this
> local function is assigned to record field.
> 3. Decided that there no way out besides patching OTP sources or not
> using parametric modules at all. After looking at OTP sources I have
> found, that parametric modules are expanded on on "expand_module"
> compiler pass. And on that stage specs are not expanded. I Have
> patched this stage with specs expand. All were correct, but dialyzer
> fails with message like specs were not fixed, so no luck... This
> happend because dialyzer using "abstract_code" from beam to extract
> specs and pass "save_abstract_code" is just before "expand_module" pass.
> Simple changing these passes order broke dialyzer at all.
> 4. The last attempt were to patch dialyzer - on the stage, when it
> extracts specs info from beam's abstract_code section. At this place I
> insert function's specs expanding with last parametric parameter - and
> it works!
>
> So the question is what the rigth way to make both parametric modules
> and dialyzer working?
> At my opinion there are no way except OTP sources patching - but what
> the rigth place for making such patch?
>
> I can attach my patch here but it is little ugly (I were tired by
> previous explorations) and I think I have chosen not rigth place for
> changing.
>
> Thanks,
> Lihanov Alexandr
>
>
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20120814/94599799/attachment.htm>
More information about the erlang-questions
mailing list