Is it possible to typecheck upon Struct creation?

Is there a way I could force type-checking during Struct creation instead of waiting for a method call to enforce a spec?

Here’s a bit of sample code:

r = %Range{first: 1, last: "ok"} # I would like this to explode
Enum.count(range)                # explodes here

# the following will explode immediately
r = 1.."ok"

For reference purposes, here are links to the code for Enum.count and Range:

While I don’t necessarily need Range itself to explode upon instantiation if I pass an invalid value, I would like to be able to enforce this kind of type safety on custom structs, so for example:

defmodule MyNum do
  defstruct first: 0, second: 0

  @typedoc """
      Type that represents Examples struct with :first as integer and :last as integer.
  """
  @type t :: %MyNum{first: integer, second: integer}
end

defmodule Adder do
  @spec add(MyNum.t) :: integer
  def add(my_num) do
    my_num.first + my_num.second
  end
end

Given the above code, I’d like num = %MyNum{ first: "ok" } to explode immediately rather than wait until I invoke a function (in this case, Adder.add/1)

I don’t think this is possible, at least not without handling the type checks yourself. What you could do instead is something like

def new(first, second) when is_integer(first) and is_integer(second),
  do: %MyNum{first: first, second: second}
def new(_, _),
  do: raise  ArgumentError, message: "Fields for MyNum must be integers"

Then you have MyNum.new(first, second) instead of %MyNum{first: first, second: second}.

At runtime a struct is really just a map that has a special __struct__ key that identifies it. The compiler can enforce required fields for structs that are created using the %Foo{} sytnax, and each struct gets an accompanying Foo.__struct__(%{} = map) function that will enforce the fields for runtime created structs. Other than that there is no other enforcement mechanisms available for structs (to my knowledge at least).

2 Likes

Not automatically. I make new functions on my structs for that purpose.

3 Likes