gleam-lang / suggestions

📙 A place for ideas and feedback
26 stars 2 forks source link

Functions to get info about functions #126

Closed martimatix closed 3 years ago

martimatix commented 3 years ago

For the purpose of providing ergonomic features in libraries, it would be nice to have a function that can return information about functions.

In particular, I would be interested in something like this:

pub fn nice_to_have() -> List(String) {
  let foo = fn(bar) { bar + 1 }
  get_args(foo)
}

// returns ["bar"]

In Erlang, it's possible to print a function but it's not possible to use this from Gleam because the function would be external and therefore the environment variables (Env) that contain the abstract code are not returned.

The Gleam compiler could return function info from the AST without relying on Erlang. I concede that this is outside the realm of classical functional programming and entering macro territory but nevertheless, this would be a nice feature to have for a library that I am thinking of writing.

lpil commented 3 years ago

Hello! Interesting idea.

What would you do with this information? Is there a particular program or library this would enable you to build? Some rough examples would be informative.

it's not possible to use this from Gleam because the function would be external and therefore the environment variables (Env) that contain the abstract code are not returned.

I believe Gleam will generate code that uses local functions whenever it is possible to do so in Erlang. Could you give an example of a situation in which this does not work?

The Gleam compiler could return function info from the AST without relying on Erlang.

If we do this it'll only work with literals and direct references. For example this code wouldn't work:

pub fn nice_to_have(foo) -> List(String) {
  get_args(foo)
}
martimatix commented 3 years ago

What would you do with this information?

I'm contemplating writing a faunadb driver for Gleam. Faunadb drivers for other programming languages work by having a collection of functions that get transformed into fauna query language (FQL).

This page from the faunadb docs shows why I'm interested in capturing the argument value. You can use the dropdown to switch to another language.

In order to prevent the last scenario, I'm looking for a way for Gleam to capture the argument name.

Could you give an example of a situation in which this does not work?

In a native erlang module, I have created the following function based off the Stackoverflow code from my earlier post.

print_function(Fun) ->
    {env, [{_, _, _, Abs}]} = erlang:fun_info(Fun, env),
    Str = erl_pp:expr({'fun', 1, {clauses, Abs}}),
    io:format([Str | "\n"]).

Then, in Gleam, I have

pub external fn print_function(a) -> String =
  "my_native_module" "print_function"

pub fn nice_to_have() -> String {
  let foo = fn(bar) { bar + 1 }
  print_function(foo)
}

This produces the error:

** exception error: no match of right hand side value {env,[]}

which is because the array is empty and according to the erlang docs, it's empty because it's considered to be an external function.

I don't believe it's an issue with Gleam as I can't even get this to work in native Erlang. I have only got it to work when I'm in the rebar3 shell and define the function in the shell. Any suggestions on how to get this to work would be greatly appreciated.

If we do this it'll only work with literals and direct references.

That makes sense.

lpil commented 3 years ago

Ah yes that's right. A function is only local if defined within the same module and referred to without using a module qualifier, which I suspect means this isn't going to work for your FQL design.

If we go for an in-Gleam approach it'll have the same limitations as Erlang's macros in that it will only work with literals. Variables etc won't be usable in the DSL. This seems like it would be a pretty big limitation, would variable wrapping like in those other languages be preferable?

martimatix commented 3 years ago

I was looking at how Scala pulls this off and it uses macros. Perhaps I'll revisit this approach if Gleam gets a macro system.

For now, variable wrapping is the way to go. Thanks for helping unpack this @lpil 🙂

lpil commented 3 years ago

Looking forward to seeing your faunadb driver!