We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Act On It
Try building a couple of new monoids on your own:
• Product: Combines numbers through multiplication.
• Min: Selects the smallest value using an Ord instance.
Follow the same pattern you saw in Sum and Max: implement empty/1, append/2, wrap/2, and unwrap/1. The goal is to get comfortable applying the Monoid structure in different contexts.
Product
For this we will create a new module for Product and go from there.
defmodule FunPark.Monoid.ActProduct do
defstruct value: 1
end
defimpl FunPark.Monoid, for: FunPark.Monoid.ActProduct do
alias FunPark.Monoid.ActProduct, as: Product
def empty(_), do: %Product{}
def append(%Product{value: value_a}, %Product{value: value_b}),
do: %Product{value: value_a * value_b}
def wrap(%Product{}, value) when is_number(value), do: %Product{value: value}
def unwrap(%Product{value: value}), do: value
end
Let’s test it out
iex(69)> prod_a = %FunPark.Monoid.ActProduct{value: 1}
%FunPark.Monoid.ActProduct{value: 1}
iex(70)> prod_b = %FunPark.Monoid.ActProduct{value: 2}
%FunPark.Monoid.ActProduct{value: 2}
iex(71)> prod_a = FunPark.Monoid.wrap(%FunPark.Monoid.ActProduct{}, 1)
%FunPark.Monoid.ActProduct{value: 1}
iex(72)> prod_b = FunPark.Monoid.wrap(%FunPark.Monoid.ActProduct{}, 2)
%FunPark.Monoid.ActProduct{value: 2}
iex(73)> value = FunPark.Monoid.append(prod_a, prod_b)
%FunPark.Monoid.ActProduct{value: 2}
iex(74)> value = FunPark.Monoid.append(value, prod_b)
%FunPark.Monoid.ActProduct{value: 4}
Okay so now lets add in a wrapper within the utils module so that we can auto-wrap and then send it back unwrapped. This was already there.
def m_append(monoid, a, b) when is_struct(monoid) do
append(wrap(monoid, a), wrap(monoid, b)) |> unwrap()
end
def m_concat(monoid, values) when is_struct(monoid) and is_list(values) do
fold_l(values, empty(monoid), fn value, acc ->
append(acc, wrap(monoid, value))
end)
|> unwrap()
end
Now let’s test it out.
iex(76)> FunPark.Monoid.Utils.m_append(%FunPark.Monoid.ActProduct{}, 2, 4)
8
Now let’s add that to the Math module
def product(a, b) do
m_append(%Monoid.ActProduct{}, a, b)
end
def product(list) when is_list(list) do
m_concat(%Monoid.ActProduct{}, list)
end
Now let’s test it out.
iex(78)> FunPark.Math.product(2, 4)
8
iex(79)> FunPark.Math.product(2, 8)
16
iex(80)> FunPark.Math.product([2, 4, 8])
64
Min
Okay so we need to use some of the same logic for this next one.
defmodule FunPark.Monoid.Min do
defstruct value: nil, id: nil, ord: FunPark.Ord
end
defimpl FunPark.Monoid, for: FunPark.Monoid.Min do
alias FunPark.Monoid.Min
alias FunPark.Ord.Utils
def empty(%Min{id: id, ord: ord}), do: %Min{value: id, id: id, ord: ord}
def append(%Min{value: a, ord: ord} = min1, %Min{value: b}) do
%Min{min1 | value: Utils.min(a, b, ord)}
end
def wrap(%Min{ord: ord}, value) do
%Min{value: value, ord: Utils.to_ord_map(ord)}
end
def unwrap(%Min{value: value}), do: value
end
Let’s test it out.
iex(82)> min_a = FunPark.Monoid.wrap(%FunPark.Monoid.Min{}, 2)
%FunPark.Monoid.Min{
value: 2,
id: nil,
ord: %{
lt?: &FunPark.Ord.lt?/2,
gt?: &FunPark.Ord.gt?/2,
ge?: &FunPark.Ord.ge?/2,
le?: &FunPark.Ord.le?/2
}
}
iex(83)> min_b = FunPark.Monoid.wrap(%FunPark.Monoid.Min{}, 4)
%FunPark.Monoid.Min{
value: 4,
id: nil,
ord: %{
lt?: &FunPark.Ord.lt?/2,
gt?: &FunPark.Ord.gt?/2,
ge?: &FunPark.Ord.ge?/2,
le?: &FunPark.Ord.le?/2
}
}
iex(84)> FunPark.Monoid.append(min_a, min_b)
%FunPark.Monoid.Min{
value: 2,
id: nil,
ord: %{
lt?: &FunPark.Ord.lt?/2,
gt?: &FunPark.Ord.gt?/2,
ge?: &FunPark.Ord.ge?/2,
le?: &FunPark.Ord.le?/2
}
}
Okay so now let’s add in a wrapper so we can just test values without a wrapper within the Ord.Utils, again this was already there.
def m_append(monoid, a, b) when is_struct(monoid) do
append(wrap(monoid, a), wrap(monoid, b)) |> unwrap()
end
def m_concat(monoid, values) when is_struct(monoid) and is_list(values) do
fold_l(values, empty(monoid), fn value, acc ->
append(acc, wrap(monoid, value))
end)
|> unwrap()
end
Let’s test it out.
iex(84)> FunPark.Monoid.append(min_a, min_b)
%FunPark.Monoid.Min{
value: 2,
id: nil,
ord: %{
lt?: &FunPark.Ord.lt?/2,
gt?: &FunPark.Ord.gt?/2,
ge?: &FunPark.Ord.ge?/2,
le?: &FunPark.Ord.le?/2
}
}
iex(85)> FunPark.Monoid.Utils.m_append(%FunPark.Monoid.Min{}, 2, 3)
2
iex(86)> FunPark.Monoid.Utils.m_concat(%FunPark.Monoid.Min{}, [3, 4, 5])
3
Now we can add it to the Math module.
def min(a, b) do
m_append(%Monoid.Min{id: Float.max_finite()}, a, b)
end
def min(list) when is_list(list) do
m_concat(%Monoid.Min{id: Float.max_finite()}, list)
end
Now let’s test it out.
iex(87)> FunPark.Math.min(4, 5)
4
iex(88)> FunPark.Math.min(4, 8)
4
iex(89)> FunPark.Math.min(6,3)
3