Qqwy

Qqwy

TypeCheck Core Team

Is it possible to programmatically construct a function capture without using `Code.eval_quoted`?

So while writing Confy, I wanted to turn shorthand atoms like:integer, :string, :atom into the ‘normal’ forms &Confy.Parsers.integer/1, &Confy.Parsers.string/1, &Confy.Parsers.atom/1, etc.

However, I struggled to properly perform quoting and unquoting while using the function capture operator&.

In the end, I settled on the following snippet:

  # Replaces simplified atom parsers with
  # an actual reference to the parser function in `Confy.Parsers`.
  # NOTE: I dislke the necessity of `Code.eval_quoted` here, but do not currently know of another way.
  defp normalize_parser(parser) when is_atom(parser) do
    case Confy.Parsers.__info__(:functions)[parser] do
      nil -> raise ArgumentError, "Parser shorthand `#{inspect(parser)}` was not recognized. Only atoms representing names of functions that live in `Confy.Parsers` are."
      1 ->
        {binding, []} = Code.eval_quoted(quote do &Confy.Parsers.unquote(parser)/1 end)
        binding
    end
  end
defp normalize_parser(other), do: other

Note the Code.eval_quoted line: Although it is probably safe since it is only executed when parser indeed is an atom and exists as a function inside the Confy.Parsers module, it does not feel nice to need it.
Is there another way to build a function capture out of a module and function atom?

Note that 'function captures like wrapping everything with fn are not what I am looking for, since the result of normalize_parser is also used to create documentation:
inspect(&Module.foo/1) is "&Module.foo/1" whereas
inspect(fn x -> Module.foo(x) end) is "#Function<6.127694169/1 in :erl_eval.expr/5>".

Thanks! :smile:

Marked As Solved

hauleth

hauleth

Function.capture/3 is what you are looking for.

Also Liked

OvermindDL1

OvermindDL1

Would not &apply(Confy.Parsers, parser, [&1]) work? :slight_smile:

apply/3 is so very useful. ^.^

iex(1)> h apply/3

                     def apply(module, function_name, args)                     

  @spec apply(module(), function_name :: atom(), [any()]) :: any()

Invokes the given function from module with the list of arguments args.

apply/3 is used to invoke functions where the module, function name or
arguments are defined dynamically at runtime. For this reason, you can't invoke
macros using apply/3, only functions.

Inlined by the compiler.

## Examples

    iex> apply(Enum, :reverse, [[1, 2, 3]])
    [3, 2, 1]

Consequently it has no speed hit over a normal indirect call either since they lower to the same beam code, just to be sure I’ve also benchmarked it. ^.^

OvermindDL1

OvermindDL1

Also instead of this, this is faster:

if function_exported?(Confy.Parsers, parser, 1) do
iex(2)> h function_exported?

                def function_exported?(module, function, arity)                 

  @spec function_exported?(module(), atom(), arity()) :: boolean()

Returns true if module is loaded and contains a public function with the given
arity, otherwise false.

Note that this function does not load the module in case it is not loaded.
Check Code.ensure_loaded/1 for more information.

Inlined by the compiler.

## Examples

    iex> function_exported?(Enum, :member?, 2)
    true
axelson

axelson

Scenic Core Team

Wow, there’s so many little goodies in the stdlib. Never ceases to amaze me.

Last Post!

axelson

axelson

Scenic Core Team

Wow, there’s so many little goodies in the stdlib. Never ceases to amaze me.

Where Next?

Popular in Questions Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
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
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
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
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement