Skip to content

Commit

Permalink
Sample code to prove for -callback
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Fitzpatrick committed Sep 15, 2015
1 parent adcc432 commit e3f94cf
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 0 deletions.
28 changes: 28 additions & 0 deletions README.md
@@ -0,0 +1,28 @@
### Does -callback work with Dialyzer?

I'm not so sure at this point. I've run the following using Erlang R15B02 (we're on an old version).
```bash
> dialyzer --build_plt --apps erts stdlib kernel
Compiling some key modules to native code... done in 0m17.77s
Creating PLT /Users/Ceryni/.dialyzer_plt ...
Unknown functions:
compile:file/2
compile:forms/2
compile:noenv_forms/2
compile:output_generated/1
crypto:des3_cbc_decrypt/5
crypto:start/0
Unknown types:
compile:option/0
done in 0m27.91s
done (passed successfully)
> dialyzer dude.erl runner.erl no_spec_dude.erl spec_dude.erl
Checking whether the PLT /Users/Ceryni/.dialyzer_plt is up-to-date... yes
Proceeding with analysis...
runner.erl:7: Function run/0 has no local return
runner.erl:9: The call spec_dude:hey_dude(42) breaks the contract (Say::string()) -> Res::string()
done in 0m0.31s
done (warnings were emitted)
```

So as you see, dialyzer issues a warning on line 9 but not line 8 as I would have expected. The `-spec` defined in `spec_dude.erl` is the same as the `-callback` defined on the behavior `dude.erl`. I'm not sure why but it seems like dialyzer is not applying the `-callback` to the implementing callback module `no_spec_dude.erl`.

This comment has been minimized.

Copy link
@aronisstav

aronisstav Sep 15, 2015

Indeed! -callback specifications are not automatically applied to behaviour implementations, hence the need for your -spec. What -callback specifications do is ensure that the implementation itself conforms to the specification described by the behaviour. If the no_spec_dude:hey_dude/1 was trying to add 1 to Say before printing it you would get the following:

no_spec_dude.erl:7: The inferred type for the 1st argument of hey_dude/1 (number()) is not a supertype of string(), which is expected type for this argument in the callback of the dude behaviour

Does this clear things up?

This comment has been minimized.

Copy link
@ivoryfitz

ivoryfitz Sep 15, 2015

Owner

Thanks for the quick reply! This does make perfect sense.

Is there a specific reason as to why the -callback is not extended to callers of the function? Or is there a way to make the implementation inherit the -callback directly?

This comment has been minimized.

Copy link
@aronisstav

aronisstav Sep 17, 2015

I think it should be possible, but would require extra bookkeeping in Dialyzer, hence it was not added. Unfortunately it is not possible to automate the inheritance of the callback either...

This comment has been minimized.

Copy link
@ivoryfitz

ivoryfitz Sep 17, 2015

Owner

Cool. Thanks for the information and answering my question! Maybe I'll take a look some other day, until then, -spec everywhere! XD

3 changes: 3 additions & 0 deletions dude.erl
@@ -0,0 +1,3 @@
-module(dude).

-callback hey_dude(Say :: string()) -> Res :: string().
8 changes: 8 additions & 0 deletions no_spec_dude.erl
@@ -0,0 +1,8 @@
-module(no_spec_dude).

-behaviour(dude).

-export([hey_dude/1]).

hey_dude(Say) ->
lists:flatten(io_lib:format("Hey Dude, ~p", [Say])).
9 changes: 9 additions & 0 deletions runner.erl
@@ -0,0 +1,9 @@
-module(runner).


-export([run/0]).

-spec run() -> ok.
run() ->
io:format("~p", [no_spec_dude:hey_dude(42)]),
io:format("~p", [spec_dude:hey_dude(42)]).
9 changes: 9 additions & 0 deletions spec_dude.erl
@@ -0,0 +1,9 @@
-module(spec_dude).

-behaviour(dude).

-export([hey_dude/1]).

-spec hey_dude(Say :: string()) -> Res :: string().
hey_dude(Say) ->
lists:flatten(io_lib:format("Hey Dude, ~p", [Say])).

0 comments on commit e3f94cf

Please sign in to comment.