Qqwy
FunLand: Algebraic("Container") Data Types for Elixir - Prerelease
Because of popular demand, and because I’ll probably be busy the next couple of days, so it would need to wait a lot longer if I didn’t publish it now, here it is:
FunLand: This is a package that adds a couple behavours to your Elixir application, which you can use to define Algebraic Data Types.
What exactly are Algebraic Data Types?
They are basically containers for simpler types, in all kinds and shapes.
Some common examples are:
- Lists (as use already every day in Elixir)
- Tuples
- Trees
- Maybe, which either contains a single value or nothing. This allows for propagation of failures in more complex operations.
- Writers, which allow you to keep track of something (such as a log) in the background while passing it through multiple operations that work on simple values.
Why are Algebraic Data Types useful?
Algebraic Data Types are useful in the same way that using loops is useful: They let you re-use a set of operations you already had on a much larger set of inputs/problems.
For instance, Mappable.map lets you re-use any function that works on a single simple type, to tranform the contents of a collection of things of that type:
Mappable.map(input, fn x -> x*2 end) would transform the list [1,2,3] into the list [2,4,6], the tuple {3,1,4} into {6,2,8}, Maybe.just(6) into Maybe.just(12), etc.
This pre-release of FunLand is mainly because I would like some feedback, and to find out if the design choices I have made so far are sound. To implement Abstract Data Types turned out to be a larger endeavour than I had expected. I hope that FunLand will be able to explain to newcomers how ADTs work and why they are useful, and make it easy for people to define their own.
also, Pull Requests are very welcome! ![]()
Sincerely,
~Wiebe-Marten/Qqwy
Most Liked
X4lldux
There was a proposition for that, see elixir-lang/elixir#925. Based on that I’ve implemented this library disc_union. It uses a little different naming, but basically works just as you explain it. The example below is an implementation of tennis kata:
defmodule Player do
use DiscUnion
defunion A | B
end
defmodule PlayerPoints do
use DiscUnion
defunion Love | Fifteen | Thirty | Forty
end
defmodule Score do
use DiscUnion
require PlayerPoints
defunion Points in PlayerPoints * PlayerPoints
| Advantage in Player
| Deuce
| Game in Player
end
defmodule Tennis do
require Player
require PlayerPoints
require Score
def score_point(%Score{}=score, %Player{}=point_player) do
IO.puts "Point for player #{inspect point_player} @ #{inspect score}"
Score.case score do
Advantage in ^point_player -> Score.game point_player
Advantage in _ -> Score.deuce
Deuce -> Score.advantage point_player
Points in PlayerPoints.forty, _ when point_player==Player.a -> Score.game Player.a
Points in _, PlayerPoints.forty when point_player==Player.b -> Score.game Player.b
Points in a, b when point_player==Player.a -> Score.points(next_point_score(a), b) |> normalize_score
Points in a, b when point_player==Player.b -> Score.points(a, next_point_score(b)) |> normalize_score
Game in _ -> IO.puts "Game is over #{inspect score}"
end
end
defp next_point_score(%PlayerPoints{}=point) do
PlayerPoints.case point do
Love -> PlayerPoints.fifteen
Fifteen -> PlayerPoints.thirty
Thirty -> PlayerPoints.forty
Forty -> raise "WAT?"
end
end
defp normalize_score(%Score{}=score) do
Score.case score, allow_underscore: true do
Points in PlayerPoints.forty, PlayerPoints.forty -> Score.deuce
_ -> score
end
end
end
I admit DSL syntax is inspired by OCaml.
It even generates compile-time warnings when one of defined cases is not covered in case body.
NobbZ
You describe ADTs as Containers for some other types, thats not the only truth. ADTs are much more complex.
When introducing ADTs I’d do roughly the following order:
- Simple enumerations as in
data Bool = False | Trueordata Fruits = Apple | Peach | Orange. - Record/Struct Like as in
data Person = Person String Date. - Replacement for tagged unions as in
data NPC = Monster String Int | Merchant String [(Item, Int)]. - Introduce type-variables as in
data Maybe a = Just a | Nothingand explain that this is an extension to the former ones.
ADT itself are not necessary to define classes (Haskell term) or interfaces (Idris term) since they are very similar to what the OO-World does call an interface ever since.
As you can see, ADT is more or less an abstraction of what the C world does know as 3 separate concepts: enum, struct, and (tagged) union.
sotojuan
FYI for those interested here are some more related projects:
https://github.com/expede/algae
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









