# Create function on-the-fly?

**URL:** <https://forum.elixirforum.com/t/create-function-on-the-fly/11999>\
**Category:** Questions\
**Created:** [January 29, 2018, 12:17am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999 "2018-01-29T00:17:08Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 29, 2018, 12:17am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/1 "2018-01-29T00:17:08Z")

</div>

Let say I have a tuple :

{:sum, %{x: 23}, %{y: 55 }}

Out of this how can I create the following function :

def sum({ %{x: 23}, %{y: 55 }} ), do: true

as a second question, let say I have :

{:sum, %{x: 23}, %{y: 55 }, %{when: “y \> 11”} }

how do I create the function with guard ? btw. guard format could be different, say list or something else . whichever is easier ?

* * *

How about the case where the function that has to be generated has the form :

def sum({ work = %{x: 23, y: 54 } }) …

---

<div class="post-metadata">

**Author:** ![benwilson512](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/benwilson512/32/1457_2.png) [@benwilson512](https://forum.elixirforum.com/u/benwilson512)\
**Post date:** [January 29, 2018, 2:31am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/2 "2018-01-29T02:31:25Z")

</div>

Can you talk a bit more about your use case?

Elixir macros can generate functions at compile time from data but this is a bit of an odd way to go about it. If you’re trying to do it at run time I think you really just need to treat it as a data transformation instead of as meta-programming.

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 29, 2018, 3:09am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/3 "2018-01-29T03:09:01Z")

</div>

I’m trying to make sort of Production system and want header-function-matching to do most of the work for me, where the rules are passed/created from tuples.

Btw .. prod system is a list of IF-THEN rules and perfectly match “def fun(IF), do: THEN pattern”

---

<div class="post-metadata">

**Author:** ![kip](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/32/1440_2.png) [@kip](https://forum.elixirforum.com/u/kip)\
**Post date:** [January 29, 2018, 5:06am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/4 "2018-01-29T05:06:28Z")

</div>

Here’s a simple example using your data that might get you started:

```elixir
defmodule Gen do
  @fun_list [
    {:sum, %{x: 23}, %{y: 55 }}
  ]
  
  for {fun, a, b} <- @fun_list do
    def unquote(fun)(unquote(Macro.escape(a)), unquote(Macro.escape(b))), do: true
  end
end

```

In IEx:

```elixir
iex(3)> Gen.sum(%{x: 23}, %{y: 55 })                                                      
true

```

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 29, 2018, 9:18pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/5 "2018-01-29T21:18:23Z")

</div>

Why this does not work, but the one you gave work.

```
defmacro rule({ name, goal, work }) do
    quote do
         def unquote(name)({ unquote(Macro.escape(goal)), unquote(Macro.escape(work)) }), do: true
    end
end

```

> # ProdSys.rule({ :test, %{ goal: “test” }, %{} })

\*\* (ArgumentError) cannot invoke def/2 outside module  
(elixir) lib/kernel.ex:5122: Kernel.assert\_module\_scope/3  
(elixir) lib/kernel.ex:3893: Kernel.define/4  
(elixir) expanding macro: Kernel.def/2  
iex:24: (file)  
(prod\_sys) expanding macro: ProdSys.rule/1  
iex:24: (file)

```
iex(28)> expr = quote do: ProdSys.rule({ :test, %{ goal: "test" }, %{} })
  {{:., [], [{: __aliases__ , [alias: false], [:ProdSys]}, :rule]}, [],
  [{:{}, [], [:test, {:%{}, [], [goal: "test"]}, {:%{}, [], []}]}]}

iex(29)> res = Macro.expand_once(expr, __ENV__ )                          
 {:def, [context: ProdSys, import: Kernel],
 [
   {:{}, [context: ProdSys],
    [
      {[],
        [:test, {:{}, [], [:%{}, [], [goal: "test"]]}, {:{}, [], [:%{}, [], []]}]}
     ]},
    [do: true]
 ]}

iex(30)> IO.puts Macro.to_string(res)                                    
 def({{[], [:test, {:%{}, [], [goal: "test"]}, {:%{}, [], []}]}}) do
  true
 end
:ok

```

---

<div class="post-metadata">

**Author:** ![OvermindDL1](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/32/2677_2.png) [@OvermindDL1](https://forum.elixirforum.com/u/OvermindDL1)\
**Post date:** [January 29, 2018, 9:19pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/6 "2018-01-29T21:19:14Z")

</div>

> [@mraptor](#):
>
> Why this does not work, but the one you gave work.

What error message do you get and what is the full example code to test with?

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 29, 2018, 9:36pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/7 "2018-01-29T21:36:51Z")

</div>

here is simpler one :

```
iex(40)> expr = quote do: ProdSys.rule({ :test, 3,5 })
 {{:., [], [{: __aliases__ , [alias: false], [:ProdSys]}, :rule]}, [],
  [{:{}, [], [:test, 3, 5]}]}

iex(41)> res = Macro.expand_once(expr, __ENV__ )                          
 {:def, [context: ProdSys, import: Kernel],
 [{:{}, [context: ProdSys], [{[], [:test, 3, 5]}]}, [do: true]]}

iex(42)> IO.puts Macro.to_string(res)                 
  def({{[], [:test, 3, 5]}}) do
   true
 end

```

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 29, 2018, 10:33pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/8 "2018-01-29T22:33:59Z")

</div>

Here is another example :

```
 defmodule Test do

defmacro rule(namex) do
	quote do
		def unquote(namex)() do
		 true
		end
	end
end

@lst [
	{:sum, 5, 7 },
	{:sum, 1, 3}
]

for { name, goal, work } <- @lst do
	def unquote(name)({ unquote(Macro.escape(goal)), unquote(Macro.escape(work)) }), do: true
end
end 

iex(8)> Test.rule(:test) 
** (ArgumentError) cannot invoke def/2 outside module
(elixir) lib/kernel.ex:5122: Kernel.assert_module_scope/3
(elixir) lib/kernel.ex:3893: Kernel.define/4
(elixir) expanding macro: Kernel.def/2
iex:8: (file)
(prod_sys) expanding macro: Test.rule/1
iex:8: (file)

iex(8)> Test.sum({5,7}) 
true

iex(9)> Test.sum({1,3})  
true

```

---

<div class="post-metadata">

**Author:** ![OvermindDL1](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/32/2677_2.png) [@OvermindDL1](https://forum.elixirforum.com/u/OvermindDL1)\
**Post date:** [January 29, 2018, 10:37pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/9 "2018-01-29T22:37:02Z")

</div>

> [@mraptor](#):
>
> \*\* (ArgumentError) cannot invoke def/2 outside module

That tells you exactly the issue, you are calling `rule/1`, which is a macro that returns a `def` AST, and you cannot call `def` at that location, you can only call it from inside a module. 🙂

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 30, 2018, 12:11am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/10 "2018-01-30T00:11:38Z")

</div>

So how can I solve it… i.e. be able to create functions on demand !?

---

<div class="post-metadata">

**Author:** ![OvermindDL1](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/32/2677_2.png) [@OvermindDL1](https://forum.elixirforum.com/u/OvermindDL1)\
**Post date:** [January 30, 2018, 12:12am UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/11 "2018-01-30T00:12:34Z")

</div>

> [@mraptor](#):
>
> So how can I solve it… i.e. be able to create functions on demand !?

Exactly what the error says, you have to call your `rule` macro from inside a `defmodule` body. 🙂

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 30, 2018, 9:12pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/12 "2018-01-30T21:12:44Z")

</div>

hmmm.. that is the idea to use it from outside.

Also exploring Prolog. There it also seems impossible to just reuse the rules, but may be I can cheat in different way.

Also looked of paper of ERESYE written in Erlang, they seem to be doing something along the lines what I’m trying.  
[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.73.4327&rep=rep1&type=pdf](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.73.4327&rep=rep1&type=pdf)

---

<div class="post-metadata">

**Author:** ![OvermindDL1](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/32/2677_2.png) [@OvermindDL1](https://forum.elixirforum.com/u/OvermindDL1)\
**Post date:** [January 30, 2018, 9:16pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/13 "2018-01-30T21:16:33Z")

</div>

As a side note, if you want a prolog’y thing on the beam there is erlog. ^.^

What do you mean ‘outside’? On the beam _all_ things, code or not everywhere at any time exist compiled in a module (even in the shell you are running is just inside a module except it even interprets what you are doing, so you could make an anonymous function for example, but that anonymous function is bound to that module too).

---

<div class="post-metadata">

**Author:** ![mraptor](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/bc8723/32.png) [@mraptor](https://forum.elixirforum.com/u/mraptor)\
**Post date:** [January 30, 2018, 9:18pm UTC](https://forum.elixirforum.com/t/create-function-on-the-fly/11999/14 "2018-01-30T21:18:03Z")

</div>

Yeah I know about erlog , I used it in the past. thanks
