david_ex
I’m struggling with optional: true dependencies in one of my projects. This project will wrap an API (complete code here), and users will be able to provide their own functions/modules to handle the HTTP interaction and response body decoding if they wish. However, default implementations of these will ship with the library.
These default implementations depend on :hackney and Jason. Having learned about optional dependencies, they seemed like exactly what I was looking for (docs):
:optional- marks the dependency as optional. In such cases, the current project will always include the optional dependency but any other project that depends on the current project won’t be forced to use the optional dependency. However, if the other project includes the optional dependency on its own, the requirements and options specified here will also be applied.
(The file is here if you want to poke around.)
If I change
defp deps do
[
{:hackney, "~> 1.14", only: [:dev, :test]},
{:jason, "~> 1.1", only: [:dev, :test]},
...
]
end
to
defp deps do
[
{:hackney, "~> 1.14", optional: true},
{:jason, "~> 1.1", optional: true},
...
]
end
I get strange results. First, dialyzer (used via dialyxir) no longer runs successfully and I get these errors:
:0:unknown_function
Function Jason.decode/1 does not exist.
________________________________________________________________________________
:0:unknown_function
Function Jason.encode/2 does not exist.
________________________________________________________________________________
:0:unknown_function
Function :hackney.body/1 does not exist.
________________________________________________________________________________
:0:unknown_function
Function :hackney.request/5 does not exist.
________________________________________________________________________________
done (warnings were emitted)
Then, 2 tests using hackney fail, although others tests also using hackney still pass. The tests failing are these.
What’s going on?
Per my understanding of the docs, using an optional dependency shouldn’t change any of these results since the dependencies would be included in my project. They simply wouldn’t be forced on the users of my package, and would only make the version requirements apply if those dependencies were already included in their own project’s mix file.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
ericmj
I cannot reproduce these errors. When I set the hackney and jason dependencies as optional tests pass cleanly and I get no errors from dialyzer.
What Elixir version are you using? Try deleting the
_buildanddepsdirectories and do a cleanmix deps.getand retry.david_ex
Thanks for your help!
It seems my issues are caused by the
:hackneyand:jasonOTP apps not getting started when configured as optional dependencies. It appears the application inference introduced in Mix 1.4 isn’t handling this case correctly, as it can be fixed by ensuring:hackneyand:jasonare started (by adding them to:extra_applications). Is this a bug in Mix, or is there some special handling I should be doing for optional OTP apps that should get started if available?Reproduction
I have pushed a clean branch reproducing the issue.
Env info:
Steps to reproduce (when on commit e4a85376def2ca2bcb1671ea812714f83b02e132):
Here’s the root issue:
If I add
:hackneyto the:extra_applicationsin mix.exs, I can run the above mix example correctly and the tests also execute properly.If I then also add
:jasonto:extra_applications, the dialyzer errors also go away.ericmj
The issue here is that optional dependencies are not started by default (this may change in Elixir 1.8) so you need to start the, manually when running tests or doing local development.
The original reasoning for this behavior was that you wouldn’t be able to test how your application functioned without the dependencies if mix always started them.
david_ex
Is this documented somewhere, or would it be helpful for me to open a PR to add this info to the docs?
ericmj
No, it’s not documented afaict but the behavior is changed in Elixir master so we can only document on the v1.7 branch.
david_ex
I found this commit making the change on master. I assume when you said “this may change” in 1.8, it was in the spirit of “you can’t be sure until it’s released”, but that this change is likely to ship in 1.8?
Also I didn’t understand whether it would be helpful to open PR clarifying the docs for
:optionalin v 1.4 - 1.7, or if in your opinion it would just add noise?In any case, thanks a lot for your help with my issue!
ericmj
I said “may” because we have noticed that it causes issues when compiling mix projects from rebar3. Others may also rely on the original behavior so there is a chance we will roll it back.
yordisprieto
Ran into this issue. I had to remove the
optionalflag from Plug to make the dialyzer happy.Any idiomatic way to fix such a problem?
mtrudel
I’ve solved this by selectively starting extra applications as necessary based on
Mix.env():https://github.com/mtrudel/websock_adapter/blob/main/mix.exs#L25-L29
mtrudel
An even better solution to this is to use the
plt_add_appsarg to dialyzer in yourmix.exsfile:See Use `plt_add_apps` arg to dialyzer to bring cowboy into analysis · phoenixframework/websock_adapter@507229d · GitHub for an example!