#Macro.var assistance ( brave souls only )

64 messages · Page 1 of 1 (latest)

pastel quarry
#

This is not for the feint of heart. Pls don't ask how or why.

So.... I have some code like this ```elixir
def compile_function(env, name, opts) do
fun_args =
for arg <- [:hello, :how, :are, :you] do
Macro.var(arg, env.module)
end

handler =
  quote do
    apply(unquote(env.module), unquote(:nice_function), unquote(fun_args))
  end

quote do
  unquote(quoted_docs(opts))

  def unquote(:"#{name}")(unquote_splicing(fun_args)) do
    unquote(handler)
  end
end

endThis successfully creates a function that looks like thiselixir
@doc "Works well, Gives hope for future (aka Peak of Mt. Stupid)"
def generated_function(hello, how, are, you) do
apply(Test, :nice_function, [hello, how, are, you])
end All good so far. However ... I need(!) to get that variable into something more easily digested. for example something likeelixir
@doc "My dream of the future"
def generated_function(hello, how, are, you) do
apply(Test, :nice_function, [%{hello: hello, how: how, are: are, you: you}])
end My problem is that whenever i try to use a `Macro.var/2` anywhere except the top level of `unquote_splicing` I end up with the ast coming through like thiselixir
@doc "Valley of dispair I currently reside in"
def generated_function(id) do
apply(Test, :nice_function, [[%{id: {:id, [], Test}}]])
end``` Please send help.

PS: Will also require food and blankets if I cant get this figured out soon

#

PPS: I have a giant can of fosters for whoever can solve it

fresh birch
#

I think you need a keyword list for the map

#

Then use unquote splicing

#

I'm on my phone, I can give you code in a bit

pastel quarry
#

ty. ill give the kw list a crack

fresh birch
#

I built something very much like this for the lsp parsing code

#

So to teach you to fish, use quote around the form you wish to replicate, then see what the ast looks like

#

Then it's a lot more obvious what to do. Maps are always tricky

#

Also, why call apply?

#

Why not directly call nice_function?

pastel quarry
#

just what i got working first

fresh birch
#

It's likely just as easy to macro up a direct call

pastel quarry
#

putting Keyword.new at the end of the map i created 'fixed' it, the variable now carries through Prayge

#

thank you kind sir

fresh birch
#

unquote(func_name)(%{unquote_splicing(map_args)})

pastel quarry
#

oh lord

#

i would never have guessed that

fresh birch
#

Where map args is the keyword or the name of the arg, macro var

#

Built in the comprehension

pastel quarry
#

im with you (by a very fine thread) but im with you

fresh birch
#

Change the body of the comp to return {arg, Macro.var(arg, __CALLER__.module}

#

Call the result map_args

#

Then make fun_args =Keyword.keys(map_args)

#

Phew, that was hard to type

limber oak
#

icecreamcohen gonna be fosterscohen soon

pastel quarry
#

dm address for prize

fresh birch
#

You're lucky I was just using macros

pastel quarry
#

just when you think things don't get any more wild

pastel quarry
fresh birch
#

nah

#

did it work?

pastel quarry
#

yeah thanks

#

and my card got temporarily blocked for signing up to this usa beer site

fresh birch
#

did you understand my suggestion for how to see the AST?

pastel quarry
#

yeah ive done that before to see it. however it resulted in me manually constructing AST to get to my goal previously ( 🤮 ). i was sure there should be an easier way with quote unquote

fresh birch
#

so, something like:

iex(1)> quote do
...(1)>   %{foo: 3, bar: 6}
...(1)> end
{:%{}, [], [foo: 3, bar: 6]}
#

you can see that the map constructor :%{} takes a keyword list

pastel quarry
#

yeah

fresh birch
#

now, you can also just emit that directly

pastel quarry
#

i didnt see that coming

fresh birch
#

so, just like the above, it's a function call

#

all function calls are 3 tuples

#

function name (an atom), the env, and the arguments

#

but you can write it out in a quote block

#

fun times

mossy geode
pine glen
#

one day ill be a man enough to be able to understand all of this.
one day.....for sure........ig

fresh birch
soft glacier
#

Not that you asked @pastel quarry and not that it's really my place to give advice but I'd try to avoid "don't ask how or why" when asking for technical help. Yeah maybe you've come up with the best solution; but often times when I explain to people what I'm trying to accomplish (rather than asking why some code isn't doing what I think it should) people will point out other, easier approaches that hadn't even occurred to me. Free advice that's worth what you paid for it! 😛

pastel quarry
pastel quarry
#

Results: Declare the external API you want to wrap like so elixir use Jonny, endpoint: "https://api.linksafe.com.au", version: "v1", auth: [ header: "apiKey" ], pool: [ size: 32, count: 8 ], limiter: [ header_responsive: [ reset: "X-Rate-Limit-Reset", remaining: "X-Rate-Limit-Remaining" ] ], routes: [ create_account: [ doc: "create account", method: :post, path: "/external/account", body: [ account: [ type: Plaza.Schema.AccountBean ] ] ], get_account_by_id: [ doc: "Get an account by id", method: :get, path: "/external/account/id/:account_id", params: [ account_id: [ type: :string ] ] ] all the functions are made for you and all the fields are parsed / validated according to the configured types when you run it

#

actual code generated:

@doc "Get an account by id"
def get_account_by_id(account_id) do
  apply(Plaza.TestClient, :request, [
    [account_id: account_id],
    %{
      body: %{},
      doc: "Get an account by id",
      method: :get,
      module: Plaza.TestClient,
      name: :get_account_by_id,
      params: %{account_id: %{sub: ":account_id", type: :string}},
      path: "/external/account/id/:account_id",
      query: %{}
    }
  ])
end```
#

bunch of improvements i want to make especially with the doc generation. which I can leverage NimbleOptions.docs however its going to be an exercise for later

#

and thats how you spend twice as long taking the 'shortcut' of writing an API wrapper...wrapper instead of manually writing all the functions.

#

cc: @fresh birch ty again

fresh birch
#

lol, this looks a lot like how I'm defining lsp messages

#
  defmodule TypeFormattingOptions do
    use Proto

    deftype tab_size: integer(),
            insert_spaces: boolean(),
            trim_trailing_whitespace: optional(boolean()),
            insert_final_newline: optional(boolean()),
            trim_final_newlines: optional(boolean()),
            "*rest": one_of([string(), boolean(), integer()])
  end
pastel quarry
#

sweet, my first idea was a defmacro like that too but i couldn't get my head around it

#

followed by an accumulating @route attribute which also fell flat

fresh birch
#

if you want to check it out, I pushed a pr to elixir_ls