-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
Environment
$ elixir --version
Erlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
Elixir 1.8.1 (compiled with Erlang/OTP 21)
Current behavior
Given the file xx.ex
defmodule XX do
defstruct [:field]
end
the following holds for XX
iex(2)> %XX{}
%XX{field: nil}
iex(3)> XX.__struct__()
%XX{field: nil}
iex(4)> struct(XX)
%XX{field: nil}
iex(5)> %XX{} == XX.__struct__()
true
but given the file yy.ex
defmodule YY do
defdelegate __struct__, to: XX
defdelegate __struct__(x), to: XX
end
the follwing happens for YY
iex(6)> %YY{}
%YY{field: nil}
iex(7)> YY.__struct__()
%XX{field: nil}
iex(8)> struct(YY)
%XX{field: nil}
iex(9)> %YY{} == YY.__struct__()
false
in which different idioms that are expected to be equivalent return inconsistent results.
Expected behavior
The expected behavior is for %Mod{}
to be equivalent to Mod.__struct__()
, consequently the function Kernel.SpecialForms.%/2
must be fixed to return the value of Mod.__struct__/0
(and of Mod.__struct__/1
as applicable).
The defdelegate
construct is working properly because it returns the result of XX.__struct__/{0,1}
as the result of YY.__struct__/{0,1}
, as expected for delegation.
This means that somehow Kernel.SpecialForms.%/2
is overwritting the result returned by YY.__struct__/{0,1}
, and it must not, and instead pass it "as is".
Note that fixing Kernel.SpecialForms.%/2
additionally solves the case when XX
is an Ecto schema. For example, given
defmodule XX do
use Ecto.Schema
schema "table" do
field :field
end
end
the following is happening now
iex(11)> %YY{}
%YY{__meta__: #Ecto.Schema.Metadata<:built, "table">, field: nil, id: nil}
iex(12)> %YY{} |> IO.inspect(structs: false, pretty: true)
%{
__meta__: %{
__struct__: Ecto.Schema.Metadata,
context: nil,
prefix: nil,
schema: XX,
source: "table",
state: :built
},
__struct__: YY,
field: nil,
id: nil
}
%YY{__meta__: #Ecto.Schema.Metadata<:built, "table">, field: nil, id: nil}
where the __meta__
refers to XX
, and not YY
.
The solution should be to not fix in Ecto also, but to fix in Kernel.SpecialForms.%/2
instead, as doing the latter solves all the reported cases.
Thanks