jeremyjh

jeremyjh

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!

Showing Posts 1 to 10

Hisako1337

Hisako1337

I’d love to check this out in my phoenix app. Could you provide a simple list of “getting started” on a current phoenix app (v1.2)? I am fairly new to dialyzer, and I remember jose tweeting that with 1.2 it should be a good experience now.

jeremyjh

jeremyjh OP

Sure, it is really straightforward.

Assuming you’ve just generated a new Phoenix project.

Edit your mix.exs:

In deps add:

{:dialyxir, "~> 0.3.5"}

In project add:

dialyzer: [plt_add_deps: :transitive]

At the command line run:

mix do deps.get, deps.compile, dialyzer.plt

Now you are all set, you can run mix dialyzer anytime you want to check your project.

except…

Currently there are some spurious warnings in the default project. At least some of these will be fixed upstream, but for now you need to add a couple annotations to get rid of them.

Example file names for a project named myapp:

In lib/myapp/repo.ex add the @dialyzer attribute as below:

defmodule Myapp.Repo do
  use Ecto.Repo, otp_app: :myapp
  @dialyzer {:nowarn_function, rollback: 1}
end

Similarly, add this attribute in web/gettext.ex:

  @dialyzer [{:nowarn_function, 'MACRO-dgettext': 3},
             {:nowarn_function, 'MACRO-dgettext': 4},
             {:nowarn_function, 'MACRO-dngettext': 5},
             {:nowarn_function, 'MACRO-dngettext': 6},
            ]

The default PageView module (which includes web/page/index.html.eex) will emit the warning:
index.html.eex:1: The pattern {'safe', _@2} can never match the type binary()

Add @dialyzer :no_match to your PageView to resolve it.

web/views/page_view.ex

defmodule Myapp.PageView do
  use Chat.Web, :view
  @dialyzer :no_match
end
Hisako1337

Hisako1337

thank you very much! you should copy this to the github readme or a wiki page and link to it - I can imagine that there will be more folks that want to try dialyzer explicitly in phoenix :slight_smile:

jeremyjh

jeremyjh OP

Good idea, I added a wiki page for it and put a plug for it in the Github readme.

sasajuric

sasajuric

Author of Elixir In Action

I believe this is a Phoenix 1.2 bug. Didn’t yet try with 1.3 to see if it’s fixed. We’re still using 1.2 and just add @dialyzer :no_match in the view module, and a brief comment.

jeremyjh

jeremyjh OP

Aha, thanks Sasa! I had tried that but included :no_match in a tuple, oops!

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Relatedly, can anyone clarify what differences exist between this library and dialyze ?

jeremyjh

jeremyjh OP

I think the main difference is that dialyze is controlled by command line switches, and dialyxir is driven more by configuration. dialyxir does take a couple of args, and you can pass any dialyzer arg and it gets passed on. dialyze is a single task and it will just do whatever is required to setup the PLT when you run it. When you are just doing analysis with dialyze you probably want to pass --no-check so that it doesn’t check for updated apps every time you analyze your project. With dialyxir you have to run the separate dialyzer.plt task to create/update/check the PLT. This lets it default to --no-check.

The implementations are also different; dialyze uses the erlang :dialyzer module to perform all operations. dialyxir builds a command-line string and shells out to dialyzer. It prints this string which is handy for debugging; several issues (usually user environment/project specific) that have been reported to the issue tracker I was able to figure out just by reading the command string it generated.

dialyxir was started first but probably lacked some features when dialyze came out. I think dialyze has had more sensible defaults, for example it has always included all direct dependencies in the PLT. I do not think it lets you change that though; dialyxir gives you full control of what goes in the PLT. The thing is I do not know if anyone wants that. dialyxir has lots of configuration and I have little idea of how much of it is used.

OvermindDL1

OvermindDL1

Instead of adding it to my 30+ *_view.ex modules I added it to the *.Web.view/1 with a TODO: to remind me to check if this has been fixed upstream yet and remove it if so. Much easier having it in one place, and it works. :slight_smile:

OvermindDL1

OvermindDL1

Found another for your wiki page, Phoenix.Presence is also apparently bugged as this code:

  use Phoenix.Presence, otp_app: :my_server, pubsub_server: MyServer.PubSub # Line 75

Generates this dialyzer issue:

help_presence.ex:75: Expression produces a value of type {'ok',pid()}, but this value is unmatched
help_presence.ex:75: The inferred return type of init/1 ({'ok',#{}}) has nothing in common with {'error',_} | {'ok',pid()}, which is the expected return type for the callback of 'Elixir.Phoenix.Presence' behaviour
help_presence.ex:75: The inferred return type of track/3 ({'error',_} | {'ok',binary()}) has nothing in common with 'ok', which is the expected return type for the callback of 'Elixir.Phoenix.Presence' behaviour
help_presence.ex:75: The inferred return type of track/4 ({'error',_} | {'ok',binary()}) has nothing in common with 'ok', which is the expected return type for the callback of 'Elixir.Phoenix.Presence' behaviour
help_presence.ex:75: The inferred return type of update/3 ({'error',_} | {'ok',binary()}) has nothing in common with 'ok', which is the expected return type for the callback of 'Elixir.Phoenix.Presence' behaviour
help_presence.ex:75: The inferred return type of update/4 ({'error',_} | {'ok',binary()}) has nothing in common with 'ok', which is the expected return type for the callback of 'Elixir.Phoenix.Presence' behaviour

And this fixes most of them:

  @dialyzer [
              {:nowarn_function, 'init': 1},
              {:nowarn_function, 'track': 3},
              {:nowarn_function, 'track': 4},
              {:nowarn_function, 'update': 3},
              {:nowarn_function, 'update': 4},
            ]

However it leaves this:

help_presence.ex:75: Expression produces a value of type {'ok',pid()}, but this value is unmatched

And I am not sure what is happening here, any help? Is this one or the others my bug instead?

Where Next? Top

Trending in Announcing Top

wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
fuelen
Hi all! I want to present a small library which provides a mix task for generating an Entity-Relationship Diagram for Ecto schemas. You...
New
anuaralfetahe
Hello Published a new library - ProcessHub! ProcessHub is a library designed to manage process distribution within the Elixir cluster. ...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mudasobwa
I am seeing a lot of aplications of Argumentum ad Vericundiam in software discussions. They do link some piece of writing and point us to...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
sergio
It’s not that it’s vocabulary is too advanced. It’s something worse. I get lost trying to follow even a paragraph written by Claude. It’...
New
sorenone
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews