bibekp

bibekp

I am trying to refactor a module to incorporate protocols while still leaving part of the base module while handling polymorphism but am having some questions/challenges..

I have a module that handles a Strategy, it has some basic methods like new, setup, update. It also has a method called optimal that should be polymorphic.

Function optimal depends on the type of strategy. Right now the module implements one particular type of Strategy, let’s call this DominantStrategy. But eventually, I want to create another called MixedStrategy. I would like to reuse the base Strategy methods new, setup, update, while specifying the individual strategy’s implementation of optimal separately.

defmodule Strategy do
  defstruct ...

  def new(args) do ... end
  def setup(%Strategy{} = s, data) do ... end
  def update(%Strategy{} = s, data) do ... end
  def optimal(%Strategy{} =s) do ... end
end

Let’s say I were to create a protocol called Decision which declares optimal

defprotocol Decision do
  def optimal() 
end

Then I would create the code implementing the protocol like so:

defmodule DominantStrategy do
  defstruct ...
  def optimal(%DominantStrategy{} = strategy) do
    ...
  end

  defimpl Decision, for: DominantStrategy do
    def optimal(%DominantStrategy{} = strategy) do
     DominantStrategy.optimal(strategy)
    end
end

defmodule MixedStrategy do
  defstruct ...
  def optimal(%MixedStrategy{} = strategy) do
    ...
  end
  defimpl Decision, for: MixedStrategy do
    def optimal(%MixedStrategy{} = strategy) do
     MixedStrategy.optimal(strategy)
    end
end

So my question is: how do I make this work with the Strategy module if the DominantStrategy and MixedStrategy structs are the same as the Strategy struct? Is there a more FP way to handle this?

Specifically:

  • Do I eliminate the Strategy struct and hence the %Strategy{} = s pattern match? 
    
  • Do I operate on the DominantStrategy struct and MixedStrategy struct generically in my top level methods like setup/update?
    
  • Does Strategy.new now return the specific Strategy implementation struct?
    
  • How to ensure the different structs (DominantStrategy, MixedStrategy) adhere to a common structure?
    

    defmodule Strategy do
    def new(args) do
    case args.type do
    :mixed → MixedStrategy.new(args)
    :dominant → DominantStrategy.new(args)
    _ → raise “Error”
    end

    def setup(s, data) do ... end
    def update(s, data) do ... end
    def optimal(s) do Decision.optimal(s) end
    

    end

Apologies in advance if this question type has been previously asked. A link would be appreciated.

If not, I would appreciate suggestions on the best way to go about this

PS. The presentation Well-Typed Elixir briefly touches on this.

– Thank you
Bibek

Showing Posts 1 to 3

bbense

bbense

It really sounds like you want a behaviour, not a protocol.

Protocols are for calling the same function on different data types.

Behaviours are for a custom implementation of a function for each module. In essence a behaviour is a promise that your module implements a function of name X with arity Y. So that if I pass in
the module name to another module it can use

Kernel.apply(module, :optimal, [strategy])

bbense

bbense

In the case of Structs, they are really just Maps with a special key that identifies the module that they are associated with.

iex> foo = 1..3
iex> bar = Map.get(foo, :“__struct__”)
iex> Kernel.apply(bar, :“range?”, [foo]) == Range.range?(foo)

bibekp

bibekp OP

Thanks there @bbense.

I guess more than passing around different types I am more passing around modules – hence the use of behaviors.

This older post helps to distinguish between the different types of polymorphic activity

My only minimal requirement would be for another module to abstract away the custom behaviors so my client code doesn’t need to worry about DominantStrategy vs. MixedStrategy

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
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
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
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
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
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
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 & 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