Some quick tests:
defmodule TypeSpecDemo do
@moduledoc """
Documentation for TypeSpecDemo.
"""
@spec hello([bar: String.t, baaz: String.t]) :: {:world, list}
def hello(opts \\ []) do
{:world, opts}
end
# correct usage
def default_to_empty_list, do: hello()
def call_with_empty_list, do: hello([])
def first_key_only, do: hello(bar: "bar")
def second_key_only, do: hello([baaz: "baaz"])
def both_keys_in_order, do: hello([bar: "bar", baaz: "baaz"])
def both_keys_reversed, do: hello([baaz: "baaz", bar: "bar"])
# incorrect usage
def bad_arg, do: hello("world")
def unknown_key, do: hello(foo: "foo")
def wrong_value, do: hello(baaz: 15)
end
Setting up according to instructions for Dialyxir, and then running mix dialyzer
, gives me the following output:
Compiling 1 file (.ex)
Checking PLT...
[:compiler, :elixir, :kernel, :logger, :stdlib]
PLT is up to date!
Starting Dialyzer
dialyzer --no_check_plt --plt /home/johwar/src/type_spec_demo/_build/dev/dialyxir_erlang-18.2.1_elixir-1.4.0-rc.1_deps-dev.plt /home/johwar/src/type_spec_demo/_build/dev/lib/type_spec_demo/ebin
Proceeding with analysis...
type_spec_demo.ex:21: Function bad_arg/0 has no local return
type_spec_demo.ex:21: The call 'Elixir.TypeSpecDemo':hello(<<_:40>>) breaks the contract ([{'bar','Elixir.String':t()} | {'baaz','Elixir.String':t()}]) -> {'world',[any()]}
type_spec_demo.ex:22: Function unknown_key/0 has no local return
type_spec_demo.ex:22: The call 'Elixir.TypeSpecDemo':hello([{'foo',<<_:24>>},...]) breaks the contract ([{'bar','Elixir.String':t()} | {'baaz','Elixir.String':t()}]) -> {'world',[any()]}
type_spec_demo.ex:23: Function wrong_value/0 has no local return
type_spec_demo.ex:23: The call 'Elixir.TypeSpecDemo':hello([{'baaz',15},...]) breaks the contract ([{'bar','Elixir.String':t()} | {'baaz','Elixir.String':t()}]) -> {'world',[any()]}
done in 0m1.04s
done (warnings were emitted)
It seems that the shorter, documented syntax of [key1: type1, key2: type2]
actually means the same as [{:key1, type1} | {:key2, type2}]
. Order does not matter and an empty list is OK, but unknown keys are rejected.
Maybe there are situations where it’s useful to have a type for the individual allowed keyword entries I guess, like option
in our example, but until I run across that I’d probably use the shorter syntax.