Today I dusted off my Elixir compiler and made a couple of updates to Dialyxir that had long been asked for and the updates are released on hex.pm in version 0.3.5. I’ve seen posts about issues due to lack of these features so thought I might bring them to attention:
0.3.5 Features
* Option to include transitive dependencies (your entire dependency graph) in the PLT (plt_add_deps: :transitive)
* Check the PLT for required updates to existing dependencies when you run mix dialyzer.plt
Other notable changes in the last 6-7 months include improved umbrella project support (thanks vadim-moz), automatic compilation (seriously this took 2.5 years Jeremy?!) and the --halt-exit-status option (thanks to schnittchen) which will give you the return code from Dialyzer for CI tools.
0.4.0 and Dependency Changes
I also wanted to get some feedback on a specific question I have about potentially changing some default behaviors in the 0.4 release with regard to dependency configuration.
From the beginning it seemed obvious that configuration to control which OTP applications get built into the PLT is required - including them all would simply take too long and most projects only need a handful. I settled on a few specific ones that are commonly used and were suggested in a mailing list post, and added mechanisms to replace or add to this list.
I handled project dependencies the same way without giving it a whole lot of thought. I started this project in May 2013 (Elixir 0.8.2); and at that time most dependency graphs were quite shallow. The personal project I was working on at the time had only two dependencies outside of OTP - one of those was only macros.
I threw in the plt_add_deps option as an afterthought - if you specified a plt_add_deps: true it would add your mix project dependencies to the PLT when the dialyzer.plt task is run. After seeing some questions that come into the issue tracker, here and elsewhere I tend to think it should be actually be the default to do this.
I think one reason I did not make plt_add_deps true by default is that I’ve never been sure its a great idea to put project dependencies into a shared PLT file, and I’ve always been a fan of the shared PLT because it takes so long to build a base PLT with OTP and Elixir libraries. I’ve thought probably if you want to add your dependencies to the PLT, probably you should use the plt_file: “mypltfile.plt” to specific a private project PLT.
But fishcakez came up with a better solution to this two years ago; his library uses a shared core PLT but copies it to a local path (in _build) and then adds the project dependencies. So, I’m thinking I will implement this in the 0.4.0 release but would like some feedback. I’d also like to consider whether I should just go ahead and include transitive dependencies by default.
Transitive Dependency “Controversy”
My initial stance on transitive dependencies was that they don’t belong in your Elixir project code - if you plan to call a function in a module then you are treating it as a direct dependency, so its best to add it to your deps list.
The thing is, not everyone agrees. At least, the Phoenix project generator doesn’t agree. It has several transitive dependencies that are used in the generated code such as ecto and plug - these get pulled in by other dependencies when you build your deps but they are not in the deps list in the generated mix.exs. In order to successfully dialyze a newly generated Phoenix project you’ve got to track these down and add them to your project file, then run dialyzer.plt again. With today’s release now you can use plt_add_deps :transitive to add them all automatically, and it works great.
I think having a smooth experience for new Phoenix and Dialyzer users would be a good thing. If you could gen a Phoenix project, add Dialyxir to it, successfully generate the correct PLT and start benefiting straight away from static analysis without learning any more about the blizzard of Dialyxir configuration options I think there would be more adoption.
The only downside is a lot of projects do not need or expect transitive dependencies to be added to their PLT as it adds a lot of time. If I made transitive a default and you are using plt_add_deps: true then that won’t change - this would continue to use the :project configuration which would only include direct dependencies. But perhaps the default if you don’t have any plt_add_deps key in your configuration at all, should be to include transitive dependencies. This is what I need the most feedback on.
Any thoughts and suggestions on this or other related topics is most appreciated in this thread!