[Proposal] Add a shortcut to access a struct within the module where it is defined

75 views
Skip to first unread message

Tim Masliuchenko

unread,
Jan 21, 2022, 5:42:02 AM1/21/22
to elixir-lang-core
It is common to define a struct together with various functions that access that struct in the module:

    defmodule Chat.Channel do
      defstruct name: "", public?: true
    
      def new do
        %Chat.Channel{name: "Untitled"}
      end

      def connect(%Chat.Channel{} = channel) do
        IO.inspect(channel)
      end
    end

It is also common to alias the struct for easier access

    defmodule Chat.Channel do
      defstruct name: "", public?: true
    
      alias Chat.Channel
      
      # ...
    end

But, say, renaming the module would require manually replacing all struct occurrences with the new module name. Aliasing can help, but if the last bit should be updated too(say Chat.Channel should be updated to Chat.Room) it would still require to manually replace everything.

There is a workaround to use __MODULE__, but IMO the code looks a bit ugly

    defmodule Chat.Channel do
      defstruct name: "", public?: true
    
      def new do
        %__MODEUL__{name: "Untitled"}
      end
    
      def connect(%__MODEUL__{} = channel) do
        IO.inspect(channel)
      end
    end

I think It would be great to have some kind of shortcut(syntactic sugar) to access the struct within the module.
First I thought about something like %_(%%, %. etc) but this way it looks a bit cryptic 

    def connect(%_{} = channel) do

So maybe something like %self would work

    defmodule Chat.Channel do
      defstruct name: "", public?: true
    
      def new do
        %self{name: "Untitled"}
      end
    
      def connect(%self{} = channel) do
        IO.inspect(channel)
      end
    end

What do you think?

Wojtek Mach

unread,
Jan 21, 2022, 5:58:56 AM1/21/22
to elixir-lang-core
Neither `%_{}` nor `%self{}` can be supported because they already have a meaning in pattern matches. The former means _any_ struct and the latter binds the matched struct name to the variable `self`.

You can give `__MODULE__` another name with an alias:

    alias __MODULE__, as: Struct

    def connect(%Struct{} = channel)
 --
 You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
 To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/da49bf41-d4ad-4fc7-a88c-1338e7a463c1n%40googlegroups.com.

Tim Masliuchenko

unread,
Jan 21, 2022, 6:56:42 AM1/21/22
to elixir-lang-core
Thanks for the tip Wojtek
Aliasing __MODULE__ should work in my case

As far as I understand this pattern isn't used too often(at least projects like Plug or Ecto don't use it), so I guess it is not really considered as idiomatic 

I just feel that if you are inside a module there should be a shortcut built-in in the language(like when you call other functions from the module you don't specify the full path) but it might be just old instincts from other languages

пʼятниця, 21 січня 2022 р. о 10:58:56 UTC Wojtek Mach пише:

Ben Wilson

unread,
Jan 21, 2022, 2:08:20 PM1/21/22
to elixir-lang-core
__MODULE__ is the right answer here IMHO. It is consistent with the other "meta constants" like __ENV__, __DIR__ __FILE__ and so on in that they desugar to constants, but are file / code relative. It isn't a super common pattern, but last time I checked generated phoenix code does a %__MODULE__{} pattern match check on the changeset functions.

Tim Masliuchenko

unread,
Jan 30, 2022, 11:03:15 AM1/30/22
to elixir-lang-core
Yeah, I've seen %__MODULE__{} in a few places too

I just realised that it is also possible to do something like

    defmodule Chat.Channel do
      defstruct name: "", public?: true

      @me __MODULE__

      def connect(%@me{name: name} = channel) do
        # ...
      end
    end

пʼятниця, 21 січня 2022 р. о 19:08:20 UTC benwil...@gmail.com пише:
Reply all
Reply to author
Forward
0 new messages