Ookma-Kyi
Using a struct within a struct produces errors
I have the following code:
defmodule Servy.Conv do
defstruct method: "", path: "", resp_body: "", status: nil
def full_status(conv) do
"#{conv.status} #{status_reason(conv.status)}"
end
defp status_reason(code) do
%{
200 => "OK",
201 => "Created",
401 => "Unauthorized",
403 => "Forbidden",
404 => "Not Found",
500 => "Internal Server Error"
}[code]
end
end
I get a “could not find struct” error when I update the full_status function to def full_status(%Conv = conv) do, even though the struct is defined above. Additionally, Visual Studio Code mentions
Cyclic module usage
When I try to do this. Any ideas?
Most Liked
garrison
I agree this behavior is confusing; I also think it would be more intuitive to alias the current module into itself. I can’t explain exactly why, it’s just less surprising for some reason.
With that said, obviously there is no changing this now, so it is what it is.
Structs are just maps with an extra __struct__ key set to the module name. Functions are imported into the scope, but structs are not. You can only alias the module name.
Note however that defining a nested module aliases that module within the parent, so you can do this:
defmodule A do
defmodule B do
defstruct [:foo, :bar]
end
def foo(%B{}), do: true
end
Even though the actual name of B is A.B. Which only makes it more confusing tbh.
gregvaughn
Yes, but it’s even more fundamental than that. The struct “name” is a module name. Even if you were not creating a struct, but in any module you cannot refer to the current module by the leaf part of its name. For example, if you were writing any function within module Foo.Bar you cannot use Bar by itself. You’d have the same 3 options I mentioned.
sodapopcan
Just to put what @gregvaughn another way (which is what helped me) is that Elixir doesn’t technically have submodules. The . is a bit of an illusion and is only meaningful for constructs like alias, otherwise it’s just like any other character in a module name. Foo and Foo.Bar have absolutely no relation as far as the VM is concerned.
sodapopcan
That goes back to what Greg was saying that structs are named after the module itself. This is no different than if you want to use a module within itself: you either have to use __MODULE__ or you can type the full name verbatim.
As I said in my previous answer, Foo.Bar and Foo are completely different modules. It sounds like you are still thinking of it as Bar is a module within Foo. This is not the case, they are totally independent. Just think of . as an underscore. The . between the module name and the function, however, is an operator, though. It’s separating module and function name.
Sorry for the all the edits but I perhaps it’s clearer to say that the Foo in Foo and the Foo in Foo.Bar also have absolutely no relation.
gregvaughn
The struct’s module is Servy.Conv not Conv. You have a few choices. You could use the full module name, or you could alias or you could use the compile time variable __MODULE__. For example:
def full_status(%__MODULE__{} = conf) do
I prefer the compile time variable because if you ever refactor the module and change its name, this will reflect the current name.







