garrison

garrison

Global constants and pattern matching

A library I’m working on has a lot of functionality related to string prefixes (technically binaries, but they’re mostly strings). I have to add prefixes to keys a lot, and I have to pattern match them back off a lot.

Like:

defp prefix(key), do: "foo/" <> key
defp is_foo("foo/" <> key), do: true
defp is_foo(_key), do: false

And so on. But a lot of this functionality is embedded in application code, in anonymous functions, in Enum.maps, variable assignments, and so on. It’s everywhere.

I want to standardize these prefixes across the codebase going forward. They are already standardized, of course, but I have module attributes scattered across a couple dozen modules when I want them to be in one place.

There is, essentially, a lot of code like this, with the attributes duplicated across many modules:

@prefix "foo"
defp something(@prefix <> rest), do: rest

The docs recommend using public functions as constants instead of attributes, but this doesn’t work because I also need to use them for pattern matching everywhere.

I can think of two approaches off the top of my head:

First, use a module with a __using__ macro to inject the same set of attributes into every module. This would be acceptable for my use case, but it seems kinda cursed.

Second, replace the attributes with macros that inject the constants into the expressions. Essentially, the function-as-constant approach but with a macro instead. This sounds more sane to me, and is what I’ll probably do, but I figured I should seek some guidance in case I’ve overlooked something. This approach is conspicuously absent from the docs I linked.

First Post!

dimitarvp

dimitarvp

Just to make sure: those prefixes are not changing during runtime, correct?

Most Liked

cmo

cmo

I don’t hate the __using__ method for those cases that are truly global and/or want to be used in function heads.

It is a little less discoverable but it doesn’t take me too long to see the use MyModuleAttrubuteConstants at the top of the module and remember what I’ve done.

mudasobwa

mudasobwa

Creator of Cure

Use global public macros as constants then.

defmodule Prefixes do
  defmacro prefix(prefix \\ "foo", rest) do
    case __CALLER__.context do
      :match -> 
        quote generated: true, do: unquote(prefix) <> unquote(rest)
    end
  end
end

defmodule Usage do
  import Prefixes, only: [prefix: 1]

  def something(prefix(key)), do: key
end

Resulting in:

iex(2)> Usage.something "ggg"
** (FunctionClauseError) no function clause matching in Usage.something/1    
    
    The following arguments were given to Usage.something/1:
    
        # 1
        "ggg"
    
    iex:6: Usage.something/1
    iex:5: (file)
iex(5)> Usage.something "fooggg"
"ggg"
mudasobwa

mudasobwa

Creator of Cure

Well, Elixir macros are being expanded during compile-time and the AST they return is being directly injected into a resulting beam code. That said, the runtime would have zero impact compared to something("foo" <> rest) because _this is exactly the code the VM machine would see.

Duting the compilation time, yeah, it’ll gorge a couple of processor ticks, but you’d never notice this.

Where Next?

Popular in Questions Top

jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

Other popular topics Top

baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

We're in Beta

About us Mission Statement