Qqwy / elixir-type_check

TypeCheck: Fast and flexible runtime type-checking for your Elixir projects.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Think about if it might at all be possible to support `@type t :: %__MODULE__{}` even above a `defstruct`

Qqwy opened this issue · comments

With the older checking implementation, this 'sort of' worked but silently did something not fully according to Elixir's typespec rules.

With the new checking implementation of the last minor revision, struct types were broken (c.f. #78 ).

The fix (#82) uncovered a related issue however: It is very common to write something like

defmodule User do
  use TypeCheck

  @type! t :: %__MODULE__{name: String.t(), age: integer()}
  defstruct [:name, :age]
end

however this will currently fail with a compiler error.

What works, is:

defmodule User do
  use TypeCheck

  defstruct [:name, :age]
  @type! t :: %__MODULE__{name: String.t(), age: integer()}
end

but I believe most style guides prefer the former, so a lot of code in the wild will use the former.


I'm not sure what, if at all, we can change to accommodate this, but it needs some thought.

I think this may be related to an issue I was running into with Ecto. Using Ecto's schema macro automatically defines the struct, eg

schema "users" do
    field :name, :string
end

means I automatically get %User{} have the name field like: %User{name: nil} in the struct.

However, if I define

@type! t :: %__MODULE__{}
schema "users" do
    field :name, :string
end

I get an error telling me to move it below the defstruct. It works fine if I do

schema "users" do
    field :name, :string
end
@type! t :: %__MODULE__{}

Yes, this is indeed exactly the same situation.
Thanks for mentioning, because hopefully it will help other people that might encounter this while using Ecto. 😃

Are there any abilities to effectively move the position of the @type! during compilation (e.g. blindly move to the end of the module)?

🤔 I have a new idea to try out: Currently a struct is rewritten into a fixed_map-type exactly at the point where the type is constructed.

This requires the struct definition itself to be available at the time the datastructure containing the type-information is constructed.
Instead, it might be possible to introduce a new builtin type for structs, and defer the check to where the struct-type is used inside a type-check (that is, at compile-time in the implementation of TypeCheck.Protocols.ToCheck#to_check).

Interesting. Do you mean similar to this sort of thing?... #21
I've come around to the idea of a shorthand for declaring typed structs so maybe this kills two birds with one stone.

For now, I think that having defstruct! is probably enough.

A fix is definitely possible, but it will require quite a bit of work and I am not sure that it is worth the effort unless people are very frequently burnt/confused by this problem.

If it turns out to be a big problem in practice for people starting to use TypeCheck, then it is worth revisiting this issue.