Alias and Macros?

557 views
Skip to first unread message

Adrian Lee

unread,
Feb 2, 2015, 12:48:54 PM2/2/15
to elixir-l...@googlegroups.com
I'm new to the language and struggling with this. I want to create an alias within a __using__ macro e.g.

defmodule Foo.Bar do
  defmacro __using__(_) do
    quote do
      alias String.replace(__MODULE__, "Bar", "Baz")
    end
  end
end

Obviously the above does not work; I understand why, but I'm at a loss how to accomplish what I want. Can anyone point me in a general direction please?

Tia

Ben Wilson

unread,
Feb 2, 2015, 1:18:38 PM2/2/15
to elixir-l...@googlegroups.com
Hey! Glad you're trying the language out. First question: What exactly are you trying to do here? Alias Foo.Bar as Foo.Baz inside of Foo.Bar? If so, you don't need __using__ as that would be what you wanted if you had some other module like Zoo.Bar inside of which you could say "using Foo.Bar" and it would alias Zoo.Bar as Zoo.Baz inside of Zoo.Bar.

As far as the actual aliasing goes, keep in mind that __MODULE__ and module names are atoms, not strings. You'd want something like this to handle the substitution. How you'd actually use that depends on what you're trying to do, per the previous question.

def replace_bar(module) do
  module
  |> Module.split
  |> Enum.map(fn
    "Bar" -> "Baz"
    other -> other
  end)
  |> Module.concat
end

- Ben

Adrian Lee

unread,
Feb 2, 2015, 1:55:31 PM2/2/15
to elixir-l...@googlegroups.com
Thanks Ben and sorry,  my example was absoutely horrendous.

I'm trying to "use Foo.Bar" and generate an alias that would be the name of the calling module with the final segment of the module name replaced. e.g. CallerModule.Bar would then contain an "alias CallerModule.Baz".

Muddy waters - perhaps if I rephrase and deal with one part at a time - my naive attempt to dynamically generate an alias is something like the following:

defmodule ElixirAlias.Bar do
alias(String.replace(__MODULE__, "Bar", "Baz") |> String.to_atom)
end
 
Which results in the error :

** (CompileError) lib/elixir_alias.ex:2: invalid argument for alias, expected a compile time atom or alias, got: {{:., [line: 2], [:erlang, :binary_to_atom]}, [line: 2], [{{:., [line: 2], [String, :replace]}, [line: 2], [ElixirAlias.Bar, "Bar", "Baz"]}, :utf8]}
    (stdlib) erl_eval.erl:657: :erl_eval.do_apply/6

I get why it doesn't work - I'm trying to generate an alias via code that isnt executed until run time, but I'm flummoxed by how to achieve what I want.

Ta

Ben Wilson

unread,
Feb 2, 2015, 4:45:58 PM2/2/15
to elixir-l...@googlegroups.com
Well, so you can kind of cheat with this one. ```alias A.B.C``` just means that ```C``` expands to ```Elixir.A.B.C```. We can use this to our advantage.

defmodule Foo.Bar do
    defmacro __using__(_) do
        quote do
            alias __MODULE__, as: Baz # All you need!
        end
    end
end

defmodule Foo.Bar do
    use Foo.Bar

    def test_baz do
        Baz.hello
    end

    def hello do
        IO.puts "Hello"
    end
end

By using the as: parameter you can avoid needing to do any parsing or editing of the module at all! Does this accomplish what you wanted?

Adrian Lee

unread,
Feb 2, 2015, 5:36:38 PM2/2/15
to elixir-l...@googlegroups.com
Thanks for the suggestion but it doesn't really work for me since I'm aliasing a different module.

I'm getting somewhere with it now but its frustrating work given I know just enough to be dangerous but not enough to understand what I'm doing.

Eric Meadows-Jönsson

unread,
Feb 2, 2015, 5:46:45 PM2/2/15
to elixir-l...@googlegroups.com
You can do:

```elixir
defmodule Foo do
  defmacro __using__(_) do
    module =
      __CALLER__.module
      |> Atom.to_string
      |> String.replace("Bar", "Baz")
      |> String.to_atom
    quote do
      alias unquote(module)
    end
  end
end
```

`alias` expects to be given a literal alias (a module name), so passing in `String.replace` does not work. Keep in mind that doing something like this seems like a bad idea since `BarFooBar` will be aliased to `BazFooBaz` which does not seem expected.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/263e21a0-9ed2-4193-bdba-7a4a9c4de48e%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Eric Meadows-Jönsson

Adrian Lee

unread,
Feb 2, 2015, 6:08:13 PM2/2/15
to elixir-l...@googlegroups.com
Cheers Eric, I'd just worked it out a variant myself: https://gist.github.com/os6sense/4d71a2e887e81050cc91

Turns out I was struggling with an unrelated problem (namely being a noob no doubt) ;)

Ben Wilson

unread,
Feb 2, 2015, 6:12:37 PM2/2/15
to elixir-l...@googlegroups.com
For what it's worth, the variant you have could be made more idiomatic.
1) I'm not sure why you're starting all of your modules with Elixir.
2) Your dyn_mod assignment process probably doesn't get you the result you want. For example, BarFooBar would become FooBarBaz. Do you want all instances of Bar in a module name to be replaced, or only the first, or only the last?

Adrian Lee

unread,
Feb 2, 2015, 6:41:00 PM2/2/15
to elixir-l...@googlegroups.com
> 1) I'm not sure why you're starting all of your modules with Elixir.

It's just toy code that I'm hacking about with.


> 2) Your dyn_mod assignment process probably doesn't get you the result you want.

Indeed, as both yourself and Eric have pointed out that's broken. List.delete_at(list, -1) is closer to what I'm trying to do.

And many thanks for the help :)
Reply all
Reply to author
Forward
0 new messages