Finding out a value stored inside a closure

metaprogramming
#1

I have been thinking about this problem for some time. This is not a real world problem, I ask this for curiosity.

Is it possible to find out the value stored inside a closure in any programming language? See this JavaScript example. How can I find out what is the value stored inside that closure?

$ node
> const f = x => y => x * y
undefined
> const closure = f(8)
undefined
> closure(9)
72
>

Is it possible to use some metaprogramming magic in Elixir to find out the value of x? This did not work:

$ iex
Erlang/OTP 21 [erts-10.3.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> f = quote do: (fn x -> fn y -> x * y end end).(8)
{{:., [],
  [
    {:fn, [],
     [
       {:->, [],
        [
          [{:x, [], Elixir}],
          {:fn, [],
           [
             {:->, [],
              [
                [{:y, [], Elixir}],
                {:*, [context: Elixir, import: Kernel],
                 [{:x, [], Elixir}, {:y, [], Elixir}]}
              ]}
           ]}
        ]}
     ]}
  ]}, [], '\b'}
iex(2)>

I found out that quoting functions is not possible in Elixir. Is this even possible in any programming language?

1 Like
#2

Many programming languages allow you to retrieve the bindings/environment of a closure. Including Erlang/Elixir:

iex(1)> defmodule Foo do
...(1)> def fun, do: fn x -> fn y -> x * y end end
...(1)> end
{:module, Foo,
 <<70, 79, 82, 49, 0, 0, 4, 204, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 151,
   0, 0, 0, 16, 10, 69, 108, 105, 120, 105, 114, 46, 70, 111, 111, 8, 95, 95,
   105, 110, 102, 111, 95, 95, 7, 99, 111, ...>>, {:fun, 0}}
iex(2)> :erlang.fun_info(Foo.fun)
[
  pid: #PID<0.105.0>,
  module: Foo,
  new_index: 0,
  new_uniq: <<254, 160, 117, 185, 64, 241, 140, 62, 241, 138, 87, 75, 85, 95,
    124, 166>>,
  index: 0,
  uniq: 133497773,
  name: :"-fun/0-fun-1-",
  arity: 1,
  env: [],
  type: :local
]
iex(3)> :erlang.fun_info(Foo.fun.(1))
[
  pid: #PID<0.105.0>,
  module: Foo,
  new_index: 1,
  new_uniq: <<254, 160, 117, 185, 64, 241, 140, 62, 241, 138, 87, 75, 85, 95,
    124, 166>>,
  index: 1,
  uniq: 133497773,
  name: :"-fun/0-fun-0-",
  arity: 1,
  env: [1],
  type: :local
]

See the :env key. However, as the note for :erlang.fun_info/1 says, it should only be used for debugging purposes. Especially, because captured variables, compiler optimizations and what not may change the value of env.

8 Likes