angelmz

angelmz

I’m still fairly new to the language. I wanted to know more about the “Elixir” way of doing things so I wrote a tiny program 3 ways, but I’m not sure which is the “right way”. I know you’d normally want only one module per page, this is just for demonstration purposes.

This one has the most isoloation of responsibilities:

defmodule Shapes do
  defmodule Rectangle do
    defstruct [:width, :height]

    def perimeter(%Rectangle{width: width, height: height}) do
      2 * (width + height)
    end

    def area(%Rectangle{width: width, height: height}) do
      width * height
    end
  end

  defmodule Circle do
    defstruct [:radius]

    def area(%Circle{radius: radius}) do
      radius * radius * :math.pi()
    end
  end
end

This one leverages pattern matching a little more.

defmodule Shape do
  defmodule Rectangle do
    defstruct [:width, :height]
  end

  defmodule Circle do
    defstruct [:radius]
  end

  def perimeter(%Rectangle{width: width, height: height}) do
    2 * (width + height)
  end

  def area(%Rectangle{width: width, height: height}) do
    width * height
  end

  def area(%Circle{radius: radius}) do
    radius * radius * :math.pi()
  end
end

This is my attempt at implementing it as a protocol. I’m a little unsure
because to my understanding protocols are for taking in different types.

defprotocol Shape do
  def area(data)
  def perimeter(data)
end

defmodule Rectangle do
  defstruct [:width, :height]

  defimpl Shape, for: Rectangle do
    def area(%Rectangle{width: width, height: height}) do
      width * height
    end

    def perimeter(%Rectangle{width: width, height: height}) do
      2 * (width + height)
    end
  end
end

defmodule Circle do
  defstruct [:radius]

  defimpl Shape, for: Circle do
    def area(%Circle{radius: radius}) do
      :math.pi() * radius * radius
    end
  end
end

Thank you for any help :slight_smile:

Showing Posts 1 to 6

ityonemo

ityonemo

You don’t need the for: when the defimpl is nested in the module it’s implementing for.

If this were actually intended to be reused I would also maybe make your modules be Shape.Rectangle and Shape.Circle to avoid module name piracy (keep as many top level namespaces open, we have aliases to help readability)

gregvaughn

gregvaughn

This sample project is almost a textbook example of polymorphism. Protocols are Elixir’s core way of supporting polymorphism. That being said, your second approach is not bad when you have limited expectations of the datatypes to support.

The main thing a protocol offers over the second approach is that you could publish the Protocol in one package. Then some other library author could choose to make their new data structure (which was unknown to you when you wrote the Protocol) fit into it.

adamu

adamu

I think your protocols version is great, but you should implement all the functions:

def perimeter(%Circle{radius: radius}) do
  2 * :math.pi() * radius
end

It seems that even structs that don’t implement all the functions in a protocol have dummy placeholders. Here’s an example from the core library for Function, which doesn’t implement all the functions of Enumerable:

https://github.com/elixir-lang/elixir/blob/6730d669fb319411f8e411d4126f1f4067ef9231/lib/elixir/lib/enum.ex#L4778-L4780

LostKobrakai

LostKobrakai

This is special though. All the “optional” callbacks of Enumerable can be implemented using the non optional reduce/3. The optional ones are only useful when the underlying datastructures allow for more efficient ways for those operations than using reduce/3. So you can still use Enum.count on any Enumerable, even if the implementation returns {:error, __MODULE__}.

That’s different to not implementing part of a protocol.

Also I think @optional_callbacks is the more appropriate way to mark optional callbacks, but Enumerable likely can’t do so for historical reasons.

adamu

adamu

Thanks for pointing that out - I didn’t check the implementation of Enumerable properly when I wrote that. I was looking for an example of a protocol that defines multiple functions, and an implementation that doesn’t implement them all. All the other protocols I can think of only define one function :thinking:

ityonemo

ityonemo

Protocols can’t have missing functions.

Behaviours can and usually the way to check is using function_exported?/3

— 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
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
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
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews