Crowdhailer

Crowdhailer

Creator of Raxx

Help with dialyzer warning on typespec that includes anonymous function

I have the following small bind (or flat_map) implementation for error tuples.

@spec bind({:ok, a} | {:error, reason}, function(a) :: {:ok, b} | {:error, reason}) ::
        {:ok, b} | {:error, reason}
      when a: any, b: any, reason: term
def bind({:ok, value}, func) when is_function(func, 1), do: func.(value)
def bind({:error, reason}, _func), do: {:error, reason}

When I use this function as follows

@spec safe_div(integer, integer) :: {:ok, float} | {:error, :zero_division}
def safe_div(_, 0) do
  {:error, :zero_division}
end

def safe_div(a, b) do
  {:ok, a / b}
end

OK.bind({:ok, 4}, &safe_div(3, &1))

I see the following warning.

test/integration.ex:5:no_return
Function run/0 has no local return.
________________________________________________________________________________
Please file a bug in https://github.com/jeremyjh/dialyxir/issues with this message.

Failed to parse warning:
[{:"(", 1}, {:"{", 1}, {:atom_full, 1, '\'ok\''}, {:",", 1}, {:atom_part, 1, 'a'}, {:"}", 1}, {:|, 1}, {:"{", 1}, {:atom_full, 1, '\'error\''}, {:",", 1}, {:atom_part, 1, 'r'}, {:atom_part, 1, 'e'}, {:atom_part, 1, 'a'}, {:atom_part, 1, 's'}, {:atom_part, 1, 'o'}, {:atom_part, 1, 'n'}, {:"}", 1}, {:",", 1}, {:atom_part, 1, 'f'}, {:atom_part, 1, 'u'}, {:atom_part, 1, 'n'}, {:atom_part, 1, 'c'}, {:atom_part, 1, 't'}, {:atom_part, 1, 'i'}, {:atom_part, 1, 'o'}, {:atom_part, 1, 'n'}, {:"(", 1}, {:atom_part, 1, 'a'}, {:")", 1}, {:::, 1}, {:"{", 1}, {:atom_full, 1, '\'ok\''}, {:",", 1}, {:atom_part, 1, 'b'}, {:"}", 1}, {:|, 1}, {:"{", 1}, {:atom_full, 1, '\'error\''}, {:",", 1}, {:atom_part, 1, 'r'}, {:atom_part, 1, 'e'}, {:atom_part, 1, 'a'}, {:atom_part, 1, 's'}, {:atom_part, 1, 'o'}, {:atom_part, 1, 'n'}, {:"}", 1}, {:")", 1}, {:->, 1}, {:"{", ...}, {...}, ...]


Legacy warning:
test/integration.ex:12: The call 'Elixir.OK':bind({'ok', 4},fun((_) -> {'error','zero_division'} | {'ok',float()})) breaks the contract ({'ok',a} | {'error',reason},function(a)::{'ok',b} | {'error',reason}) -> {'ok',b} | {'error',reason} when a :: any(), b :: any(), reason :: term()
________________________________________________________________________________
test/integration.ex:42:unused_fun
Function fetch_key/2 will never be called.
________________________________________________________________________________
done (warnings were emitted)

I can’t make head or tail of it. Any help would be appreciated.
You can see the full code on this PR. Add remaining type specs by CrowdHailer · Pull Request #54 · CrowdHailer/OK · GitHub

First Post!

NobbZ

NobbZ

You have the function type wrong. I’ll try to take a closer look into it in a couple of minutes.

Most Liked

sasajuric

sasajuric

Author of Elixir In Action

To specify a lambda, you should use (arg1, arg2, ... -> result). With that change, the following typespec should be working:

@spec bind({:ok, a} | {:error, reason}, (a -> {:ok, b} | {:error, reason})) ::
          {:ok, b} | {:error, reason}
        when a: any, b: any, reason: term
NobbZ

NobbZ

Because you create a parameter named function(a) which shall have the type b, which again is an alias for any. And there is no value that contradicts any.

Last Post!

NobbZ

NobbZ

That all occurrences of a should have the same type is only a semantic and mostly of documentation also purpose. Dialyzer does not check if this is actually true.

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 49266 226
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 44778 311
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement