angelmz

angelmz

Wanting to know more about the "Elixir" way of doing things

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:

#go

Marked As Solved

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.

Also Liked

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)

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.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New

We're in Beta

About us Mission Statement