{"id":13836609,"url":"https://github.com/witchcrafters/algae","last_synced_at":"2025-05-16T15:09:38.325Z","repository":{"id":37023289,"uuid":"46647095","full_name":"witchcrafters/algae","owner":"witchcrafters","description":"Bootstrapped algebraic data types for Elixir","archived":false,"fork":false,"pushed_at":"2022-10-22T06:56:39.000Z","size":691,"stargazers_count":338,"open_issues_count":21,"forks_count":19,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-05-14T23:33:05.255Z","etag":null,"topics":["adt","algebraic-data-types","data-structures","dsl"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/algae","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/witchcrafters.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-22T04:59:54.000Z","updated_at":"2025-03-15T16:08:00.000Z","dependencies_parsed_at":"2022-07-16T23:16:15.565Z","dependency_job_id":null,"html_url":"https://github.com/witchcrafters/algae","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Falgae","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Falgae/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Falgae/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/witchcrafters%2Falgae/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/witchcrafters","download_url":"https://codeload.github.com/witchcrafters/algae/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553958,"owners_count":22090417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adt","algebraic-data-types","data-structures","dsl"],"created_at":"2024-08-04T15:00:50.906Z","updated_at":"2025-05-16T15:09:33.316Z","avatar_url":"https://github.com/witchcrafters.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"![](https://github.com/robot-overlord/algae/blob/main/brand/logo.png?raw=true)\n\n[![Build Status](https://travis-ci.org/expede/algae.svg?branch=master)](https://travis-ci.org/expede/algae) [![Inline docs](http://inch-ci.org/github/expede/algae.svg?branch=master)](http://inch-ci.org/github/expede/algae) [![Deps Status](https://beta.hexfaktor.org/badge/all/github/expede/algae.svg)](https://beta.hexfaktor.org/github/expede/algae) [![hex.pm version](https://img.shields.io/hexpm/v/algae.svg?style=flat)](https://hex.pm/packages/algae) [![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](http://hexdocs.pm/algae/) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/expede/algae/blob/master/LICENSE)\n\nAlgae provides a boilerplate-avoiding DSL for defining algebraic data types (ADTs),\nplus several common structures\n\n# Quickstart\nAdd Algae to your list of dependencies in `mix.exs`:\n\n```elixir\n\ndef deps do\n  [{:algae, \"~\u003e 1.2\"}]\nend\n\n```\n\n# Table of Contents\n\n- [Product Builder](#product-builder)\n  - [Definition DSL](#definition-dsl)\n  - [Constructor](#constructor)\n  - [Empty Tag](#empty-tag)\n- [Sum Builder](#sum-builder)\n  - [Default Constructor](#default-constructor)\n  - [Tagged Unions](#tagged-unions)\n- [A Sampling of ADTs](#a-sampling-of-adts)\n  - [`Id`](#algaeid)\n  - [`Maybe`](#algaemaybe)\n  - [`Tree.BinarySearch`](#algaetreebinarysearch)\n\n---\n\n\u003e **NOTE**  \n\u003e Please `import Algae` before trying out the examples below.\n\u003e The samples assume that is has already been done to remove\n\u003e the unnecessary clutter.\n\n---\n\n# Product Builder\nBuild a product type\n\nIncludes:\n\n* Struct\n* Type definition\n* Constructor function (for piping and defaults)\n* Implicit defaults for simple values\n\n## Definition DSL\n\nFor convenience, several variants of the DSL are available.\n\n### Standard\n\n```elixir\ndefmodule Player do\n  # =============== #\n  # Data Definition #\n  # =============== #\n\n  defdata do\n    name       :: String.t()\n    hit_points :: non_neg_integer()\n    experience :: non_neg_integer()\n  end\n\n  # =================== #\n  #    Rest of Module   #\n  # (business as usual) #\n  # =================== #\n\n  @spec attack(t(), t()) :: {t(), t()}\n  def attack(%{experience: xp} = player, %{hit_points: hp} = target) do\n    {\n      %{player | experience: xp + 50},\n      %{target | hit_points: hp - 10}\n    }\n  end\nend\n\n#=\u003e %Player{name: \"Sir Bob\", hit_points: 10, experience: 500}\n```\n\n### Single Field Shorthand\n\nWithout any fields specified, Algae will default to a single field with\nthe same name as the module (essentially a \"wrapper type\"). You must still\nprovide the type for this field, however.\n\nEmbedded in another module:\n\n```elixir\ndefmodule Id do\n  defdata any()\nend\n\n%Id{}\n#=\u003e %Id{id: nil}\n```\n\nStandalone:\n\n```elixir\ndefdata Wrapper :: any()\n\n%Wrapper{}\n#=\u003e %Wrapper{wrapper: nil}\n```\n\n## Constructor\n\nA helper function, especially useful for piping. The order of arguments is\nthe same as the order that they are defined in.\n\n```elixir\ndefmodule Person do\n  defdata do\n    name :: String.t()\n    age  :: non_neg_integer()\n  end\nend\n\nPerson.new(\"Rachel Weintraub\")\n#=\u003e %Person{\n#     name: \"Rachel Weintraub\",\n#     age:  0\n#   }\n```\n\n### Constructor Defaults\n\nFields will automatically default to a sensible value (a typical \"zero\" for\nthat datatype). For example, `non_neg_integer()` will default to `0`,\nand `String.t()` will default to `\"\"`.\n\nYou may also overwrite these defaults with the `\\\\` syntax.\n\n```elixir\ndefmodule Pet do\n  defdata do\n    name      :: String.t()\n    leg_count :: non_neg_integer() \\\\ 4\n  end\nend\n\nPet.new(\"Crookshanks\")\n#=\u003e %Pet{\n#     name: \"Crookshanks\",\n#     leg_count: 4\n#   }\n\nPet.new(\"Paul the Psychic Octopus\", 8)\n#=\u003e %Pet{\n#     name: \"Paul the Psychic Octopus\",\n#     leg_count: 8\n#   }\n```\n\nThis overwriting syntax is _required_ for complex types:\n\n```elixir\ndefdata Grocery do\n  item :: {String.t(), integer(), boolean()} \\\\ {\"Orange\", 4, false}\nend\n\nGrocery.new()\n#=\u003e %Grocery{\n#     item: {\"Orange\", 4, false}\n#   }\n```\n\n### Overwrite Constructor\n\nThe `new` constructor function may be overwritten.\n\n```elixir\ndefmodule Constant do\n  defdata :: fun()\n\n  def new(value), do: %Constant{constant: fn _ -\u003e value end}\nend\n\nfourty_two = Constant.new(42)\nfourty_two.constant.(33)\n#=\u003e 42\n```\n\n## Empty Tag\n\nAn empty type (with no fields) is definable using the `none`() type\n\n```elixir\ndefmodule Nothing do\n  defdata none()\nend\n\nNothing.new()\n#=\u003e %Nothing{}\n```\n\n# Sum Builder\n\nBuild a sum (coproduct) type from product types\n\n```elixir\ndefmodule Light do\n  # ============== #\n  # Sum Definition #\n  # ============== #\n\n  defsum do\n    defdata Red    :: none()\n    defdata Yellow :: none()\n    defdata Green  :: none()\n  end\n\n  # =================== #\n  #    Rest of Module   #\n  # (business as usual) #\n  # =================== #\n\n  def from_number(1), do: %Light.Red{}\n  def from_number(2), do: %Light.Yellow{}\n  def from_number(3), do: %Light.Green{}\nend\n\nLight.new()\n#=\u003e %Light.Red{}\n```\n\n## Embedded Products\n\nData with multiple fields can be defined directly as part of a sum\n\n```elixir\ndefmodule Pet do\n  defsum do\n    defdata Cat do\n      name :: String.t()\n      claw_sharpness :: String.t()\n    end\n\n    defdata Dog do\n      name :: String.t()\n      bark_loudness :: non_neg_integer()\n    end\n  end\nend\n```\n\n## Default Constructor\n\nThe first `defdata`'s constructor will be the default constructor for the sum\n\n```elixir\ndefmodule Maybe do\n  defsum do\n    defdata Nothing :: none()\n    defdata Just    :: any()\n  end\nend\n\nMaybe.new()\n#=\u003e %Maybe.Nothing{}\n```\n\n## Tagged Unions\n\nSums join existing types with tags: new types to help distinguish the context\nthat they are in (the sum type)\n\n```elixir\ndefdata Book  :: String.t() \\\\ \"War and Peace\"\ndefdata Video :: String.t() \\\\ \"2001: A Space Odyssey\"\n\ndefmodule Media do\n  defsum do\n    defdata Paper :: Book.t()\n    defdata Film  :: Video.t() \\\\ Video.new(\"A Clockwork Orange\")\n  end\nend\n\nmedia = Media.new()\n#=\u003e %Paper{\n#      paper: %Book{\n#        book: \"War and Peace\"\n#      }\n#   }\n```\n\n# A Sampling of ADTs\n\nSee [complete docs](https://hexdocs.pm/algae) for more\n\n## `Algae.Id`\n\nThe simplest ADT: a simple wrapper for some data\n\n```elixir\n%Algae.Id{id: \"hi!\"}\n```\n\n## `Algae.Maybe`\n\nMaybe represents the presence or absence of something.\n\nPlease note that `nil` is actually a value, as it can be passed to functions!\n`nil` is not bottom!\n\n```elixir\nAlgae.Maybe.new()\n#=\u003e %Algae.Maybe.Nothing{}\n\nAlgae.Maybe.new(42)\n#=\u003e %Algae.Maybe.Just{just: 42}\n```\n\n## `Tree.BinarySearch`\n\n```elixir\nalias Algae.Tree.BinarySearch, as: BTree\n\n#   42\n#  /  \\\n# 77  1234\n#     /  \\\n#    98  32\n\nBTree.Branch.new(\n  42,\n  BTree.Branch.new(77),\n  BTree.Branch.new(\n    1234,\n    BTree.Branch.new(98),\n    BTree.Branch.new(32)\n  )\n)\n\n#=\u003e %Algae.Tree.BinarySearch.Branch{\n#     value: 42,\n#     left: %Algae.Tree.BinarySearch.Branch{\n#       value: 77,\n#       left:  %Algae.Tree.BinarySearch.Empty{},\n#       right: %Algae.Tree.BinarySearch.Empty{}\n#     },\n#     right: %Algae.Tree.BinarySearch.Branch{\n#       value: 1234,\n#       left:  %Algae.Tree.BinarySearch.Branch{\n#         value: 98,\n#         left:  %Algae.Tree.BinarySearch.Empty{},\n#         right: %Algae.Tree.BinarySearch.Empty{}\n#       },\n#       right: %Algae.Tree.BinarySearch.Branch{\n#         value: 32,\n#         left:  %Algae.Tree.BinarySearch.Empty{},\n#         right: %Algae.Tree.BinarySearch.Empty{}\n#       }\n#     }\n#   }\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwitchcrafters%2Falgae","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwitchcrafters%2Falgae","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwitchcrafters%2Falgae/lists"}