Compilation error on anonymous function argument with multiple clauses

Dorgan
2023-01-24

Dorgan:

Given an extension with these definitions:

  @event_handler [
    type:
      {:spark_function_behaviour, ChannelHandler.Plugs.Handler,
       {ChannelHandler.Plugs.Handler.Function, 2}},
    required: true
  ]

  @event %Spark.Dsl.Entity{
    name: :event,
    target: Event,
    args: [:name],
    describe: """
    Handles an event matching exactly `name`.

    ## Examples

        event "create_post" do
          handler fn {payload, bindings}, socket ->
            # ...
          end
        end
    """,
    entities: [plugs: [@plug]],
    schema: [
      name: [
        type: :string,
        required: true,
        doc: """
        The event to match.
        """
      ],
      handler: @event_handler
    ]
  }

And this usage:

  handlers do
    event "create" do
      plug Plugs.CastInput

      handler fn
        {%{"id" => id}, bindings}, socket ->
          {:reply, {:ok, "Dataset #{id} created for karta #{bindings.karta.id}"}, socket}

        _, socket ->
          {:noreply, socket}
      end
    end
  end

I get this error:

== Compilation error in file lib/channels_web/channels/test_channel/dataset_handler.ex ==
** (ArgumentError) cannot inject attribute @spark_dsl_config into function/macro because cannot escape #Function<6.112779023/2 in :elixir_compiler_2.__MODULE__/1>. The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, PIDs and remote functions in the format &Mod.fun/arity
    (elixir 1.14.2) lib/kernel.ex:3543: Kernel.do_at/5
    (elixir 1.14.2) expanding macro: Kernel.@/1
    /home/dorgan/dev/channels/lib/channels_web/channels/test_channel/dataset_handler.ex:1: (file)
    (spark 0.3.5) /home/dorgan/dev/channels/lib/channels_web/channels/test_channel/dataset_handler.ex:1: Spark.Dsl.__before_compile__/1

If I remove the second clause to the fn in the handler, then it works.

ZachDaniel:

I can already imagine why that’s happening. Will fix it tonight or tomorrow

Dorgan:

enjoy your vacation :)

Dorgan:

I see that in CodeHelpers spark only handles single clause anonymous functions, so it would be a matter of generating more clauses here https://github.com/ash-project/spark/blob/3f84eb5ac97fd61550113d00f126058a5370ad65/lib/spark/code_helpers.ex#L139-L162 I think

ZachDaniel:

Yep, exactly. We’d want to give it a name based on the hash of all clauses though, so that each definition has the same name

ZachDaniel:

It’s probably a pretty simple change all things considered. I can merge a PR if you make one 😀

Dorgan:

I’ll give it a shot!

Dorgan:

It seems we’re already taking an md5 hash of the full quoted expression so I just added more clauses: https://github.com/ash-project/spark/pull/18

ZachDaniel:

Looks perfect!

ZachDaniel:

And maybe not important, but I wouldn’t have expected the “unquote_splicing” to work since it would be putting commas after function defs

ZachDaniel:

Actually, one small thing

ZachDaniel:

Can you loop over the defs and make a list of quoted expressions instead?

ZachDaniel:

Actually, dunno if that will work… I guess yours is tested and works so maybe I’m getting at nothing 😂

Dorgan:

It seems to produce the correct ast:

ZachDaniel:

Let’s go with it then 👍

ZachDaniel:

Merged. Will cut a release later. Thanks for the PR!

Dorgan:

<:KyouAYAYA_MM:628639134008737794>