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
RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
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
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
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
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

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews