Want to see the full-length video right now for free?
The original code may look something like this,
defmodule DrawShapes do
def areas(shapes) do
for shape <- shapes do
area(shape)
end
end
def area(shape) do
case shape.shape_type do
:circle ->
(shape.radius * :math.pi) |> :math.pow(2)
:square ->
shape.side |> :math.pow(2)
end
end
end
shapes = [
%{shape_type: :circle, radius: 3},
%{shape_type: :square, side: 4}
]
DrawShapes.areas(shapes)
And we could extract higher level data types,
defmodule DrawShapes do
def areas(shapes) do
for shape <- shapes do
area(shape)
end
end
def area(shape) do
module = shape.__struct__
apply(module, :area, [shape])
end
end
defmodule Circle do
defstruct radius: nil
def new(radius), do: %Circle{radius: radius}
def area(circle) do
(circle.radius * :math.pi) |> :math.pow(2)
end
end
defmodule Square do
defstruct side: nil
def new(side), do: %Square{side: side}
def area(square) do
square.side |> :math.pow(2)
end
end
shapes = [Circle.new(3), Square.new(4)]
DrawShapes.areas(shapes)
We can also define a protocol for how an abstraction should behave. This is a powerful tool in Elixir and it allows us to get polymorphic behavior.
defmodule Circle do
defstruct radius: nil
def new(radius), do: %Circle{radius: radius}
end
defmodule Square do
defstruct side: nil
def new(side), do: %Square{side: side}
end
defprotocol Shape do
def area(shape)
end
defimpl Shape, for: Circle do
def area(circle) do
(circle.radius * :math.pi) |> :math.pow(2)
end
end
defimpl Shape, for: Square do
def area(square) do
square.side * square.side
end
end
defmodule DrawShapes do
def areas(shapes) do
for shape <- shapes do
area(shape)
end
end
def area(shape) do
Shape.area(shape)
end
end
shapes = [Circle.new(2), Square.new(2)]
DrawShapes.areas(shapes)