Elixir: #put_in function with structs raises UndefinedFunctionError (get_and_update), macro form is fine

Created on 30 Dec 2015  Â·  3Comments  Â·  Source: elixir-lang/elixir

The method put_in when used with structs raises an UndefinedFuntionError get_and_update. Using put_in on a map works fine, and using it in its macro form works fine.

A little setup:

defmodule BugReport do
  defstruct owner: %{}, details: "", severity: 1
end
defmodule Customer do
  defstruct name: "", company: ""
end

And reproducing it:

struct_example = %BugReport{
  owner: %Customer{ name: "Rufus the Dufus", company: "Int'l Conspiracy Corp"},
  details: "OMG",
  severity: 99999
}

map_example = %{
  owner: %{
    name: "Rufus the Dufus"
  }
}

map_example    = put_in(map_example, [:owner, :name], "Cory")
#=> map_example.owner.name == "Cory"

struct_example = put_in(struct_example.owner.name, "Cory") 
#=> struct_example.owner.name == "Cory"

struct_example = put_in(struct_example, [:owner, :name], "Cory")
** (UndefinedFunctionError) undefined function BugReport.get_and_update/3
         BugReport.get_and_update(%BugReport{details: "THE SSOFWAR EDOESNT EVEN WERK!!?!1111!!", owner: %Customer{company: "Int'l Conspiracy Corp", name: "Rufus the Dufus"}, severity: 99999}, :owner, #Function<17.51792600/1 in Kernel.get_and_update_in/3>)
(elixir) lib/kernel.ex:1721: Kernel.put_in/3

I am on the 1.2-rc1 branch

Most helpful comment

Another way to think about this is as follows:

put_in(struct_example.owner.name, "Cory")

translates to something like (pseudo-code):

put_in(struct_example, [Access.field(:owner), Access.field(:name)], "Cory")

while:

put_in(struct_example, [:owner, :name], "Cory")

translates to something like (pseudo-code):

put_in(struct_example, [Access.key(:owner), Access.key(:name)], "Cory")

They are different access rules. A struct has fields but it does not have keys generally speaking like a map or a keyword list would. Issue #4009 is about making all of this a bit more explicit.

All 3 comments

Yes because [:owner, :name] use Access which is not implemented for structs.

Another way to think about this is as follows:

put_in(struct_example.owner.name, "Cory")

translates to something like (pseudo-code):

put_in(struct_example, [Access.field(:owner), Access.field(:name)], "Cory")

while:

put_in(struct_example, [:owner, :name], "Cory")

translates to something like (pseudo-code):

put_in(struct_example, [Access.key(:owner), Access.key(:name)], "Cory")

They are different access rules. A struct has fields but it does not have keys generally speaking like a map or a keyword list would. Issue #4009 is about making all of this a bit more explicit.

Got it. Sorry working from an old version of the prag prog book but using a newer version of elixir!

Thanks for the link.

Sent from my iPhone

On Dec 29, 2015, at 4:06 PM, José Valim [email protected] wrote:

Another way to think about this is as follows:

put_in(struct_example.owner.name, "Cory")
translates to something like:

put_in(struct_example, [Access.field(:owner), Access.field(:name)], "Cory")
while:

put_in(struct_example, [Access.key(:owner), Access.key(:name)], "Cory")
They are different access rules. And a struct has fields but it does not have keys generally speaking like a map or a keyword list would. Issue #4009 is about making all of this a bit more explicit.

—
Reply to this email directly or view it on GitHub.

Was this page helpful?
0 / 5 - 0 ratings