denna

denna

I am trying to provide a way to create something like inheritance.
i want to be able to call a a function on a Module that inherits the function from another Module. Is the following the most straightforward way?

defprotocol Proto do
  @spec foo(t()) :: charlist()
  def foo(item)
end

defmodule SpecializedProto do
  @callback info() ::  charlist()
end

defimpl Proto, for: Atom do
  @impl true
  def foo(item) do
    apply(item,:foo,[item])
  end
end

defimpl Proto, for: SpecializedProto do
  @impl true
  def foo(item) do
    apply(item,:info,[])
  end
end

defmodule Test do
  @behaviour SpecializedProto
  def info(), do: "test"
  defdelegate foo(x), to: Proto.SpecializedProto
end

Proto.foo(Test)

Showing Posts 1 to 5

dimitarvp

dimitarvp

I believe you should start with describing the problem you are trying to solve, so we can give you ideas and alternative approaches. Inheritance is just one way to do things. There are others as well.

denna

denna OP

hm difficult to break down, but the library I am working on is trying to provide abstractions for embedded UI. Combinations of button presses tigger actions and combinations of leds. so i am trying to create a widget library that abstract different kind of behaviors.

it works ok well with only functions but then i need to keep track of the state initialization separately. So instead of composing functions i would rather compose very specialized modules (Test in my example) that implement a protocool (Proto) that specifies an additional init function, but then the modules use again to more generalized functions (SpecializedProto in my example).

al2o3cr

al2o3cr

This isn’t what protocols are for, and probably (I have not run it) doesn’t work like you really want.

For the above declaration to make sense, you’d need to be calling Proto.foo with a %SpecializedProto{} struct.

Protocols solve a particular problem: how to make a function handle many data types that aren’t known by the function’s original authors. A simple example from the stdlib is String.Chars, which defines to_string and is used by the kernel to_string function.

Without protocols, the only way to write String.Chars.to_string would be with many heads:

defmodule String.Chars 
  # non-protocol version
  def to_string(v) when is_integer(v), do: ...
  def to_string(v) when is_map(v), do: ...
  def to_string(v) when is_list(v), do: ...
  # etc
end

However, having custom formatting for user-defined structs would require adding new clauses to String.Chars.to_string which isn’t supported (or particularly practical).

Protocols let you do that kind of dispatching because defimpls don’t have to be supplied all-at-once like defs.


A general suggestion when prototyping something like this: start out by writing a straightforward implementation that does what you want and uses the components you’re creating. Copy-paste code FREELY during this initial part, but try to keep changes to copied code clearly labeled.

Once you’ve got things sketched out enough, only then start thinking about how to DRY things out / reduce boilerplate / do macro-fu / etc. It will be a lot more obvious where you need flexibility like protocols with working code.

denna

denna OP

This isn’t what protocols are for, and probably (I have not run it) doesn’t work like you really want.

it does work because Test is a atom. so it uses:

defimpl Proto, for: Atom do

so it would be in this case what is in oo speak a singleton

i realized i maybe simplified my example to much so now it want it to be possible with a struct or a Modul-Atom in the call:

defprotocol Proto do
  @spec foo(t()) :: charlist()
  def foo(item)
end

defprotocol SpecializedProto do
  @spec info(t()) ::  charlist()
  def info(t)
end

defimpl Proto, for: Atom do
  @impl true
  def foo(item) do
    apply(item,:foo,[item])
  end
end

defimpl SpecializedProto, for: Atom do
  @impl true
  def info(item) do
    apply(item,:info,[item])
  end
end

defimpl Proto, for: SpecializedProto do
  @impl true
  def foo(item) do
    SpecializedProto.info(item)
  end
end

defmodule Test do
  defstruct []
  defdelegate info(x), to: SpecializedProto.Test
  defdelegate foo(x), to: Proto.Test
end

defimpl SpecializedProto, for: Test  do
  def info(_), do: "test"
end

defimpl Proto, for: Test  do
  defdelegate foo(x), to: Proto.SpecializedProto
end


#IO.puts(Proto.foo(Test))
#IO.puts(Proto.foo(%Test{}))

what is not nice is the use of apply and defdelegate because the implementation is positioned in another module again.

al2o3cr

al2o3cr

Your example is more readable if some of the reuse of the name SpecializedProto is removed (here are the changed clauses):

defimpl Proto, for: UnimportantName do
  @impl true
  def foo(item) do
    SpecializedProto.info(item)
  end
end

defimpl Proto, for: Test  do
  defdelegate foo(x), to: Proto.UnimportantName
end

I’m still not understanding the intent of passing an atom versus a struct in these functions, but I suspect that’s because that argument never gets used.

For instance, think about the path that a call like SpecializedProto.info(Test) takes:

  • dispatched to the Atom implementation, calls Test.info(Test)
  • Test.info(Test) delegates to SpecializedProto.Test.info(Test)
  • the SpecializedProto implementation for Test structs then receives the atom Test instead of a struct

Perhaps something like this would be better:

defimpl SpecializedProto, for: Atom do
  @impl true
  def info(item) do
    apply(item,:info,[struct(item)])
  end
end

Then you don’t need the defdelegate info(...) on Test, and SpecializedProto.Test always gets the struct that it expects.

— All posts loaded —

Where Next? Top

Trending in Questions Top

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
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
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
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
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews