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.map
s, 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.