tensor-programming

tensor-programming

I’ve got a wrapper module that I am using around a library. The purpose of this module is to add small behavior to the library without outright forking it. I have a macro that produces the appropriate defdelegate and defoverridable calls on all of the functions that I need from library but I find myself writing a ton of boilerplate that could probably also be in the macro.

I have a struct called LibraryWrapper that looks like this:

defstruct type: Library.new(...)

For 1 arity functions I have a generalization that looks like this:

alias LibraryWrapper, as: LW

def foo(%LW{type: type}), do: Library.foo(type)
defdelegate foo(type), to: Library

For 2 arity functions, I have a generalization that looks like this:

alias LibraryWrapper, as: LW

def bar(%LW{type: type_a}, %LW{type: type_b}), do: Library.bar(type_a, type_b)
def bar(type_a, %LW{type: type_b}), do: Library.bar(type_a, type_b)
def bar(%LW{type: type_a}, type_b), do: Library.bar(type_a, type_b)
defdelegate(type_a, type_b), to: Library

If there were higher arity functions, the pattern would continue (but there aren’t for this particular library). Heres what the macro I am using looks like currently; and as you will see the defdelegate calls inside of it are completely irrelevant because the overridden functions override these making it so that they will never match. But if I could add a general pattern for all arity functions before the defdelegate call then I could just use the macro and not write all of the boilerplate.

defmacro wrap(module) do
  module= Macro.expand(module, __CALLER__)
  functions = module.__info__(:functions)

  sigs = 
    Enum.map(functions, fn {name, arity} ->
       args = 
           if arity == 0 do
             []
            else
              Enum.map(1..arity, fn i ->
                {String.to_atom(<<?A + i - 1>>), [], nil}
              end)
            end
        {name, [], args}
    end)

  combined = List.zip([sigs, functions])

  for comb <- combined do
    quote do 
      defdelegate unquote(elem(comb, 0)), to: unquote(module)
      defdelegate unquote([elem(comb, 1)])
     end
   end
end

Where I am running into issues is trying to figure out how to build the AST for getting the field out of the struct that I plan to pass into the macro. Also, I am not entirely sure how to build AST where the function matches on the specific struct type as I am doing above. I also have to wonder if there is a better way to do this apart from making a struct wrapper.

Showing Posts 1 to 1

tensor-programming

tensor-programming OP

Was able to figure it out with a pretty janky macro. But at least it seems to work in this case:

defmacro __using__(modules) do
    defs =
      Enum.reduce(modules, [], fn mod_ast, acc ->
        exports =
          mod_ast
          |> Macro.expand(__ENV__)
          |> apply(:module_info, [:exports])

        pre_defs = [module_info: 0, module_info: 1, __info__: 1]

        [{mod_ast, exports -- pre_defs} | acc]
      end)

    for {module, exports} <- defs do
      for {func_name, arity} <- exports do
        args = Macro.generate_arguments(arity, __MODULE__)
        body = make_body(args)
        quote do
          def unquote(func_name)(unquote_splicing(args)) do
            unquote_splicing(body)
            unquote(module).unquote(func_name)(unquote_splicing(args))
          end

          defoverridable unquote([{func_name, arity}])
        end
      end
    end
  end

defp make_body(args) do
    Enum.map(args, fn arg ->
      quote do
        unquote(arg) =
          case Map.get(unquote(arg), :type) do
            nil ->
              unquote(arg)

            type ->
              type
          end
      end
    end)
  end

I know this can be cleaned up significantly but at least it works for my purposes.

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews