Do error struct modules have their types specified somewhere?
I’m trying to use something like:
@spec my_fun(FunctionClauseError.t()) :: :ok
(I need to pass the actual error struct).
I could use:
@spec my_fun(%FunctionClauseError{}) :: :ok
but this makes Dialyzer ignore struct field types and additionally it creates a compile-time dependency.
Plain defstruct
s don’t have field types. You could make an Exception
-like struct using typed_struct
, but since the generated exception/1
uses Kernel.struct!/2
it could still produce nonsense without being detected by Dialyzer. For instance, I wouldn’t expect the usage code below to complain about types despite being clearly wrong:
defmodule FancyTypedException do
use TypedStruct
typedstruct do
field :__exception__, boolean(), default: true
field :integer_only, non_neg_integer()
field :message, String.t()
end
@behaviour Exception
@impl Exception
def message(exception) do
exception.message
end
@impl Exception
def exception(msg) when is_binary(msg) do
exception(message: msg)
end
def exception(args) when is_list(args) do
struct!(struct, args)
end
end
usage code:
raise FancyTypedException, message: "wat", integer_only: "nope"
This will result in a FancyTypedException
struct with a binary in integer_only
. 
Writing exception/1
with a struct literal might help, but IIRC the challenge is that exception/1
is called via Kernel.apply
at runtime so Dialyzer doesn’t have much visibility.
1 Like
Thanks, guys, but now I see that I may not have expressed clearly what I want to do.
I need to pass error structs that are in the standard library (so I don’t have control over them), e.g. FunctionClauseError: elixir/exception.ex at a64d42f5d3cb6c32752af9d3312897e8cd5bb7ec · elixir-lang/elixir · GitHub
The reason I need those types is that I’ve got functions that pass around those errors, and Credo will protest (since I’ve got Credo.Check.Warning.SpecWithStruct rule enabled).
@al2o3cr by “this makes Dialyzer ignore struct field types” I meant that in case of using %MyModule{} in typespec Dialyzer will only be able to check whether the arg is a map with field __struct__ = MyModule
, and that’s all. Using something like MyModule.t() it would check struct field types as well, provided there is a type definition for it, but standard library error modules don’t have those types specified apparently.
Btw long time no see Josh @polygonpusher 
IMO there are two separate situations here, with distinct solutions:
-
in the generic, user-defined case: define MyModule.t()
and use it. The Credo check may be useful for reducing compilation times
-
in the stdlib case: there isn’t any type information beyond “this is a map with __struct__: FunctionClauseError
”. The Credo check doesn’t make any sense, as stdlib is compiled already! Use %FunctionClauseError{}
and silence the check.
1 Like
You’re right, in this particular case, I should ignore the warning since the error module won’t be recompiled anyway. I guess I fixated on doing this “the right way”, since there may be situations in which I would build the error struct by hand to pass it, and I would like Dialyzer to be able to check the field types as well.
Thanks for your help!