Hello I have been trying to implement this function into elixir, but cannot seem to get it to work. It seems like a perfect use case for macros in my eyes.
I want a macro lets call newdef that takes a name argument, and a do block
defmodule Test do
defmacro newdef(name, do: block) do
...
end
end
this macro will then def three functions with the same name
quote do
defmodule Functions do
def unquote(name)([]) do
:endof
end
def unquote(name)([char | rest]) do
unquote(block) #this needs to use the 'char' argument
unquote(name)(rest)
end
def unquote(name)(string) do
array = string_to_array(string)
unquote(name)(array)
end
end
end
The Issues I am having:
getting the part in the do: access the char
creating functions that can pattern match
using the newdef in the same way I can use def in elixir
Thank you if you could give some enlightenment
Desired:
import Test
newdef namedfunction do
IO.puts(char)
end
First of all, you should be very careful with macros. Most likely, you don’t need a macro in your case. See Write macros responsibly.
The problem in your code is that the char in the macro does not affects the char used in your namedfunction. See Macro hygiene. With var!/1 you can make char available outside the macro.
Your could look like this (again I would not recommend writing it this way):
defmodule Foo do
defmacro magic(name, do: block) do
quote do
defmodule Functions do
def unquote(name)([]) do
:endof
end
def unquote(name)([var!(char) | rest]) do
unquote(block)
unquote(name)(rest)
end
def unquote(name)(string) do
array = String.split(string, "")
unquote(name)(array)
end
end
end
end
end
defmodule Bar do
import Foo
magic :go do
IO.puts(char)
end
end
iex(1)> Bar.Functions.go("hello")
h
e
l
l
o
:endof
iex(2)>
As @Marcus answered, you can use var!/1 to break macro hygiene, but I don’t think you should do so, or at least you should give the programmers using your macro (including your future self) some kind of hints about the magical local variables inside block.
step 1: design the intended use case
Instead of forcing the callers to use char as the local variable name, you can give the callers the freedom of choosing whatever variable name they want. For example, you can let the callers use the following syntax:
defmodule Foo do
import NewDef
newdef foo do
grapheme -> IO.puts(grapheme)
end
end
step 2: see the AST
quote do
foo -> foo + 1
end
It shows
[
{:->, _meta, [ # operator ->
[{:foo, _meta, Elixir}], # argument list of ->
{:+, _meta, [{:foo, _meta, Elixir}, 1]} # body of ->
]}
]
step 3: implement your macro
The simplest way is to assign char to whatever variable the caller chooses.
defmodule NewDef do
defmacro newdef({name, _, _}, do: [{:->, _, [[variable], expr]}]) do
quote do
def unquote(name)([]) do
:endof
end
def unquote(name)([char | rest]) do
unquote(variable) = char
unquote(expr)
unquote(name)(rest)
end
def unquote(name)(string) do
graphemes = String.graphemes(string)
unquote(name)(graphemes)
end
end
end
end
Thank you so much for the help.
Please tell me if my thinking is correct but I feel like this is a use case for macros.
It isn’t about implementing that specific function.
But I want to implement a variety of functions that will all have that similar functional structure. Which seems like what macros are for. So would defining a macro that does this be the correct implementation?
If not what would you recommend? Or, if macros would be the correct way how would you personally implement this?
It is difficult to say if you really needs macros. I can’t see from your example what you want to achieve. In doubt I always start with an implementation without macros. And most of the time when I use macros I do later on a refactoring to get rid of macros.
If you explain in more detail what you want to achieve, someone can tell you if you need macros.
Okay i I think I can describe it better. Should I simply make a new reply here or make a new topic? I would think making just another reply since it is still answering this question correct?
Both are fine but I’d still make a new thread with a changed title because people respond more to those. You are free to put a link to this thread in it as well.