From 3e0566bbe3b619b2aa2f0ea359f3ca8eb5478874 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 31 Jul 2017 17:21:43 -0700 Subject: [PATCH] Beta 1 (#36) Major improvements to structure, docs, and tests - Added Semigroupoid -> Category -> Arrow - Added Foldable -> Traversable - Added Extend -> Comonad - Added Bifunctor - Fixed do-notation - Split `chain do` and `monad _ do` --- .travis.yml | 4 +- README.md | 58 +- brand/Icon/PNG/WC-icon-sml@2x-circle.png | Bin 0 -> 15159 bytes docs/all.json | 1 + lib/witchcraft.ex | 48 +- lib/witchcraft/applicative.ex | 151 ++++- lib/witchcraft/apply.ex | 379 +++++++++++-- lib/witchcraft/arrow.ex | 437 +++++++++++++++ lib/witchcraft/bifunctor.ex | 179 +++++- lib/witchcraft/category.ex | 70 ++- lib/witchcraft/chain.ex | 473 ++++++++++++++++ lib/witchcraft/chain/ast.ex | 8 + lib/witchcraft/chainable.ex | 55 -- lib/witchcraft/comonad.ex | 103 ++++ lib/witchcraft/extend.ex | 340 +++++++++++ lib/witchcraft/extendable.ex | 35 -- lib/witchcraft/foldable.ex | 686 +++++++++++++++-------- lib/witchcraft/foldable/bitstring.ex | 9 + lib/witchcraft/foldable/empty_error.ex | 28 +- lib/witchcraft/foldable/list.ex | 5 + lib/witchcraft/foldable/map.ex | 5 + lib/witchcraft/foldable/tuple.ex | 9 + lib/witchcraft/functor.ex | 189 +++++-- lib/witchcraft/monad.ex | 158 ++++-- lib/witchcraft/monad/ast.ex | 17 +- lib/witchcraft/monoid.ex | 86 +-- lib/witchcraft/ord.ex | 195 +++++++ lib/witchcraft/ord/float.ex | 7 + lib/witchcraft/ord/integer.ex | 7 + lib/witchcraft/ord/list.ex | 25 + lib/witchcraft/ord/map.ex | 21 + lib/witchcraft/ord/string.ex | 7 + lib/witchcraft/ord/tuple.ex | 15 + lib/witchcraft/orderable.ex | 89 --- lib/witchcraft/orderable/order.ex | 95 ---- lib/witchcraft/semigroup.ex | 78 ++- lib/witchcraft/semigroupoid.ex | 140 +++++ lib/witchcraft/setoid.ex | 105 +++- lib/witchcraft/traversable.ex | 307 ++++++++++ lib/witchcraft/unit.ex | 18 + mix.exs | 8 +- mix.lock | 12 +- test/witchcraft_test.exs | 61 +- 43 files changed, 3880 insertions(+), 843 deletions(-) create mode 100644 brand/Icon/PNG/WC-icon-sml@2x-circle.png create mode 100644 docs/all.json create mode 100644 lib/witchcraft/arrow.ex create mode 100644 lib/witchcraft/chain.ex create mode 100644 lib/witchcraft/chain/ast.ex delete mode 100644 lib/witchcraft/chainable.ex create mode 100644 lib/witchcraft/comonad.ex create mode 100644 lib/witchcraft/extend.ex delete mode 100644 lib/witchcraft/extendable.ex create mode 100644 lib/witchcraft/foldable/bitstring.ex create mode 100644 lib/witchcraft/foldable/list.ex create mode 100644 lib/witchcraft/foldable/map.ex create mode 100644 lib/witchcraft/foldable/tuple.ex create mode 100644 lib/witchcraft/ord.ex create mode 100644 lib/witchcraft/ord/float.ex create mode 100644 lib/witchcraft/ord/integer.ex create mode 100644 lib/witchcraft/ord/list.ex create mode 100644 lib/witchcraft/ord/map.ex create mode 100644 lib/witchcraft/ord/string.ex create mode 100644 lib/witchcraft/ord/tuple.ex delete mode 100644 lib/witchcraft/orderable.ex delete mode 100644 lib/witchcraft/orderable/order.ex create mode 100644 lib/witchcraft/semigroupoid.ex create mode 100644 lib/witchcraft/traversable.ex create mode 100644 lib/witchcraft/unit.ex diff --git a/.travis.yml b/.travis.yml index b7a8bfe..47db95b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: elixir elixir: - - 1.3.2 + - 1.5.0 otp_release: - - 19.0.2 + - 20.0 script: mix test; mix credo --strict after_script: - MIX_ENV=docs mix do deps.get, inch.report diff --git a/README.md b/README.md index b9c6527..1bea098 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](./brand/Wordmark/PNG/WC-wordmark-lrg@2x.png) +![](https://github.com/expede/witchcraft/raw/master/brand/Wordmark/PNG/WC-wordmark-lrg@2x.png) `Witchcraft` is a library providing common algebraic and categorical abstractions to Elixir. (Monoids, functors, monads, arrows, and categories) @@ -7,7 +7,8 @@ A big thank you to [Brandon Labbé](https://dribbble.com/brandonlabbe) for creating the logo to this project -# Table of Contents +# README +## Table of Contents - [Quick Start](#quick-start) - [Values](#values) - [Beginner Friendliness](#beginner-friendliness) @@ -30,6 +31,59 @@ def deps do end ``` +# Relationship to Other Packages +``` +Quark TypeClass + ↘ ↙ + Witchcraft + ↓ + Algae +``` + +* [Quark](https://hex.pm/packages/quark): Standard combinators (`id`, `compose`, &c) +* [TypeClass](https://hex.pm/packages/type_class): Used internally to generate type classes +* [Algae](https://hex.pm/packages/algae): Algebraic data types that implement Witchcraft type classes + +# Hierarchy + +``` + +Semigroupoid Semigroup Setoid Foldable Functor -----------┐ + ↓ ↓ ↓ ↓ ↙ ↓ ↘ | + Category Monoid Ord Traversable Apply Bifunctor | + ↓ ↙ ↘ ↓ + Arrow Applicative Chain Extend + ↘ ↙ ↓ + Monad Comonad + +``` + +It is very common to want everything in a chain. You can import the entire chain +with `use`. For example: + +```elixir +use Witchcraft.Monad +``` + +Any options that you pass to `use` will be propagated all the way down the chain + +```elixir +use Witchcraft.Monad, except: [~>: 2] +``` + +Some modules override `Kernel` operators and functions. While this is generally safe, +if you would like to skip all overrides, pass `override_kernel: false` as an option + +```elixir +use Witchcraft.Foldable, override_kernel: false +``` + +Finally, to import the entire library: + +```elixir +use Witchcraft +``` + # Values ## Beginner Friendliness As much as possible, keep things friendly. Concrete examples are available in the diff --git a/brand/Icon/PNG/WC-icon-sml@2x-circle.png b/brand/Icon/PNG/WC-icon-sml@2x-circle.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6ba3e21abb18e59a940eb90525cad0780af7db GIT binary patch literal 15159 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 literal 0 HcmV?d00001 diff --git a/docs/all.json b/docs/all.json new file mode 100644 index 0000000..33160da --- /dev/null +++ b/docs/all.json @@ -0,0 +1 @@ +{"shell":true,"revision":"c8e9bd4eabb2767f517a9db121dae5d67eae08cf","objects":[{"type":null,"source":"lib/witchcraft.ex:2","object_type":"ModuleObject","moduledoc":"Top level module\n\n## Hierarchy\n\n Semigroupoid Semigroup Setoid Foldable Functor -----------┐\n ↓ ↓ ↓ ↓ ↙ ↓ ↘ |\n Category Monoid Ord Traversable Apply Bifunctor |\n ↓ ↙ ↘ ↓\n Arrow Applicative Chain Extend\n ↘ ↙ ↓\n Monad Comonad\n\n## `use Wicthcraft`\n\nThere is a convenient `use` macro to import *all* functions in the library.\n\n use Witchcraft\n\nThis recursively calls `use` on all children modules.\n\nAny options passed to `use` will be passed down to all dependencies.\n\n use Witchcraft, execpt: [right_fold: 2]\n\nIf you would like to not override the functions and operators from `Kernel`,\nyou can pass the special option `override_kernel: false`.\n\n use Witchcraft, override_kernel: false\n\nThis same style of `use` is also available on all submodules, and follow\nthe dependency chart (above).\n","module":"Elixir.Witchcraft","id":"Witchcraft"},{"type":null,"source":"lib/witchcraft/applicative.ex:4","object_type":"ModuleObject","moduledoc":"`Applicative` extends `Apply` with the ability to lift value into a\nparticular data type or \"context\".\n\nThis fills in the connection between regular function application and `Apply`\n\n data --------------- function ---------------> result\n | | |\n of(Container, data) of(Container, function) of(Container, result)\n ↓ ↓ ↓\n %Container --- %Container ---> %Container\n\n## Type Class\n\nAn instance of `Witchcraft.Applicative` must also implement `Witchcraft.Apply`,\nand define `Witchcraft.Applicative.of/2`.\n\n Functor [map/2]\n ↓\n Apply [ap/2]\n ↓\n Applicative [of/2]\n","module":"Elixir.Witchcraft.Applicative","id":"Witchcraft.Applicative"},{"type":"protocol","source":"lib/witchcraft/applicative.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Applicative` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Applicative`\n","module":"Elixir.Witchcraft.Applicative.Proto","id":"Witchcraft.Applicative.Proto"},{"type":"impl","source":"lib/witchcraft/applicative.ex:167","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Applicative.Proto.Function","id":"Witchcraft.Applicative.Proto.Function"},{"type":"impl","source":"lib/witchcraft/applicative.ex:171","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Applicative.Proto.List","id":"Witchcraft.Applicative.Proto.List"},{"type":"impl","source":"lib/witchcraft/applicative.ex:175","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Applicative.Proto.Tuple","id":"Witchcraft.Applicative.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/apply.ex:4","object_type":"ModuleObject","moduledoc":"An extension of `Witchcraft.Functor`, `Apply` provides a way to map functions\nto their arguments when both are wrapped in the same kind of container.\n\nFor a nice, illustrated introduction,\nsee [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html).\n\n## Graphically\n\nIf function application looks like like\n\n data |> function == result\n\nand a functor looks like this\n\n %Container ~> function == %Container\n\nthen an apply looks like\n\n %Container ~>> %Container == %Container\n\nwhich is similar to function application inside containers, plus the ability to\nattach special effects to applications.\n\n data --------------- function ---------------> result\n %Container --- %Container ---> %Container\n\nThis lets us do functorial things like\n\n* continue applying values to a curried function resulting from a `Witchcraft.Functor.lift/2`\n* apply multiple functions to multiple arguments (with lists)\n* propogate some state (like [`Nothing`](https://hexdocs.pm/algae/Algae.Maybe.Nothing.html#content)\nin [`Algae.Maybe`](https://hexdocs.pm/algae/Algae.Maybe.html#content))\n\nbut now with a much larger number of arguments, reuse partially applied functions,\nand run effects with the function container as well as the data container.\n\n## Examples\n\n iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3])\n [2, 3, 4, 10, 20, 30]\n\n iex> import Witchcraft.Functor\n ...> [100, 200]\n ...> ~> fn(x, y, z) ->\n ...> x * y / z\n ...> end <<~ [5, 2]\n ...> <<~ [100, 50]\n [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]\n # ↓ ↓\n # 100 * 5 / 100 200 * 5 / 50\n\n %Algae.Maybe.Just{just: 42}\n ~> fn(x, y, z) ->\n x * y / z\n end <<~ %Algae.Maybe.Nothing{}\n <<~ %Algae.Maybe.Just{just: 99}\n #=> %Algae.Maybe.Nothing{}\n\n## Type Class\n\nAn instance of `Witchcraft.Apply` must also implement `Witchcraft.Functor`,\nand define `Witchcraft.Apply.ap/2`.\n\n Functor [map/2]\n ↓\n Apply [ap/2]\n","module":"Elixir.Witchcraft.Apply","id":"Witchcraft.Apply"},{"type":"protocol","source":"lib/witchcraft/apply.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Apply` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Apply`\n","module":"Elixir.Witchcraft.Apply.Proto","id":"Witchcraft.Apply.Proto"},{"type":"impl","source":"lib/witchcraft/apply.ex:328","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Apply.Proto.Function","id":"Witchcraft.Apply.Proto.Function"},{"type":"impl","source":"lib/witchcraft/apply.ex:333","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Apply.Proto.List","id":"Witchcraft.Apply.Proto.List"},{"type":"impl","source":"lib/witchcraft/apply.ex:342","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Apply.Proto.Tuple","id":"Witchcraft.Apply.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/arrow.ex:4","object_type":"ModuleObject","moduledoc":"Arrows abstract the idea of computations, potentially with a context.\n\nArrows are in fact an abstraction above monads, and can be used both to\nexpress all other type classes in Witchcraft. They also enable some nice\nflow-based reasoning about computation.\n\nFor a nice illustrated explination,\nsee [Haskell/Understanding arrows](https://en.wikibooks.org/wiki/Haskell/Understanding_arrows)\n\nArrows let you think diagrammatically, and is a powerful way of thinking\nabout flow programming, concurrency, and more.\n\n ┌---> f --------------------------┐\n | v\n input ---> split unsplit ---> result\n | ^\n | ┌--- h ---┐ |\n | | v |\n └---> g ---> split unsplit ---┘\n | ^\n └--- i ---┘\n\n## Type Class\n\nAn instance of `Witchcraft.Arrow` must also implement `Witchcraft.Category`,\nand define `Witchcraft.Arrow.arrowize/2`.\n\n Semigroupoid [compose/2]\n ↓\n Category [identity/1]\n ↓\n Arrow [arrowize/2]\n\n","module":"Elixir.Witchcraft.Arrow","id":"Witchcraft.Arrow"},{"type":"protocol","source":"lib/witchcraft/arrow.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Arrow` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Arrow`\n","module":"Elixir.Witchcraft.Arrow.Proto","id":"Witchcraft.Arrow.Proto"},{"type":"impl","source":"lib/witchcraft/arrow.ex:432","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Arrow.Proto.Function","id":"Witchcraft.Arrow.Proto.Function"},{"type":null,"source":"lib/witchcraft/bifunctor.ex:4","object_type":"ModuleObject","moduledoc":"Similar to `Witchcraft.Functor`, but able to map two functions over two\nseparate portions of some data structure (some product type).\n\nEspecially helpful when you need different hebaviours on different fields.\n\n## Type Class\n\nAn instance of `Witchcraft.Bifunctor` must also implement `Witchcraft.Functor`,\nand define `Witchcraft.Apply.ap/2`.\n\n Functor [map/2]\n ↓\n Bifunctor [bimap/2]\n\n","module":"Elixir.Witchcraft.Bifunctor","id":"Witchcraft.Bifunctor"},{"type":"protocol","source":"lib/witchcraft/bifunctor.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Bifunctor` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Bifunctor`\n","module":"Elixir.Witchcraft.Bifunctor.Proto","id":"Witchcraft.Bifunctor.Proto"},{"type":"impl","source":"lib/witchcraft/bifunctor.ex:157","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Bifunctor.Proto.Tuple","id":"Witchcraft.Bifunctor.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/category.ex:4","object_type":"ModuleObject","moduledoc":"A category is some collection of objects and relationships (morphisms) between them.\n\nThis idea is captured by the idea of an identity function for objects,\nand the ability to compose relationships between objects. In most cases,\nthese are very straightforward and composition and identity are the standard\nfunctions from the `Quark` package or similar.\n\n## Type Class\n\nAn instance of `Witchcraft.Category` must also implement `Witchcraft.Semigroupoid`,\nand define `Witchcraft.Category.identity/1`.\n\n Semigroupoid [compose/2]\n ↓\n Category [identity/1]\n","module":"Elixir.Witchcraft.Category","id":"Witchcraft.Category"},{"type":"protocol","source":"lib/witchcraft/category.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Category` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Category`\n","module":"Elixir.Witchcraft.Category.Proto","id":"Witchcraft.Category.Proto"},{"type":"impl","source":"lib/witchcraft/category.ex:70","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Category.Proto.Function","id":"Witchcraft.Category.Proto.Function"},{"type":null,"source":"lib/witchcraft/chain.ex:4","object_type":"ModuleObject","moduledoc":"Chain function applications on contained data that may have some additional effect\n\nAs a diagram:\n\n %Container --- (data -> %Container) ---> %Container\n\n## Examples\n\n iex> chain([1, 2, 3], fn x -> [x, x] end)\n [1, 1, 2, 2, 3, 3]\n\n alias Algae.Maybe.{Nothing, Just}\n\n %Just{just: 42} >>> fn x -> %Just{just: x + 1} end\n #=> %Just{just: 43}\n\n %Just{just: 42}\n >>> fn x -> if x > 50, do: %Just{just: x + 1}, else: %Nothing{} end\n >>> fn y -> y * 100 end\n #=> %Nothing{}\n\n## Type Class\n\nAn instance of `Witchcraft.Chain` must also implement `Witchcraft.Apply`,\nand define `Witchcraft.Chain.chain/2`.\n\n Functor [map/2]\n ↓\n Apply [ap/2]\n ↓\n Chain [chain/2]\n","module":"Elixir.Witchcraft.Chain","id":"Witchcraft.Chain"},{"type":"protocol","source":"lib/witchcraft/chain.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Chain` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Chain`\n","module":"Elixir.Witchcraft.Chain.Proto","id":"Witchcraft.Chain.Proto"},{"type":"impl","source":"lib/witchcraft/chain.ex:444","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Chain.Proto.Function","id":"Witchcraft.Chain.Proto.Function"},{"type":"impl","source":"lib/witchcraft/chain.ex:452","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Chain.Proto.List","id":"Witchcraft.Chain.Proto.List"},{"type":"impl","source":"lib/witchcraft/chain.ex:460","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Chain.Proto.Tuple","id":"Witchcraft.Chain.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/comonad.ex:4","object_type":"ModuleObject","moduledoc":"The dual of monads, `Comonad` brings an unwrapping function to `Extend`able data.\n\nNote that the unwrapping function (`extract`) *must return a value*, and is not\navailable on many data structres that have an empty element. For example,\nthere is no `Comonad` instance for `List` because we cannot pull a value\nout of `[]`.\n\n## Type Class\n\nAn instance of `Witchcraft.Comonad` must also implement `Witchcraft.Extend`,\nand define `Witchcraft.Comonad.extract/1`.\n\n Functor [map/2]\n ↓\n Extend [nest/1]\n ↓\n Comonad [extract/1]\n","module":"Elixir.Witchcraft.Comonad","id":"Witchcraft.Comonad"},{"type":"protocol","source":"lib/witchcraft/comonad.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Comonad` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Comonad`\n","module":"Elixir.Witchcraft.Comonad.Proto","id":"Witchcraft.Comonad.Proto"},{"type":"impl","source":"lib/witchcraft/comonad.ex:96","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Comonad.Proto.Tuple","id":"Witchcraft.Comonad.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/extend.ex:4","object_type":"ModuleObject","moduledoc":"`Extend` is essentially \"co`Chain`\", meaning that it reverses the relationships\nin `Chain`.\n\nInstead of a flattening operation, we have `nest` which wraps the data in\nan additional layer of itsef.\n\nInstead of a `chain`ing function that acts on raw data and wraps it,\nwe have `extend` which unwraps data, may modify it, and returns the unwrapped value\n\n## Type Class\n\nAn instance of `Witchcraft.Extend` must also implement `Witchcraft.Functor`,\nand define `Witchcraft.Extend.nest/1`.\n\n Functor [map/2]\n ↓\n Extend [nest/1]\n\n","module":"Elixir.Witchcraft.Extend","id":"Witchcraft.Extend"},{"type":"protocol","source":"lib/witchcraft/extend.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Extend` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Extend`\n","module":"Elixir.Witchcraft.Extend.Proto","id":"Witchcraft.Extend.Proto"},{"type":"impl","source":"lib/witchcraft/extend.ex:314","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Extend.Proto.Function","id":"Witchcraft.Extend.Proto.Function"},{"type":"impl","source":"lib/witchcraft/extend.ex:328","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Extend.Proto.List","id":"Witchcraft.Extend.Proto.List"},{"type":"impl","source":"lib/witchcraft/extend.ex:333","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Extend.Proto.Tuple","id":"Witchcraft.Extend.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/foldable.ex:4","object_type":"ModuleObject","moduledoc":"Data that can be folded over to change its structure by altering or combining elements\n\n## Examples\n\n iex> right_fold([1, 2, 3], 0, &+/2) # sum\n 6\n\n## Properties\n\nPeople are working on Foldable properties. This is one of the exceptions to\nthere needing to conform to properties. In the meantime, we are testing that\nnaturality is preserved, which is be a free theorm.\n\nIf that fails, something is very wrong with the instance.\n\n## Type Class\n\nAn instance of `Witchcraft.Foldable` define `Witchcraft.Foldable.right_fold/3`.\n\n Foldable [right_fold/3]\n","module":"Elixir.Witchcraft.Foldable","id":"Witchcraft.Foldable"},{"type":"exception","source":"lib/witchcraft/foldable/empty_error.ex:2","object_type":"ModuleObject","moduledoc":"Represent the error state of trying to fold over an empty structure\n\n## Examples\n\n iex> %Witchcraft.Foldable.EmptyError{}\n %Witchcraft.Foldable.EmptyError{\n message: \"Unable to process empty data\",\n plug_status: 500\n }\n\n","module":"Elixir.Witchcraft.Foldable.EmptyError","id":"Witchcraft.Foldable.EmptyError"},{"type":"protocol","source":"lib/witchcraft/foldable.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Foldable` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Foldable`\n","module":"Elixir.Witchcraft.Foldable.Proto","id":"Witchcraft.Foldable.Proto"},{"type":"impl","source":"lib/witchcraft/foldable/bitstring.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Foldable.Proto.BitString","id":"Witchcraft.Foldable.Proto.BitString"},{"type":"impl","source":"lib/witchcraft/foldable/list.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Foldable.Proto.List","id":"Witchcraft.Foldable.Proto.List"},{"type":"impl","source":"lib/witchcraft/foldable/map.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Foldable.Proto.Map","id":"Witchcraft.Foldable.Proto.Map"},{"type":"impl","source":"lib/witchcraft/foldable/tuple.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Foldable.Proto.Tuple","id":"Witchcraft.Foldable.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/functor.ex:4","object_type":"ModuleObject","moduledoc":"Functors are datatypes that allow the application of functions to their interior values.\nAlways returns data in the same structure (same size, tree layout, and so on).\n\nPlease note that bitstrings are not functors, as they fail the\nfunctor composition constraint. They change the structure of the underlying data,\nand thus composed lifting does not equal lifing a composed function. If you\nneed to map over a bitstring, convert it to and from a charlist.\n\n## Type Class\n\nAn instance of `Witchcraft.Functor` must define `Witchcraft.Functor.map/2`.\n\n Functor [map/2]\n","module":"Elixir.Witchcraft.Functor","id":"Witchcraft.Functor"},{"type":"protocol","source":"lib/witchcraft/functor.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Functor` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Functor`\n","module":"Elixir.Witchcraft.Functor.Proto","id":"Witchcraft.Functor.Proto"},{"type":"impl","source":"lib/witchcraft/functor.ex:156","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Functor.Proto.Function","id":"Witchcraft.Functor.Proto.Function"},{"type":"impl","source":"lib/witchcraft/functor.ex:172","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Functor.Proto.List","id":"Witchcraft.Functor.Proto.List"},{"type":"impl","source":"lib/witchcraft/functor.ex:210","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Functor.Proto.Map","id":"Witchcraft.Functor.Proto.Map"},{"type":"impl","source":"lib/witchcraft/functor.ex:176","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Functor.Proto.Tuple","id":"Witchcraft.Functor.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/monad.ex:4","object_type":"ModuleObject","moduledoc":"Very similar to `Chain`, `Monad` provides a way to link actions, and a way\nto bring plain values into the correct context (`Applicative`).\n\nThis allows us to view actions in a full framework along the lines of\nfunctor and applicative:\n\n data ---------------- function ----------------------------> result\n | | |\n of(Container, data) of/2, or similar of(Container, result)\n ↓ ↓ ↓\n %Container --- (data -> %Container) ---> %Container\n\nAs you can see, the linking function may just be `of` now that we have that.\n\nFor a nice, illustrated introduction,\nsee [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html).\n\nHaving `of` also lets us enhance do-notation with a convenuenct `return` function (see `monad/2`)\n\n## Type Class\n\nAn instance of `Witchcraft.Monad` must also implement `Witchcraft.Applicative`\nand `Wicthcraft.Chainable`.\n\n Functor [map/2]\n ↓\n Apply [ap/2]\n ↓ ↓\n [of/2] Applicative Chain [chain/2]\n ↓ ↓\n Monad\n [_]\n","module":"Elixir.Witchcraft.Monad","id":"Witchcraft.Monad"},{"type":"protocol","source":"lib/witchcraft/monad.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Monad` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Monad`\n","module":"Elixir.Witchcraft.Monad.Proto","id":"Witchcraft.Monad.Proto"},{"type":"impl","source":"lib/witchcraft/monad.ex:135","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monad.Proto.Function","id":"Witchcraft.Monad.Proto.Function"},{"type":"impl","source":"lib/witchcraft/monad.ex:136","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monad.Proto.List","id":"Witchcraft.Monad.Proto.List"},{"type":"impl","source":"lib/witchcraft/monad.ex:138","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monad.Proto.Tuple","id":"Witchcraft.Monad.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/monoid.ex:4","object_type":"ModuleObject","moduledoc":"Monoid extends the semigroup with the concept of an \"empty\" or \"zero\" element.\n\n## Type Class\n\nAn instance of `Witchcraft.Monoid` must also implement `Witchcraft.Semigroup`,\nand define `Witchcraft.Monoid.empty/1`.\n\n Semigroup [append/2]\n ↓\n Monoid [empty/1]\n","module":"Elixir.Witchcraft.Monoid","id":"Witchcraft.Monoid"},{"type":"protocol","source":"lib/witchcraft/monoid.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Monoid` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Monoid`\n","module":"Elixir.Witchcraft.Monoid.Proto","id":"Witchcraft.Monoid.Proto"},{"type":"impl","source":"lib/witchcraft/monoid.ex:97","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.BitString","id":"Witchcraft.Monoid.Proto.BitString"},{"type":"impl","source":"lib/witchcraft/monoid.ex:93","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.Float","id":"Witchcraft.Monoid.Proto.Float"},{"type":"impl","source":"lib/witchcraft/monoid.ex:85","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.Function","id":"Witchcraft.Monoid.Proto.Function"},{"type":"impl","source":"lib/witchcraft/monoid.ex:89","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.Integer","id":"Witchcraft.Monoid.Proto.Integer"},{"type":"impl","source":"lib/witchcraft/monoid.ex:101","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.List","id":"Witchcraft.Monoid.Proto.List"},{"type":"impl","source":"lib/witchcraft/monoid.ex:105","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.Map","id":"Witchcraft.Monoid.Proto.Map"},{"type":"impl","source":"lib/witchcraft/monoid.ex:109","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Monoid.Proto.Tuple","id":"Witchcraft.Monoid.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/ord.ex:4","object_type":"ModuleObject","moduledoc":"`Ord` describes how to order elements of a data type\n\nThis is a total order, so all elements are either `:equal`, `:greater`, or `:lesser`\nthan each other.\n\n## Type Class\n\nAn instance of `Witchcraft.Ord` must also implement `Witchcraft.Setoid`,\nand define `Witchcraft.Ord.compare/2`.\n\n Setoid [equivalent?/2]\n ↓\n Ord [compare/2]\n","module":"Elixir.Witchcraft.Ord","id":"Witchcraft.Ord"},{"type":"protocol","source":"lib/witchcraft/ord.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Ord` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Ord`\n","module":"Elixir.Witchcraft.Ord.Proto","id":"Witchcraft.Ord.Proto"},{"type":"impl","source":"lib/witchcraft/ord/string.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Ord.Proto.BitString","id":"Witchcraft.Ord.Proto.BitString"},{"type":"impl","source":"lib/witchcraft/ord/float.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Ord.Proto.Float","id":"Witchcraft.Ord.Proto.Float"},{"type":"impl","source":"lib/witchcraft/ord/integer.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Ord.Proto.Integer","id":"Witchcraft.Ord.Proto.Integer"},{"type":"impl","source":"lib/witchcraft/ord/list.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Ord.Proto.List","id":"Witchcraft.Ord.Proto.List"},{"type":"impl","source":"lib/witchcraft/ord/map.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Ord.Proto.Map","id":"Witchcraft.Ord.Proto.Map"},{"type":"impl","source":"lib/witchcraft/ord/tuple.ex:3","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Ord.Proto.Tuple","id":"Witchcraft.Ord.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/semigroup.ex:4","object_type":"ModuleObject","moduledoc":"A semigroup is a structure describing data that can be appendenated with others of its type.\nThat is to say that appending another list returns a list, appending one map\nto another returns a map, and appending two integers returns an integer, and so on.\n\nThese can be chained together an arbitrary number of times. For example:\n\n 1 <> 2 <> 3 <> 5 <> 7 == 18\n [1, 2, 3] <> [4, 5, 6] <> [7, 8, 9] == [1, 2, 3, 4, 5, 6, 7, 8, 9]\n \"foo\" <> \" \" <> \"bar\" == \"foo bar\"\n\nThis generalizes the idea of a monoid, as it does not require an `empty` version.\n\n## Type Class\n\nAn instance of `Witchcraft.Semigroup` must define `Witchcraft.Semigroup.append/2`.\n\n Semigroup [append/2]\n","module":"Elixir.Witchcraft.Semigroup","id":"Witchcraft.Semigroup"},{"type":"protocol","source":"lib/witchcraft/semigroup.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Semigroup` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Semigroup`\n","module":"Elixir.Witchcraft.Semigroup.Proto","id":"Witchcraft.Semigroup.Proto"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:147","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.BitString","id":"Witchcraft.Semigroup.Proto.BitString"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:143","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.Float","id":"Witchcraft.Semigroup.Proto.Float"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:135","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.Function","id":"Witchcraft.Semigroup.Proto.Function"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:139","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.Integer","id":"Witchcraft.Semigroup.Proto.Integer"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:151","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.List","id":"Witchcraft.Semigroup.Proto.List"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:155","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.Map","id":"Witchcraft.Semigroup.Proto.Map"},{"type":"impl","source":"lib/witchcraft/semigroup.ex:159","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroup.Proto.Tuple","id":"Witchcraft.Semigroup.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/semigroupoid.ex:4","object_type":"ModuleObject","moduledoc":"A semigroupoid describes some way of composing morphisms on between some\ncollection of objects.\n\n## Type Class\n\nAn instance of `Witchcraft.Semigroupoid` must define `Witchcraft.Semigroupoid.compose/2`.\n\n Semigroupoid [compose/2]\n","module":"Elixir.Witchcraft.Semigroupoid","id":"Witchcraft.Semigroupoid"},{"type":"protocol","source":"lib/witchcraft/semigroupoid.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Semigroupoid` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Semigroupoid`\n","module":"Elixir.Witchcraft.Semigroupoid.Proto","id":"Witchcraft.Semigroupoid.Proto"},{"type":"impl","source":"lib/witchcraft/semigroupoid.ex:137","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Semigroupoid.Proto.Function","id":"Witchcraft.Semigroupoid.Proto.Function"},{"type":null,"source":"lib/witchcraft/setoid.ex:4","object_type":"ModuleObject","moduledoc":"A setoid is a type with an equivalence relation\n\nThis is most useful when equivalence of some data is not the same as equality.\n\nSince some types have differing concepts of equality, this allows overriding\nthe behaviour from `Kernel.==/2`. To get the Setoid `==` operator override,\nsimply `use Witchcraft.Setoid`.\n\n## Type Class\n\nAn instance of `Witchcraft.Setoid` must define `Witchcraft.Setoid.equivalent?/2`\n\n Setoid [equivalent?/2]\n","module":"Elixir.Witchcraft.Setoid","id":"Witchcraft.Setoid"},{"type":"protocol","source":"lib/witchcraft/setoid.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Setoid` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Setoid`\n","module":"Elixir.Witchcraft.Setoid.Proto","id":"Witchcraft.Setoid.Proto"},{"type":"impl","source":"lib/witchcraft/setoid.ex:118","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.BitString","id":"Witchcraft.Setoid.Proto.BitString"},{"type":"impl","source":"lib/witchcraft/setoid.ex:114","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.Float","id":"Witchcraft.Setoid.Proto.Float"},{"type":"impl","source":"lib/witchcraft/setoid.ex:110","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.Integer","id":"Witchcraft.Setoid.Proto.Integer"},{"type":"impl","source":"lib/witchcraft/setoid.ex:126","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.List","id":"Witchcraft.Setoid.Proto.List"},{"type":"impl","source":"lib/witchcraft/setoid.ex:130","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.Map","id":"Witchcraft.Setoid.Proto.Map"},{"type":"impl","source":"lib/witchcraft/setoid.ex:134","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.MapSet","id":"Witchcraft.Setoid.Proto.MapSet"},{"type":"impl","source":"lib/witchcraft/setoid.ex:122","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Setoid.Proto.Tuple","id":"Witchcraft.Setoid.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/traversable.ex:4","object_type":"ModuleObject","moduledoc":"Walk across a data structure from left to right,\nrunning some action on each element in turn.\n\nSimilar to applicatives, it can be used to do things like collecting some effect\nwhile performing other actions.\n\n## Type Class\n\nAn instance of `Witchcraft.Traversable` must also implement `Witchcraft.Foldable`\nand `Witchcraft.Functor`, and define `Witchcraft.Foldable.right_fold/3`.\n\n [right_fold/3] Foldable Functor [map/2]\n ↓ ↓\n Traversable\n [right_fold/3]\n","module":"Elixir.Witchcraft.Traversable","id":"Witchcraft.Traversable"},{"type":"protocol","source":"lib/witchcraft/traversable.ex:3","object_type":"ModuleObject","moduledoc":"Protocol for the `Elixir.Witchcraft.Traversable` type class\n\nFor this type class's API, please refer to `Elixir.Witchcraft.Traversable`\n","module":"Elixir.Witchcraft.Traversable.Proto","id":"Witchcraft.Traversable.Proto"},{"type":"impl","source":"lib/witchcraft/traversable.ex:295","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Traversable.Proto.List","id":"Witchcraft.Traversable.Proto.List"},{"type":"impl","source":"lib/witchcraft/traversable.ex:282","object_type":"ModuleObject","moduledoc":null,"module":"Elixir.Witchcraft.Traversable.Proto.Tuple","id":"Witchcraft.Traversable.Proto.Tuple"},{"type":null,"source":"lib/witchcraft/unit.ex:2","object_type":"ModuleObject","moduledoc":"The `unit` or `Void` type. A stand in for \"no added information here\".\n\nWhy not encode unit as `{}`? Many protocols (Witchcraft and others)\nconvert tuples to lists, and thus will treat unit as `{}` and thus `[]`,\nwhich we don't want. The struct removes this ambiguity.\n","module":"Elixir.Witchcraft.Unit","id":"Witchcraft.Unit"},{"type":"def","source":"lib/witchcraft/applicative.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Applicative","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:74","signature":[["sample",[],null]],"object_type":"FunctionObject","name":"of","module_id":"Witchcraft.Applicative","id":"of/1","doc":"Partially apply `of/2`, generally as a way to bring many values into the same context\n\n## Examples\n\n iex> to_tuple = of({\"very example\", \"much wow\"})\n ...> [to_tuple.(42), to_tuple.(\"hello\"), to_tuple.([1, 2, 3])]\n [{\"\", 42}, {\"\", \"hello\"}, {\"\", [1, 2, 3]}]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:41","signature":[["sample",[],null],["to_wrap",[],null]],"object_type":"FunctionObject","name":"of","module_id":"Witchcraft.Applicative","id":"of/2","doc":"Bring a value into the same data type as some sample\n\n## Examples\n\n iex> of([], 42)\n [42]\n\n iex> of([1, 2, 3], 42)\n [42]\n\n iex> of({\"a\", \"b\", 155}, 42)\n {\"\", \"\", 42}\n\n iex> of(fn -> nil end, 42).(55)\n 42\n\n iex> of(fn(a, b, c) -> a + b - c end, 42).(55)\n 42\n\n iex> import Witchcraft.Apply\n ...>\n ...> []\n ...> |> of(&+/2)\n ...> |> curried_ap([1, 2, 3])\n ...> |> ap(of([], 42))\n [43, 44, 45]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:102","signature":[["sample",[],null],["to_wrap",[],null]],"object_type":"FunctionObject","name":"pure","module_id":"Witchcraft.Applicative","id":"pure/2","doc":"Alias for `of/2`, for cases that this helps legibility or style\n\n## Example\n\n iex> pure({\"ohai\", \"thar\"}, 42)\n {\"\", 42}\n\n iex> [] |> pure(42)\n [42]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:117","signature":[["sample",[],null],["to_wrap",[],null]],"object_type":"FunctionObject","name":"unit","module_id":"Witchcraft.Applicative","id":"unit/2","doc":"Alias for `of/2`, for cases that this helps legibility or style\n\n## Example\n\n iex> unit({\":)\", \":(\"}, 42)\n {\"\", 42}\n\n iex> [] |> unit(42)\n [42]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:87","signature":[["sample",[],null],["to_wrap",[],null]],"object_type":"FunctionObject","name":"wrap","module_id":"Witchcraft.Applicative","id":"wrap/2","doc":"Alias for `of/2`, for cases that this helps legibility or style\n\n## Example\n\n iex> wrap({\":|\", \"^.~\"}, 42)\n {\"\", 42}\n\n iex> [] |> wrap(42)\n [42]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Applicative.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Applicative.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Applicative.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:41","signature":[["sample",[],null],["to_wrap",[],null]],"object_type":"FunctionObject","name":"of","module_id":"Witchcraft.Applicative.Proto","id":"of/2","doc":"Bring a value into the same data type as some sample\n\n## Examples\n\n iex> of([], 42)\n [42]\n\n iex> of([1, 2, 3], 42)\n [42]\n\n iex> of({\"a\", \"b\", 155}, 42)\n {\"\", \"\", 42}\n\n iex> of(fn -> nil end, 42).(55)\n 42\n\n iex> of(fn(a, b, c) -> a + b - c end, 42).(55)\n 42\n\n iex> import Witchcraft.Apply\n ...>\n ...> []\n ...> |> of(&+/2)\n ...> |> curried_ap([1, 2, 3])\n ...> |> ap(of([], 42))\n [43, 44, 45]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:167","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Applicative.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:167","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Applicative.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:167","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Applicative.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:168","signature":[["_",[],"Elixir"],["unwrapped",[],null]],"object_type":"FunctionObject","name":"of","module_id":"Witchcraft.Applicative.Proto.Function","id":"of/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:171","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Applicative.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:171","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Applicative.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:171","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Applicative.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:172","signature":[["_",[],"Elixir"],["unwrapped",[],null]],"object_type":"FunctionObject","name":"of","module_id":"Witchcraft.Applicative.Proto.List","id":"of/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/applicative.ex:175","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Applicative.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:175","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Applicative.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/applicative.ex:175","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Applicative.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/applicative.ex:181","signature":[["sample",[],null],["unwrapped",[],null]],"object_type":"FunctionObject","name":"of","module_id":"Witchcraft.Applicative.Proto.Tuple","id":"of/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:150","signature":[["wrapped_funs",[],null],["wrapped",[],null]],"object_type":"FunctionObject","name":"<<~","module_id":"Witchcraft.Apply","id":"<<~/2","doc":"Operator alias for `ap/2`\n\nMoves against the pipe direction, but in the order of normal function application\n\n## Examples\n\n iex> [fn x -> x + 1 end, fn y -> y * 10 end] <<~ [1, 2, 3]\n [2, 3, 4, 10, 20, 30]\n\n iex> import Witchcraft.Functor\n ...>\n ...> [100, 200]\n ...> ~> fn(x, y, z) -> x * y / z\n ...> end <<~ [5, 2]\n ...> <<~ [100, 50]\n ...> ~> fn x -> x + 1 end\n [6.0, 11.0, 3.0, 5.0, 11.0, 21.0, 5.0, 9.0]\n\n iex> import Witchcraft.Functor, only: [<~: 2]\n ...> fn(a, b, c, d) -> a * b - c + d end <~ [1, 2] <<~ [3, 4] <<~ [5, 6] <<~ [7, 8]\n [5, 6, 4, 5, 6, 7, 5, 6, 8, 9, 7, 8, 10, 11, 9, 10]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Apply","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:93","signature":[["wrapped_funs",[],null],["wrapped_args",[],null]],"object_type":"FunctionObject","name":"ap","module_id":"Witchcraft.Apply","id":"ap/2","doc":"Apply argumnets to a function, when both are wrapped in the same data structure\n\n## Examples\n\n iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3])\n [2, 3, 4, 10, 20, 30]\n\n iex> [100, 200]\n ...> |> Witchcraft.Functor.lift(fn(x, y, z) -> x * y / z end)\n ...> |> ap([5, 2])\n ...> |> ap([100, 50])\n [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]\n # ↓ ↓\n # 100 * 5 / 100 200 * 5 / 50\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:195","signature":[["wrapped_funs",[],null],["wrapped_args",[],null]],"object_type":"FunctionObject","name":"curried_ap","module_id":"Witchcraft.Apply","id":"curried_ap/2","doc":"Same as `ap/2`, but with all functions curried\n\n## Examples\n\n iex> [&+/2, &*/2]\n ...> |> curried_ap([1, 2, 3])\n ...> |> ap([4, 5, 6])\n [5, 6, 7, 6, 7, 8, 7, 8, 9, 4, 5, 6, 8, 10, 12, 12, 15, 18]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:213","signature":[["wrapped_args",[],null],["wrapped_funs",[],null]],"object_type":"FunctionObject","name":"curried_pipe_ap","module_id":"Witchcraft.Apply","id":"curried_pipe_ap/2","doc":"Same as `pipe_ap/2`, but with all functions curried\n\n## Examples\n\n iex> [1, 2, 3]\n ...> |> curried_pipe_ap([fn x -> x + 1 end, fn y -> y * 10 end])\n [2, 3, 4, 10, 20, 30]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:259","signature":[["wrapped_a",[],null],["wrapped_b",[],null]],"object_type":"FunctionObject","name":"following","module_id":"Witchcraft.Apply","id":"following/2","doc":"Sequence actions, replacing the last argument with the first argument's values\n\nThis is essentially a sequence of actions forgetting the second argument\n\n## Examples\n\n iex> [1, 2, 3]\n ...> |> following([3, 4, 5])\n ...> |> following([5, 6, 7])\n [\n 1, 1, 1, 1, 1, 1, 1, 1, 1,\n 2, 2, 2, 2, 2, 2, 2, 2, 2,\n 3, 3, 3, 3, 3, 3, 3, 3, 3\n ]\n\n iex> {1, 2, 3} |> following({4, 5, 6}) |> following({7, 8, 9})\n {12, 15, 3}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:282","signature":[["a",[],null],["b",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"lift","module_id":"Witchcraft.Apply","id":"lift/3","doc":"Extends `Functor.lift/2` to apply arguments to a binary function\n\n## Examples\n\n iex> lift([1, 2], [3, 4], &+/2)\n [4, 5, 5, 6]\n\n iex> [1, 2]\n ...> |> lift([3, 4], &*/2)\n [3, 4, 6, 8]\n\n","arity":3},{"type":"def","source":"lib/witchcraft/apply.ex:298","signature":[["a",[],null],["b",[],null],["c",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"lift","module_id":"Witchcraft.Apply","id":"lift/4","doc":"Extends `lift` to apply arguments to a ternary function\n\n## Examples\n\n iex> lift([1, 2], [3, 4], [5, 6], fn(a, b, c) -> a * b - c end)\n [-2, -3, -1, -2, 1, 0, 3, 2]\n\n","arity":4},{"type":"def","source":"lib/witchcraft/apply.ex:315","signature":[["a",[],null],["b",[],null],["c",[],null],["d",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"lift","module_id":"Witchcraft.Apply","id":"lift/5","doc":"Extends `lift` to apply arguments to a quaternary function\n\n## Examples\n\n iex> lift([1, 2], [3, 4], [5, 6], [7, 8], fn(a, b, c, d) -> a * b - c + d end)\n [5, 6, 4, 5, 6, 7, 5, 6, 8, 9, 7, 8, 10, 11, 9, 10]\n\n","arity":5},{"type":"def","source":"lib/witchcraft/apply.ex:135","signature":[["wrapped",[],null],["wrapped_funs",[],null]],"object_type":"FunctionObject","name":"pipe_ap","module_id":"Witchcraft.Apply","id":"pipe_ap/2","doc":"Pipe-ordered application. NOT just a flipped version of `ap/2`.\n\nThis isn't just a flipped `ap` in order to get correct effect sequencing.\n\n## Examples\n\n iex> [1, 2, 3]\n ...> |> pipe_ap([fn x -> x + 1 end, fn y -> y * 10 end])\n [2, 10, 3, 20, 4, 30]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:230","signature":[["wrapped_a",[],null],["wrapped_b",[],null]],"object_type":"FunctionObject","name":"then","module_id":"Witchcraft.Apply","id":"then/2","doc":"Sequence actions, replacing the first/previous values with the last argument\n\nThis is essentially a sequence of actions forgetting the first argument\n\n## Examples\n\n iex> [1, 2, 3]\n ...> |> then([4, 5, 6])\n ...> |> then([7, 8, 9])\n [\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9\n ]\n\n iex> {1, 2, 3} |> then({4, 5, 6}) |> then({7, 8, 9})\n {12, 15, 9}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:176","signature":[["wrapped",[],null],["wrapped_funs",[],null]],"object_type":"FunctionObject","name":"~>>","module_id":"Witchcraft.Apply","id":"~>>/2","doc":"Operator alias for `pipe_ap/2`, moving in the pipe direction\n\n## Examples\n\n iex> [1, 2, 3] ~>> [fn x -> x + 1 end, fn y -> y * 10 end]\n [2, 3, 4, 10, 20, 30]\n\n iex> import Witchcraft.Functor\n ...>\n ...> [100, 50]\n ...> ~>> ([5, 2] # Note the bracket\n ...> ~>> ([100, 200] # on both `Apply` lines\n ...> ~> fn(x, y, z) -> x * y / z end))\n [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Apply.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/apply.ex:93","signature":[["wrapped_funs",[],null],["wrapped_args",[],null]],"object_type":"FunctionObject","name":"ap","module_id":"Witchcraft.Apply.Proto","id":"ap/2","doc":"Apply argumnets to a function, when both are wrapped in the same data structure\n\n## Examples\n\n iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3])\n [2, 3, 4, 10, 20, 30]\n\n iex> [100, 200]\n ...> |> Witchcraft.Functor.lift(fn(x, y, z) -> x * y / z end)\n ...> |> ap([5, 2])\n ...> |> ap([100, 50])\n [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0]\n # ↓ ↓\n # 100 * 5 / 100 200 * 5 / 50\n\n","arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Apply.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/apply.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Apply.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/apply.ex:328","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Apply.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:328","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Apply.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:328","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Apply.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/apply.ex:330","signature":[["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"ap","module_id":"Witchcraft.Apply.Proto.Function","id":"ap/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:333","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Apply.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:333","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Apply.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:333","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Apply.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/apply.ex:334","signature":[["fun_list",[],null],["val_list",[],null]],"object_type":"FunctionObject","name":"ap","module_id":"Witchcraft.Apply.Proto.List","id":"ap/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/apply.ex:342","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Apply.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:342","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Apply.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/apply.ex:342","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Apply.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/apply.ex:350","signature":[["tuple_a",[],null],["tuple_b",[],null]],"object_type":"FunctionObject","name":"ap","module_id":"Witchcraft.Apply.Proto.Tuple","id":"ap/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:316","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"&&&","module_id":"Witchcraft.Arrow","id":"&&&/2","doc":"Operator alias for `fanout/2`\n\n## Examples\n\n iex> fanned = fn x -> x - 10 end &&& fn y -> inspect(y) <> \"!\" end\n ...> fanned.(42)\n {32, \"42!\"}\n\n iex> fanned =\n ...> fn x -> x - 10 end\n ...> &&& fn y -> inspect(y) <> \"!\" end\n ...> &&& fn z -> inspect(z) <> \"?\" end\n ...> &&& fn d -> inspect(d) <> inspect(d) end\n ...> &&& fn e -> e / 2 end\n ...>\n ...> fanned.(42)\n {{{{32, \"42!\"}, \"42?\"}, \"4242\"}, 21.0}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:240","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"^^^","module_id":"Witchcraft.Arrow","id":"^^^/2","doc":"See `Witchcraft.Arrow.product/2`.","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Arrow","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/arrow.ex:54","signature":[["sample",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"arrowize","module_id":"Witchcraft.Arrow","id":"arrowize/2","doc":"Lift a function into an arrow, much like how `of/2` does with data.\n\nEssentially a label for composing functions end-to-end, where instances\nmay have their own special idea of what composition means. The simplest example\nis a regular function. Others are possible, such as Kleisli arrows.\n\n## Examples\n\n iex> use Witchcraft.Arrow\n ...> times_ten = arrowize(fn -> nil end, &(&1 * 10))\n ...> 5 |> pipe(times_ten)\n 50\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:233","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"beside","module_id":"Witchcraft.Arrow","id":"beside/2","doc":"Alias for `product/2`, meant to invoke a spacial metaphor\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:295","signature":[["arrow_f",[],null],["arrow_g",[],null]],"object_type":"FunctionObject","name":"fanout","module_id":"Witchcraft.Arrow","id":"fanout/2","doc":"Duplicate incoming data into both halves of a 2-tuple, and run one function\non the left copy, and a different function on the right copy.\n\n ┌------> f.(a) = x ------┐\n | v\n a ---> split = {a, a} {x, y}\n | ^\n └------> g.(a) = y ------┘\n\n## Examples\n\n iex> Witchcraft.Semigroupoid.pipe(42, fanout(&(&1 - 10), &(inspect(&1) <> \"!\")))\n {32, \"42!\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:245","signature":[["arrow",[],null]],"object_type":"FunctionObject","name":"first","module_id":"Witchcraft.Arrow","id":"first/1","doc":"Target the first element of a tuple\n\n## Examples\n\n iex> first(fn x -> x * 50 end).({1, 1})\n {50, 1}\n\n","arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:283","signature":[["sample",[],null]],"object_type":"FunctionObject","name":"id_arrow","module_id":"Witchcraft.Arrow","id":"id_arrow/1","doc":"The identity function lifted into an arrow of the correct type\n\n## Examples\n\n iex> id_arrow(fn -> nil end).(99)\n 99\n\n","arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:415","signature":[["arrow",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"postcompose","module_id":"Witchcraft.Arrow","id":"postcompose/2","doc":"Compose an arrow (left) with a function (right) to produce a new arrow.\n\n## Examples\n\n iex> f = postcompose(\n ...> arrowize(fn _ -> nil end, fn x -> x + 1 end),\n ...> fn y -> y * 10 end\n ...> )\n ...> f.(42)\n 430\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:399","signature":[["fun",[],null],["arrow",[],null]],"object_type":"FunctionObject","name":"precompose","module_id":"Witchcraft.Arrow","id":"precompose/2","doc":"Compose a function (left) with an arrow (right) to produce a new arrow.\n\n## Examples\n\n iex> f = precompose(\n ...> fn x -> x + 1 end,\n ...> arrowize(fn _ -> nil end, fn y -> y * 10 end)\n ...> )\n ...> f.(42)\n 430\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:214","signature":[["arrow_f",[],null],["arrow_g",[],null]],"object_type":"FunctionObject","name":"product","module_id":"Witchcraft.Arrow","id":"product/2","doc":"Take two arguments (as a 2-tuple), and run one function on the left side (first element),\nand run a different function on the right side (second element).\n\n ┌------> f.(a) = x -------┐\n | v\n {a, b} {x, y}\n | ^\n └------> g.(b) = y -------┘\n\n## Examples\n\n iex> product(&(&1 - 10), &(&1 <> \"!\")).({42, \"Hi\"})\n {32, \"Hi!\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:381","signature":[["arg",[],"Elixir"]],"object_type":"FunctionObject","name":"reassociate","module_id":"Witchcraft.Arrow","id":"reassociate/1","doc":"Switch te associativity of a nested tuple. Helpful since many arrows act\non a subset of a tuple, and you may want to move portions in and oit of that stream.\n\n## Examples\n\n iex> reassociate({1, {2, 3}})\n {{1, 2}, 3}\n\n iex> reassociate({{1, 2}, 3})\n {1, {2, 3}}\n\n","arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:264","signature":[["arrow",[],null]],"object_type":"FunctionObject","name":"second","module_id":"Witchcraft.Arrow","id":"second/1","doc":"Target the second element of a tuple\n\n## Examples\n\n iex> second(fn x -> x * 50 end).({1, 1})\n {1, 50}\n\n","arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:339","signature":[["x",[],null]],"object_type":"FunctionObject","name":"split","module_id":"Witchcraft.Arrow","id":"split/1","doc":"Copy a single value into both positions of a 2-tuple.\n\nThis is useful is you want to run functions on the input separately.\n\n## Examples\n\n iex> split(42)\n {42, 42}\n\n iex> import Witchcraft.Semigroupoid, only: [<~>: 2]\n ...> 5\n ...> |> split()\n ...> |> (second(fn x -> x - 2 end)\n ...> <~> first(fn y -> y * 10 end)\n ...> <~> second(&inspect/1)).()\n {50, \"3\"}\n\n iex> use Witchcraft.Arrow\n ...> 5\n ...> |> split()\n ...> |> pipe(second(fn x -> x - 2 end))\n ...> |> pipe(first(fn y -> y * 10 end))\n ...> |> pipe(second(&inspect/1))\n {50, \"3\"}\n\n","arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:243","signature":[["arg",[],"Elixir"]],"object_type":"FunctionObject","name":"swap","module_id":"Witchcraft.Arrow","id":"swap/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:369","signature":[["arg",[],"Elixir"],["combine",[],null]],"object_type":"FunctionObject","name":"unsplit","module_id":"Witchcraft.Arrow","id":"unsplit/2","doc":"Merge two tuple values with a combining function.\n\n## Examples\n\n iex> unsplit({1, 2}, &+/2)\n 3\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Arrow.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:54","signature":[["sample",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"arrowize","module_id":"Witchcraft.Arrow.Proto","id":"arrowize/2","doc":"Lift a function into an arrow, much like how `of/2` does with data.\n\nEssentially a label for composing functions end-to-end, where instances\nmay have their own special idea of what composition means. The simplest example\nis a regular function. Others are possible, such as Kleisli arrows.\n\n## Examples\n\n iex> use Witchcraft.Arrow\n ...> times_ten = arrowize(fn -> nil end, &(&1 * 10))\n ...> 5 |> pipe(times_ten)\n 50\n\n","arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Arrow.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Arrow.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:432","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Arrow.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/arrow.ex:432","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Arrow.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/arrow.ex:432","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Arrow.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/arrow.ex:435","signature":[["_",[],"Elixir"],["fun",[],null]],"object_type":"FunctionObject","name":"arrowize","module_id":"Witchcraft.Arrow.Proto.Function","id":"arrowize/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/arrow.ex:436","signature":[["arrow",[],null]],"object_type":"FunctionObject","name":"first","module_id":"Witchcraft.Arrow.Proto.Function","id":"first/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/bifunctor.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Bifunctor","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/bifunctor.ex:90","signature":[["data",[],null],["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"bilift","module_id":"Witchcraft.Bifunctor","id":"bilift/3","doc":"The same as `bimap/3`, but with the functions curried\n\n## Examples\n\n iex> {:ok, 2, \"hi\"}\n ...> |> bilift(&*/2, &<>/2)\n ...> |> bimap(fn f -> f.(9) end, fn g -> g.(\"?!\") end)\n {:ok, 18, \"hi?!\"}\n\n","arity":3},{"type":"def","source":"lib/witchcraft/bifunctor.ex:36","signature":[["data",[],null],["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"bimap","module_id":"Witchcraft.Bifunctor","id":"bimap/3","doc":"`map` separate fuctions over two fields in a product type.\n\nThe order of fields doesn't always matter in the map.\nThe first/second function application is determined by the instance.\nIt also does not have to map all fields in a product type.\n\n## Diagram\n\n ┌------------------------------------┐\n ↓ |\n %Combo{a: 50, b: :ok, c: \"hello\"} |> bimap(&(&1 * 100), &String.upcase/1)\n ↑ |\n └---------------------------------┘\n #=> %Combo{a: 500, b: :ok, c: \"HELLO\"}\n\n## Examples\n\n iex> {1, \"a\"} |> bimap(&(&1 * 100), &(&1 <> \"!\"))\n {100, \"a!\"}\n\n iex> {:msg, 42, \"number is below 50\"}\n ...> |> bimap(&(%{subject: &1}), &String.upcase/1)\n {:msg, %{subject: 42}, \"NUMBER IS BELOW 50\"}\n\n","arity":3},{"type":"def","source":"lib/witchcraft/bifunctor.ex:116","signature":[["data",[],null],["f",[],null]],"object_type":"FunctionObject","name":"lift_first","module_id":"Witchcraft.Bifunctor","id":"lift_first/2","doc":"The same as `map_first`, but with a curried function\n\n## Examples\n\n iex> {:ok, 2, \"hi\"}\n ...> |> lift_first(&*/2)\n ...> |> map_first(fn f -> f.(9) end)\n {:ok, 18, \"hi\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/bifunctor.ex:142","signature":[["data",[],null],["g",[],null]],"object_type":"FunctionObject","name":"lift_second","module_id":"Witchcraft.Bifunctor","id":"lift_second/2","doc":"The same as `map_second`, but with a curried function\n\n## Examples\n\n iex> {:ok, 2, \"hi\"}\n ...> |> lift_second(&<>/2)\n ...> |> map_second(fn f -> f.(\"?!\") end)\n {:ok, 2, \"hi?!\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/bifunctor.ex:104","signature":[["data",[],null],["f",[],null]],"object_type":"FunctionObject","name":"map_first","module_id":"Witchcraft.Bifunctor","id":"map_first/2","doc":"`map` a function over the first value only\n\n## Examples\n\n iex> {:ok, 2, \"hi\"} |> map_first(&(&1 * 100))\n {:ok, 200, \"hi\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/bifunctor.ex:130","signature":[["data",[],null],["g",[],null]],"object_type":"FunctionObject","name":"map_second","module_id":"Witchcraft.Bifunctor","id":"map_second/2","doc":"`map` a function over the second value only\n\n## Examples\n\n iex> {:ok, 2, \"hi\"} |> map_second(&(&1 <> \"!?\"))\n {:ok, 2, \"hi!?\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/bifunctor.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Bifunctor.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/bifunctor.ex:36","signature":[["data",[],null],["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"bimap","module_id":"Witchcraft.Bifunctor.Proto","id":"bimap/3","doc":"`map` separate fuctions over two fields in a product type.\n\nThe order of fields doesn't always matter in the map.\nThe first/second function application is determined by the instance.\nIt also does not have to map all fields in a product type.\n\n## Diagram\n\n ┌------------------------------------┐\n ↓ |\n %Combo{a: 50, b: :ok, c: \"hello\"} |> bimap(&(&1 * 100), &String.upcase/1)\n ↑ |\n └---------------------------------┘\n #=> %Combo{a: 500, b: :ok, c: \"HELLO\"}\n\n## Examples\n\n iex> {1, \"a\"} |> bimap(&(&1 * 100), &(&1 <> \"!\"))\n {100, \"a!\"}\n\n iex> {:msg, 42, \"number is below 50\"}\n ...> |> bimap(&(%{subject: &1}), &String.upcase/1)\n {:msg, %{subject: 42}, \"NUMBER IS BELOW 50\"}\n\n","arity":3},{"type":"def","source":"lib/witchcraft/bifunctor.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Bifunctor.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/bifunctor.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Bifunctor.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/bifunctor.ex:157","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Bifunctor.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/bifunctor.ex:157","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Bifunctor.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/bifunctor.ex:157","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Bifunctor.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/bifunctor.ex:166","signature":[["tuple",[],null],["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"bimap","module_id":"Witchcraft.Bifunctor.Proto.Tuple","id":"bimap/3","doc":null,"arity":3},{"type":"def","source":"lib/witchcraft/category.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Category","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/category.ex:51","signature":[["category",[],null]],"object_type":"FunctionObject","name":"id","module_id":"Witchcraft.Category","id":"id/1","doc":"See `Witchcraft.Category.identity/1`.","arity":1},{"type":"def","source":"lib/witchcraft/category.ex:37","signature":[["category",[],null]],"object_type":"FunctionObject","name":"identity","module_id":"Witchcraft.Category","id":"identity/1","doc":"Take some value and return it again\n\n## Examples\n\n iex> classic_id = identity(fn -> nil end)\n ...> classic_id.(42)\n 42\n\n","arity":1},{"type":"def","source":"lib/witchcraft/category.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Category.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/category.ex:37","signature":[["category",[],null]],"object_type":"FunctionObject","name":"identity","module_id":"Witchcraft.Category.Proto","id":"identity/1","doc":"Take some value and return it again\n\n## Examples\n\n iex> classic_id = identity(fn -> nil end)\n ...> classic_id.(42)\n 42\n\n","arity":1},{"type":"def","source":"lib/witchcraft/category.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Category.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/category.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Category.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/category.ex:70","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Category.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/category.ex:70","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Category.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/category.ex:70","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Category.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/category.ex:71","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"identity","module_id":"Witchcraft.Category.Proto.Function","id":"identity/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:138","signature":[["chain_fun",[],null],["chainable",[],null]],"object_type":"FunctionObject","name":"<<<","module_id":"Witchcraft.Chain","id":"<< to_monad = fn x -> (fn _ -> x end) end\n ...> bound = to_monad.(&(&1 + 10)) <<< to_monad.(&(&1 * 10))\n ...> bound.(10)\n 20\n\nIn Haskell, this is the famous `=<<` operator, but Elixir doesn't allow that\ninfix operator.\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:119","signature":[["chainable",[],null],["chain_fun",[],null]],"object_type":"FunctionObject","name":">>>","module_id":"Witchcraft.Chain","id":">>>/2","doc":"Operator alias for `chain/2`\n\nExtends the `~>` / `~>>` heirarchy with one more level of power / abstraction\n\n## Examples\n\n iex> to_monad = fn x -> (fn _ -> x end) end\n ...> bound = to_monad.(&(&1 * 10)) >>> to_monad.(&(&1 + 10))\n ...> bound.(10)\n 20\n\nIn Haskell, this is the famous `>>=` operator, but Elixir doesn't allow that\ninfix operator.\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Chain","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:103","signature":[["chainable",[],null],["binder",[],null]],"object_type":"FunctionObject","name":"bind","module_id":"Witchcraft.Chain","id":"bind/2","doc":"An alias for `chain/2`.\n\nProvided as a convenience for those coming from other languages.\n","arity":2},{"type":"defmacro","source":"lib/witchcraft/chain.ex:257","signature":[["list",[],"Elixir"]],"object_type":"FunctionObject","name":"chain","module_id":"Witchcraft.Chain","id":"chain/1","doc":"`do` notation sugar\n\nSequences chainable actions. Note that each line must be of the same type.\n\nFor a version with `return`, please see `Witchcraft.Monad.do/2`\n\n## Examples\n\n iex> chain do\n ...> [1]\n ...> end\n [1]\n\n iex> chain do\n ...> [1, 2, 3]\n ...> [4, 5, 6]\n ...> [7, 8, 9]\n ...> end\n [\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9\n ]\n\n iex> chain do\n ...> a <- [1, 2, 3]\n ...> b <- [4, 5, 6]\n ...> [a * b]\n ...> end\n [\n 4, 8, 12,\n 5, 10, 15,\n 6, 12, 18\n ]\n\nNormal functions are fine within the `do` as well, as long as each line\nends up being the same chainable type\n\n iex> import Witchcraft.{Functor, Applicative}\n ...> chain do\n ...> map([1, 2, 3], fn x -> x + 1 end)\n ...> of([], 42)\n ...> [7, 8, 9] ~> fn x -> x * 10 end\n ...> end\n [\n 70, 80, 90,\n 70, 80, 90,\n 70, 80, 90\n ]\n\nOr with a custom type\n\n alias Algae.Maybe.{Nothing, Just}\n\n chain do\n %Just{just: 4}\n %Just{just: 5}\n %Just{just: 6}\n end\n #=> %Just{just: 6}\n\n chain do\n %Just{just: 4}\n %Nothing{}\n %Just{just: 6}\n end\n #=> %Nothing{}\n\n## `let` bindings\n\n`let`s allow you to hold static values inside a do-block, much like normal assignment\n\n iex> chain do\n ...> let a = 4\n ...> [a]\n ...> end\n [4]\n\nThis is somewhat limited, though, as values drawn from a chianable structure\nwith `<-` are not in scope when desugared. For example, this is not possible\ndue to the recursive binding on `x`:\n\n chain do\n x <- [1, 2, 3]\n y <- [4, 5, 6]\n let will_fail = x + 1\n [y * will_fail]\n end\n\n## Desugaring\n\n### Sequencing\n\nThe most basic form\n\n chain do\n [1, 2, 3]\n [4, 5, 6]\n [7, 8, 9]\n end\n\nis equivalent to\n\n [1, 2, 3]\n |> then([4, 5, 6])\n |> then([7, 8, 9])\n\n### `<-` (\"drawn from\")\n\nDrawing values from within a chainable structure is similar feels similar\nto assignmet, but it is pulling each value separately in a chain link function.\n\nFor instance\n\n chain do\n a <- [1, 2, 3]\n b <- [4, 5, 6]\n [a * b]\n end\n\ndesugars to this\n\n [1, 2, 3] >>> fn a ->\n [4, 5, 6] >>> fn b ->\n [a + b]\n end\n end\n\nbut is often much cleaner to read in do-notation, as it cleans up all of the\nnested functions (especially when the chain is very long).\n\n","arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:52","signature":[["chainable",[],null],["link_fun",[],null]],"object_type":"FunctionObject","name":"chain","module_id":"Witchcraft.Chain","id":"chain/2","doc":"Sequentially compose actions, piping values through successive function chains.\n\nThe applied linking function must be unary and return data in the same\ntype of container as the input. The chain function essentially \"unwraps\"\na contained value, applies a linking function that returns\nthe initial (wrapped) type, and collects them into a flat(ter) structure.\n\n`chain/2` is sometimes called \"flat map\", since it can also\nbe expressed as `data |> map(link_fun) |> flatten()`.\n\nAs a diagram:\n\n %Container --- (data -> %Container) ---> %Container\n\n## Examples\n\n iex> chain([1, 2, 3], fn x -> [x, x] end)\n [1, 1, 2, 2, 3, 3]\n\n iex> [1, 2, 3]\n ...> |> chain(fn x -> [x, x] end)\n ...> |> chain(fn y -> [y, 2 * y, 3 * y] end)\n [1, 2, 3, 1, 2, 3, 2, 4, 6, 2, 4, 6, 3, 6, 9, 3, 6, 9]\n\n iex> chain([1, 2, 3], fn x ->\n ...> chain([x + 1], fn y ->\n ...> chain([y + 2, y + 10], fn z ->\n ...> [x, y, z]\n ...> end)\n ...> end)\n ...> end)\n [1, 2, 4, 1, 2, 12, 2, 3, 5, 2, 3, 13, 3, 4, 6, 3, 4, 14]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:215","signature":[["action_g",[],null],["action_f",[],null]],"object_type":"FunctionObject","name":"compose_link","module_id":"Witchcraft.Chain","id":"compose_link/2","doc":"Compose link functions to create a new link function.\n\nNote that this runs the same direction as `<|>` (\"the math way\").\n\nThis is `pipe_compose_link/2` with arguments flipped.\n\n## Examples\n\n iex> links =\n ...> fn x -> [x, x] end\n ...> |> compose_link(fn y -> [y * 10] end)\n ...> |> compose_link(fn z -> [z + 42] end)\n ...>\n ...> [1, 2, 3] >>> links\n [430, 430, 440, 440, 450, 450]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:213","signature":[["nested",[],null]],"object_type":"FunctionObject","name":"flatten","module_id":"Witchcraft.Chain","id":"flatten/1","doc":"See `Witchcraft.Chain.join/1`.","arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:157","signature":[["nested",[],null]],"object_type":"FunctionObject","name":"join","module_id":"Witchcraft.Chain","id":"join/1","doc":"Join together one nested level of a data structure that contains itself\n\n## Examples\n\n iex> join([[1, 2, 3]])\n [1, 2, 3]\n\n iex> join([[1, 2, 3], [4, 5, 6]])\n [1, 2, 3, 4, 5, 6]\n\n iex> join([[[1, 2, 3], [4, 5, 6]]])\n [[1, 2, 3], [4, 5, 6]]\n\n alias Algae.Maybe.{Nothing, Just}\n %Just{\n just: %Just{\n just: 42\n }\n } |> join()\n #=> %Just{just: 42}\n\n join %Just{just: %Nothing{}}\n #=> %Nothing{}\n\n join %Just{just: %Just{just: %Nothing{}}}\n #=> %Just{just: %Nothing{}}\n\n %Nothing{} |> join() |> join() |> join() # ...and so on, forever\n #=> %Nothing{}\n\nJoining tuples is a bit counterintuitive, as it requires a very specific format:\n\n iex> join { # Outer 2-tuple\n ...> {1, 2}, # Inner 2-tuple\n ...> {\n ...> {3, 4}, # Doubly inner 2-tuple\n ...> {5, 6, 7}\n ...> }\n ...> }\n {{4, 6}, {5, 6, 7}}\n\n iex> join {\n ...> {\"a\", \"b\"},\n ...> {\n ...> {\"!\", \"?\"},\n ...> {:ok, 123}\n ...> }\n ...> }\n {{\"a!\", \"b?\"}, {:ok, 123}}\n\n","arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:236","signature":[["action_f",[],null],["action_g",[],null]],"object_type":"FunctionObject","name":"pipe_compose_link","module_id":"Witchcraft.Chain","id":"pipe_compose_link/2","doc":"Compose link functions to create a new link function.\n\nThis is `compose_link/2` with arguments flipped.\n\n## Examples\n\n iex> links =\n ...> fn x -> [x, x] end\n ...> |> pipe_compose_link(fn y -> [y * 10] end)\n ...> |> pipe_compose_link(fn z -> [z + 42] end)\n ...>\n ...> [1, 2, 3] >>> links\n [52, 52, 62, 62, 72, 72]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:111","signature":[["chainable",[],null],["binder",[],null]],"object_type":"FunctionObject","name":"reverse_bind","module_id":"Witchcraft.Chain","id":"reverse_bind/2","doc":"An alias for `reverse_chain/2`.\n\nProvided as a convenience for those coming from other languages.\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:91","signature":[["chain_fun",[],null],["chainable",[],null]],"object_type":"FunctionObject","name":"reverse_chain","module_id":"Witchcraft.Chain","id":"reverse_chain/2","doc":"`chain/2` but with the arguments flipped\n\n## Examples\n\n iex> reverse_chain(fn x -> [x, x] end, [1, 2, 3])\n [1, 1, 2, 2, 3, 3]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Chain.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:52","signature":[["chainable",[],null],["link_fun",[],null]],"object_type":"FunctionObject","name":"chain","module_id":"Witchcraft.Chain.Proto","id":"chain/2","doc":"Sequentially compose actions, piping values through successive function chains.\n\nThe applied linking function must be unary and return data in the same\ntype of container as the input. The chain function essentially \"unwraps\"\na contained value, applies a linking function that returns\nthe initial (wrapped) type, and collects them into a flat(ter) structure.\n\n`chain/2` is sometimes called \"flat map\", since it can also\nbe expressed as `data |> map(link_fun) |> flatten()`.\n\nAs a diagram:\n\n %Container --- (data -> %Container) ---> %Container\n\n## Examples\n\n iex> chain([1, 2, 3], fn x -> [x, x] end)\n [1, 1, 2, 2, 3, 3]\n\n iex> [1, 2, 3]\n ...> |> chain(fn x -> [x, x] end)\n ...> |> chain(fn y -> [y, 2 * y, 3 * y] end)\n [1, 2, 3, 1, 2, 3, 2, 4, 6, 2, 4, 6, 3, 6, 9, 3, 6, 9]\n\n iex> chain([1, 2, 3], fn x ->\n ...> chain([x + 1], fn y ->\n ...> chain([y + 2, y + 10], fn z ->\n ...> [x, y, z]\n ...> end)\n ...> end)\n ...> end)\n [1, 2, 4, 1, 2, 12, 2, 3, 5, 2, 3, 13, 3, 4, 6, 3, 4, 14]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Chain.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Chain.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:444","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Chain.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:444","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Chain.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:444","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Chain.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:449","signature":[["fun",[],null],["chain_fun",[],null]],"object_type":"FunctionObject","name":"chain","module_id":"Witchcraft.Chain.Proto.Function","id":"chain/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:452","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Chain.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:452","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Chain.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:452","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Chain.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:453","signature":[["list",[],null],["chain_fun",[],null]],"object_type":"FunctionObject","name":"chain","module_id":"Witchcraft.Chain.Proto.List","id":"chain/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/chain.ex:460","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Chain.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:460","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Chain.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/chain.ex:460","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Chain.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/chain.ex:469","signature":[["arg",[],"Elixir"],["chain_fun",[],null]],"object_type":"FunctionObject","name":"chain","module_id":"Witchcraft.Chain.Proto.Tuple","id":"chain/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/comonad.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Comonad","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/comonad.ex:38","signature":[["nested",[],null]],"object_type":"FunctionObject","name":"extract","module_id":"Witchcraft.Comonad","id":"extract/1","doc":"Extract a value out of some context / data structure. This is the opposite\nof `Witchcraft.Applicative.of/2`.\n\n## Examples\n\n iex> extract({1, 2})\n 2\n\n extract(%Id{id: 42})\n #=> 42\n\n","arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:55","signature":[["nested",[],null]],"object_type":"FunctionObject","name":"unwrap","module_id":"Witchcraft.Comonad","id":"unwrap/1","doc":"Alias for `extract/1`\n\n## Examples\n\n iex> unwrap({1, 2})\n 2\n\n unwrap(%Id{id: 42})\n #=> 42\n\n","arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Comonad.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:38","signature":[["nested",[],null]],"object_type":"FunctionObject","name":"extract","module_id":"Witchcraft.Comonad.Proto","id":"extract/1","doc":"Extract a value out of some context / data structure. This is the opposite\nof `Witchcraft.Applicative.of/2`.\n\n## Examples\n\n iex> extract({1, 2})\n 2\n\n extract(%Id{id: 42})\n #=> 42\n\n","arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Comonad.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Comonad.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:96","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Comonad.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/comonad.ex:96","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Comonad.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/comonad.ex:96","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Comonad.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/comonad.ex:102","signature":[["arg",[],"Elixir"]],"object_type":"FunctionObject","name":"extract","module_id":"Witchcraft.Comonad.Proto.Tuple","id":"extract/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Extend","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:257","signature":[["g",[],null],["f",[],null]],"object_type":"FunctionObject","name":"compose_colink","module_id":"Witchcraft.Extend","id":"compose_colink/2","doc":"\n## Examples\n\n iex> composed =\n ...> fn xs -> List.first(xs) * 10 end\n ...> |> compose_colink(fn ys -> List.first(ys) - 10 end)\n ...>\n ...> extend([1, 2, 3], composed)\n [-90, -80, -70]\n\n iex> fn xs -> List.first(xs) * 10 end\n ...> |> compose_colink(fn ys -> List.first(ys) - 10 end)\n ...> |> compose_colink(fn zs -> List.first(zs) * 50 end)\n ...> |> reverse_extend([1, 2, 3])\n [400, 900, 1400]\n\n iex> fn xs -> List.first(xs) * 10 end\n ...> |> compose_colink(fn ys -> List.first(ys) - 10 end)\n ...> |> compose_colink(fn zs -> List.first(zs) * 50 end)\n ...> |> compose_colink(fn zs -> List.first(zs) + 12 end)\n ...> |> reverse_extend([1, 2, 3])\n [6400, 6900, 7400]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/extend.ex:230","signature":[["data",[],null],["colink",[],null]],"object_type":"FunctionObject","name":"curried_extend","module_id":"Witchcraft.Extend","id":"curried_extend/2","doc":"The same as `extend/2`, but with the colinking function curried.\n\n## Examples\n\n iex> [1, 2, 3]\n ...> |> curried_extend(fn(list, coeff) -> List.first(list) * coeff end)\n ...> |> extend(fn(funs) -> List.first(funs).(10) end)\n [10, 20, 30]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/extend.ex:191","signature":[["data",[],null],["colink",[],null]],"object_type":"FunctionObject","name":"extend","module_id":"Witchcraft.Extend","id":"extend/2","doc":"Similar to `Witchcraft.Chain.chain/2`, except that it reverses the input and output\ntypes of the colinking function.\n\n## Examples\n\nChain:\n\n iex> Witchcraft.Chain.chain([1, 2, 3], fn x -> [x * 10] end)\n [10, 20, 30]\n\nExtend:\n\n iex> extend([1, 2, 3], fn list -> List.first(list) * 10 end)\n [10, 20, 30]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/extend.ex:43","signature":[["data",[],null]],"object_type":"FunctionObject","name":"nest","module_id":"Witchcraft.Extend","id":"nest/1","doc":"Wrap some nestable data structure in another layer of itself\n\n## Examples\n\n iex> nest([1, 2, 3])\n [[1, 2, 3], [2, 3], [3]]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:285","signature":[["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"pipe_compose_colink","module_id":"Witchcraft.Extend","id":"pipe_compose_colink/2","doc":"\n## Examples\n\n iex> fn xs -> List.first(xs) * 10 end\n ...> |> pipe_compose_colink(fn ys -> List.first(ys) - 2 end)\n ...> |> reverse_extend([1, 2, 3])\n [8, 18, 28]\n\n iex> composed =\n ...> fn xs -> List.first(xs) * 10 end\n ...> |> pipe_compose_colink(fn ys -> List.first(ys) - 2 end)\n ...> |> pipe_compose_colink(fn zs -> List.first(zs) * 5 end)\n ...>\n ...> extend([1, 2, 3], composed)\n [40, 90, 140]\n\n iex> fn xs -> List.first(xs) * 10 end\n ...> |> pipe_compose_colink(fn ys -> List.first(ys) - 2 end)\n ...> |> pipe_compose_colink(fn zs -> List.first(zs) * 5 end)\n ...> |> pipe_compose_colink(fn zs -> List.first(zs) + 1 end)\n ...> |> reverse_extend([1, 2, 3])\n [41, 91, 141]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/extend.ex:244","signature":[["colink",[],null],["data",[],null]],"object_type":"FunctionObject","name":"reverse_curried_extend","module_id":"Witchcraft.Extend","id":"reverse_curried_extend/2","doc":"The same as `extend/2`, but with the colinking function curried.\n\n## Examples\n\n iex> fn(list) -> List.first(list) * 10 end\n ...> |> reverse_curried_extend([1, 2, 3])\n [10, 20, 30]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/extend.ex:215","signature":[["colink",[],null],["data",[],null]],"object_type":"FunctionObject","name":"reverse_extend","module_id":"Witchcraft.Extend","id":"reverse_extend/2","doc":"`extend/2` with arguments flipped.\n\nMakes piping composed colinks easier (see `compose_colink/2` and `pipe_compose_colink/2`).\n\n## Examples\n\n iex> fn list -> List.first(list) * 10 end\n ...> |> reverse_extend([1, 2, 3])\n [10, 20, 30]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/extend.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Extend.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Extend.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Extend.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:43","signature":[["data",[],null]],"object_type":"FunctionObject","name":"nest","module_id":"Witchcraft.Extend.Proto","id":"nest/1","doc":"Wrap some nestable data structure in another layer of itself\n\n## Examples\n\n iex> nest([1, 2, 3])\n [[1, 2, 3], [2, 3], [3]]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:314","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Extend.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:314","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Extend.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:314","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Extend.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:315","signature":[["fun",[],null]],"object_type":"FunctionObject","name":"nest","module_id":"Witchcraft.Extend.Proto.Function","id":"nest/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:328","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Extend.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:328","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Extend.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:328","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Extend.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:329","signature":[["entire",[],null]],"object_type":"FunctionObject","name":"nest","module_id":"Witchcraft.Extend.Proto.List","id":"nest/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:333","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Extend.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:333","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Extend.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/extend.ex:333","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Extend.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/extend.ex:339","signature":[["arg",[],"Elixir"]],"object_type":"FunctionObject","name":"nest","module_id":"Witchcraft.Extend.Proto.Tuple","id":"nest/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Foldable","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable.ex:535","signature":[["foldable_bools",[],null]],"object_type":"FunctionObject","name":"all?","module_id":"Witchcraft.Foldable","id":"all?/1","doc":"Check if a foldable is full of only `true`s\n\n## Examples\n\n iex> all?([true, true, false])\n false\n\n %BinaryTree{\n left: true,\n right: %BinaryTree{\n left: true,\n right: false\n }\n } |> all?()\n #=> false\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:556","signature":[["foldable",[],null],["predicate",[],null]],"object_type":"FunctionObject","name":"all?","module_id":"Witchcraft.Foldable","id":"all?/2","doc":"The same as `all?/1`, but with a custom predicate matcher\n\n## Examples\n\n iex> import Integer\n ...> all?([1, 2, 3], &is_odd/1)\n false\n\n %BinaryTree{\n left: 1,\n right: %BinaryTree{\n left: 2,\n right: 3\n }\n }\n |> all?(&Integer.is_odd?/1)\n #=> false\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:581","signature":[["foldable_bools",[],null]],"object_type":"FunctionObject","name":"any?","module_id":"Witchcraft.Foldable","id":"any?/1","doc":"Check if a foldable contains any `true`s\n\n## Examples\n\n iex> any? [true, true, false]\n true\n\n %BinaryTree{\n left: true,\n right: %BinaryTree{\n left: true,\n right: false\n }\n } |> any?()\n #=> true\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:602","signature":[["foldable",[],null],["predicate",[],null]],"object_type":"FunctionObject","name":"any?","module_id":"Witchcraft.Foldable","id":"any?/2","doc":"The same as `all?/1`, but with a custom predicate matcher\n\n## Examples\n\n iex> require Integer\n ...> any?([1, 2, 3], &Integer.is_odd/1)\n true\n\n %BinaryTree{\n left: 1,\n right: %BinaryTree{\n left: 2,\n right: 3\n }\n }\n |> any(&Integer.is_odd?/1)\n #=> true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:465","signature":[["contained_lists",[],null]],"object_type":"FunctionObject","name":"concat","module_id":"Witchcraft.Foldable","id":"concat/1","doc":"Concatenate all lists in a foldable structure\n\n## Examples\n\n iex> concat([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n %BinaryTree{\n left: [1, 2, 3],\n right: %BinaryTree{\n left: [4, 5],\n right: [6]\n }\n }\n |> concat()\n #=> [1, 2, 3, 4, 5, 6]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:489","signature":[["foldable",[],null],["mapper",[],null]],"object_type":"FunctionObject","name":"concat_map","module_id":"Witchcraft.Foldable","id":"concat_map/2","doc":"Lift a function over a foldable structure generating lists of results,\nand then concatenate the resulting lists\n\n## Examples\n\n iex> concat_map([1, 2, 3, 4, 5, 6], fn x -> [x, x] end)\n [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6]\n\n %BinaryTree{\n left: 1,\n right: %BinaryTree{\n left: 2,\n right: 3\n }\n }\n |> concat_map(fn x -> [x, x] end)\n #=> [1, 1, 2, 2, 3, 3]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:270","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"count","module_id":"Witchcraft.Foldable","id":"count/1","doc":"See `Witchcraft.Foldable.length/1`.","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:234","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"empty?","module_id":"Witchcraft.Foldable","id":"empty?/1","doc":"Check if a foldable data structure is empty\n\n## Examples\n\n iex> empty?(\"\")\n true\n\n iex> empty?(\"hi\")\n false\n\n iex> empty?(%{})\n true\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:179","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"fold","module_id":"Witchcraft.Foldable","id":"fold/1","doc":"Combine all elements using monoidal append\n\n## Examples\n\n iex> fold([1, 2, 3])\n 6\n\n iex> fold([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:194","signature":[["foldable",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"fold_map","module_id":"Witchcraft.Foldable","id":"fold_map/2","doc":"Map a functional over all elements and `fold` them together\n\n## Examples\n\n iex> fold_map([1, 2, 3], fn x -> [x, x * 10] end)\n [1, 10, 2, 20, 3, 30]\n\n iex> fold_map([[1, 2, 3], [4, 5, 6], [7, 8, 9]], fn x -> [x, x] end)\n [\n [1, 2, 3], [1, 2, 3],\n [4, 5, 6], [4, 5, 6],\n [7, 8, 9], [7, 8, 9]\n ]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:159","signature":[["foldable",[],null],["folder",[],null]],"object_type":"FunctionObject","name":"left_fold","module_id":"Witchcraft.Foldable","id":"left_fold/2","doc":"The same as `left_fold/3`, but uses the first element as the seed\n\n## Examples\n\n iex> left_fold([1, 2, 3], &+/2)\n 6\n\n iex> left_fold([100, 2, 5], &//2)\n 10.0 # ((100 / 2) / 5)\n\n iex> left_fold([1 | [2 | [3]]], fn(x, acc) -> [x | acc] end)\n [[1 | 2] | 3]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:130","signature":[["foldable",[],null],["seed",[],null],["folder",[],null]],"object_type":"FunctionObject","name":"left_fold","module_id":"Witchcraft.Foldable","id":"left_fold/3","doc":"Left-associative fold over a structure to alter the structure and/or reduce\nit to a single summary value.\n\nThe folder must be a binary function, with the second argument being the\naccumulated value thus far.\n\n## Examples\n\n iex> sum = fn xs -> right_fold(xs, 0, &+/2) end\n ...> sum.([1, 2, 3])\n 6\n ...> sum.([4, 5, 6])\n 15\n\n iex> left_fold([1, 2, 3], [], fn(x, acc) -> [x | acc] end)\n [[[[] | 1] | 2] | 3]\n\n","arity":3},{"type":"def","source":"lib/witchcraft/foldable.ex:252","signature":[["list",[],null]],"object_type":"FunctionObject","name":"length","module_id":"Witchcraft.Foldable","id":"length/1","doc":"Count the number of elements in a foldable structure\n\n## Examples\n\n iex> use Witchcraft.Foldable\n ...> length(%{})\n 0\n iex> length(%{a: 1, b: 2})\n 2\n iex> length(\"ࠀabc\")\n 4\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:325","signature":[["foldable_comparable",[],null]],"object_type":"FunctionObject","name":"max","module_id":"Witchcraft.Foldable","id":"max/1","doc":"Find the maximum element in a foldable structure using the default ordering\nfrom `Witchcraft.Ord`.\n\nElements must implement `Witchcraft.Ord`.\n\n## Examples\n\n iex> use Witchcraft.Foldable\n ...> max([2, 3, 1])\n 3\n ...> max([[4], [1, 2, 3, 4]])\n [4]\n\n %BinaryTree{\n node: 1,\n left: %BinaryTree{\n node: 3\n left: 4\n },\n right: 2\n }\n |> max()\n #=> 4\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:296","signature":[["foldable",[],null],["list",[],"Elixir"]],"object_type":"FunctionObject","name":"max","module_id":"Witchcraft.Foldable","id":"max/2","doc":"Find the maximum element in a foldable structure using a custom comparitor\n\nElements must implement `Witchcraft.Ord`.\n\nComes in both a safe and unsafe(`!`) version\n\n## Examples\n\n iex> use Witchcraft.Foldable\n ...> [1, 2, 7]\n ...> |> max(by: fn(x, y) ->\n ...> x\n ...> |> Integer.mod(3)\n ...> |> Witchcraft.Ord.compare(Integer.mod(y, 3))\n ...> end)\n 2\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:273","signature":[["foldable",[],null],["target",[],null]],"object_type":"FunctionObject","name":"member?","module_id":"Witchcraft.Foldable","id":"member?/2","doc":"Check if a foldable structure contains a particular element\n\n## Examples\n\n iex> member?([1, 2, 3], 2)\n true\n\n iex> member?([1, 2, 3], 99)\n false\n\n iex> member?(%{a: 1, b: 2}, 2)\n false\n\n iex> member?(%{a: 1, b: 2}, {:b, 2})\n true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:383","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"min","module_id":"Witchcraft.Foldable","id":"min/1","doc":"Find the minimum element in a foldable structure using the default ordering\nfrom `Witchcraft.Ord`.\n\nElements must implement `Witchcraft.Ord`.\n\n## Examples\n\n iex> use Witchcraft.Foldable\n ...> min([2, 3, 1])\n 1\n ...> min([[4], [1, 2, 3, 4]])\n [1, 2, 3, 4]\n\n %BinaryTree{\n node: 4,\n left: %BinaryTree{\n node: 3\n left: 1\n },\n right: 2\n }\n |> min()\n #=> 1\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:354","signature":[["foldable",[],null],["list",[],"Elixir"]],"object_type":"FunctionObject","name":"min","module_id":"Witchcraft.Foldable","id":"min/2","doc":"Find the maximum element in a foldable structure using a custom comparitor\n\nElements must implement `Witchcraft.Ord`.\n\nComes in both a safe and unsafe(`!`) version\n\n## Examples\n\n iex> use Witchcraft.Foldable\n ...> [8, 2, 1]\n ...> |> min(by: fn(x, y) ->\n ...> x\n ...> |> Integer.mod(4)\n ...> |> Witchcraft.Ord.compare(Integer.mod(y, 4))\n ...> end)\n 8\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:518","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"null?","module_id":"Witchcraft.Foldable","id":"null?/1","doc":"Test whether the structure is empty. The default implementation is\noptimized for structures that are similar to lists, because there\nis no general way to do better.\n\n## Examples\n\n iex> null?([])\n true\n\n iex> null?([1, 2, 3])\n false\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:443","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"product","module_id":"Witchcraft.Foldable","id":"product/1","doc":"Product of all numbers in a foldable\n\n## Examples\n\n iex> product([1, 2, 3])\n 6\n\n %BinaryTree{\n left: 4,\n right: %BinaryTree{\n left: 2,\n right: 10\n }\n }\n |> product()\n #=> 80\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:412","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"random","module_id":"Witchcraft.Foldable","id":"random/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:107","signature":[["foldable",[],null],["folder",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable","id":"right_fold/2","doc":"The same as `right_fold/3`, but uses the first element as the seed\n\n## Examples\n\n iex> right_fold([1, 2, 3], &+/2)\n 6\n\n iex> right_fold([100, 2, 5], &//2)\n 40.0 # (2 / (5 / 100))\n\n iex> right_fold([[], 1, 2, 3], fn(x, acc) -> [x | acc] end)\n [1, 2, 3]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/foldable.ex:63","signature":[["foldable",[],null],["seed",[],null],["folder",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable","id":"right_fold/3","doc":"Right-associative fold over a structure to alter the structure and/or reduce\nit to a single summary value. The right-association makes it possible to\ncease computation on infinite streams of data.\n\nThe folder must be a binary function, with the second argument being the\naccumulated value thus far.\n\n## Examples\n\n iex> sum = fn xs -> right_fold(xs, 0, &+/2) end\n ...> sum.([1, 2, 3])\n 6\n ...> sum.([4, 5, 6])\n 15\n\n","arity":3},{"type":"def","source":"lib/witchcraft/foldable.ex:271","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"size","module_id":"Witchcraft.Foldable","id":"size/1","doc":"See `Witchcraft.Foldable.length/1`.","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:422","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"sum","module_id":"Witchcraft.Foldable","id":"sum/1","doc":"Sum all numbers in a foldable\n\n## Examples\n\n iex> sum([1, 2, 3])\n 6\n\n %BinaryTree{\n left: 4,\n right: %BinaryTree{\n left: 2,\n right: 10\n }\n } |> sum()\n #=> 16\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:627","signature":[["foldable_monad",[],null]],"object_type":"FunctionObject","name":"then_sequence","module_id":"Witchcraft.Foldable","id":"then_sequence/1","doc":"Run each action from left to right, discarding all values.\n\nAlways returns `%Witchcraft.Unit{}` in the same foldbale structure that you started with.\n\n## Examples\n\n iex> then_sequence([[1, 2, 3], [4, 5, 6]])\n [\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{},\n %Witchcraft.Unit{}\n ]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:219","signature":[["foldable",[],null]],"object_type":"FunctionObject","name":"to_list","module_id":"Witchcraft.Foldable","id":"to_list/1","doc":"Turn any `Foldable` into a `List`\n\n## Example\n\n iex> to_list({1, 2, 3})\n [1, 2, 3]\n\n iex> to_list(%{a: 1, b: 2, c: 3})\n [c: 3, b: 2, a: 1]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/foldable/empty_error.ex:25","signature":[["msg",[],null]],"object_type":"FunctionObject","name":"exception","module_id":"Witchcraft.Foldable.EmptyError","id":"exception/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/foldable/empty_error.ex:25","signature":[["exception",[],null]],"object_type":"FunctionObject","name":"message","module_id":"Witchcraft.Foldable.EmptyError","id":"message/1","doc":null,"arity":1},{"type":"defmacro","source":"lib/witchcraft/foldable/empty_error.ex:27","signature":[["data",[],null]],"object_type":"FunctionObject","name":"new","module_id":"Witchcraft.Foldable.EmptyError","id":"new/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Foldable.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Foldable.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Foldable.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable.ex:63","signature":[["foldable",[],null],["seed",[],null],["folder",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable.Proto","id":"right_fold/3","doc":"Right-associative fold over a structure to alter the structure and/or reduce\nit to a single summary value. The right-association makes it possible to\ncease computation on infinite streams of data.\n\nThe folder must be a binary function, with the second argument being the\naccumulated value thus far.\n\n## Examples\n\n iex> sum = fn xs -> right_fold(xs, 0, &+/2) end\n ...> sum.([1, 2, 3])\n 6\n ...> sum.([4, 5, 6])\n 15\n\n","arity":3},{"type":"def","source":"lib/witchcraft/foldable/bitstring.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Foldable.Proto.BitString","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/bitstring.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Foldable.Proto.BitString","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/bitstring.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Foldable.Proto.BitString","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable/bitstring.ex:4","signature":[["string",[],null],["seed",[],null],["reducer",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable.Proto.BitString","id":"right_fold/3","doc":null,"arity":3},{"type":"def","source":"lib/witchcraft/foldable/list.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Foldable.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/list.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Foldable.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/list.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Foldable.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable/list.ex:4","signature":[["list",[],null],["seed",[],null],["reducer",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable.Proto.List","id":"right_fold/3","doc":null,"arity":3},{"type":"def","source":"lib/witchcraft/foldable/map.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Foldable.Proto.Map","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/map.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Foldable.Proto.Map","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/map.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Foldable.Proto.Map","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable/map.ex:4","signature":[["map",[],null],["seed",[],null],["reducer",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable.Proto.Map","id":"right_fold/3","doc":null,"arity":3},{"type":"def","source":"lib/witchcraft/foldable/tuple.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Foldable.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/tuple.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Foldable.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/foldable/tuple.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Foldable.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/foldable/tuple.ex:4","signature":[["tuple",[],null],["seed",[],null],["reducer",[],null]],"object_type":"FunctionObject","name":"right_fold","module_id":"Witchcraft.Foldable.Proto.Tuple","id":"right_fold/3","doc":null,"arity":3},{"type":"def","source":"lib/witchcraft/functor.ex:118","signature":[["fun",[],null],["data",[],null]],"object_type":"FunctionObject","name":"<~","module_id":"Witchcraft.Functor","id":"<~/2","doc":"`<~/2` with arguments flipped.\n\n iex> (fn x -> x + 5 end) <~ [1,2,3]\n [6, 7, 8]\n\nNote that the mnemonic is flipped from `|>`, and combinging directions can\nbe confusing. It's generally recommended to use `~>`, or to keep `<~` on\nthe same line both of it's arguments:\n\n iex> fn(x, y) -> x + y end <~ [1, 2, 3]\n ...> |> List.first()\n ...> |> apply([9])\n 10\n\n...or in an expression that's only pointing left:\n\n iex> fn y -> y * 10 end\n ...> <~ fn x -> x + 55 end\n ...> <~ [1, 2, 3]\n [560, 570, 580]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Functor","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:76","signature":[["wrapped",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"lift","module_id":"Witchcraft.Functor","id":"lift/2","doc":"`map/2` but with the function automatically curried\n\n## Examples\n\n iex> lift([1, 2, 3], fn x -> x + 1 end)\n [2, 3, 4]\n\n iex> [1, 2, 3]\n ...> |> lift(fn x -> x + 55 end)\n ...> |> lift(fn y -> y * 10 end)\n [560, 570, 580]\n\n iex> [1, 2, 3]\n ...> |> lift(fn(x, y) -> x + y end)\n ...> |> List.first()\n ...> |> apply([9])\n 10\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:32","signature":[["wrapped",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"map","module_id":"Witchcraft.Functor","id":"map/2","doc":"`map` a function into one layer of a data wrapper.\nThere is an autocurrying variant: `lift/2`.\n\n## Examples\n\n iex> map([1, 2, 3], fn x -> x + 1 end)\n [2, 3, 4]\n\n iex> %{a: 1, b: 2} ~> fn x -> x * 10 end\n %{a: 10, b: 20}\n\n iex> map(%{a: 2, b: [1, 2, 3]}, fn\n ...> int when is_integer(int) -> int * 100\n ...> value -> inspect(value)\n ...> end)\n %{a: 200, b: \"[1, 2, 3]\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:143","signature":[["wrapped",[],null],["replace_with",[],null]],"object_type":"FunctionObject","name":"replace","module_id":"Witchcraft.Functor","id":"replace/2","doc":"Replace all inner elements with a constant value\n\n## Examples\n\n iex> replace([1, 2, 3], \"hi\")\n [\"hi\", \"hi\", \"hi\"]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:99","signature":[["data",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"~>","module_id":"Witchcraft.Functor","id":"~>/2","doc":"Operator alias for `lift/2`\n\n## Example\n\n iex> [1, 2, 3]\n ...> ~> fn x -> x + 55 end\n ...> ~> fn y -> y * 10 end\n [560, 570, 580]\n\n iex> [1, 2, 3]\n ...> ~> fn(x, y) -> x + y end\n ...> |> List.first()\n ...> |> apply([9])\n 10\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Functor.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Functor.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Functor.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:32","signature":[["wrapped",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"map","module_id":"Witchcraft.Functor.Proto","id":"map/2","doc":"`map` a function into one layer of a data wrapper.\nThere is an autocurrying variant: `lift/2`.\n\n## Examples\n\n iex> map([1, 2, 3], fn x -> x + 1 end)\n [2, 3, 4]\n\n iex> %{a: 1, b: 2} ~> fn x -> x * 10 end\n %{a: 10, b: 20}\n\n iex> map(%{a: 2, b: [1, 2, 3]}, fn\n ...> int when is_integer(int) -> int * 100\n ...> value -> inspect(value)\n ...> end)\n %{a: 200, b: \"[1, 2, 3]\"}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:156","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Functor.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:156","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Functor.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:156","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Functor.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:159","signature":[["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"map","module_id":"Witchcraft.Functor.Proto.Function","id":"map/2","doc":"Compose functions\n\n## Example\n\n iex> ex = Witchcraft.Functor.lift(fn x -> x * 10 end, fn x -> x + 2 end)\n ...> ex.(2)\n 22\n\n","arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:172","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Functor.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:172","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Functor.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:172","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Functor.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:173","signature":[["list",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"map","module_id":"Witchcraft.Functor.Proto.List","id":"map/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:210","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Functor.Proto.Map","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:210","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Functor.Proto.Map","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:210","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Functor.Proto.Map","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:211","signature":[["hashmap",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"map","module_id":"Witchcraft.Functor.Proto.Map","id":"map/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/functor.ex:176","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Functor.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:176","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Functor.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/functor.ex:176","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Functor.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/functor.ex:177","signature":[["tuple",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"map","module_id":"Witchcraft.Functor.Proto.Tuple","id":"map/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/monad.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Monad","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"defmacro","source":"lib/witchcraft/monad.ex:53","signature":[["sample",[],null],["list",[],"Elixir"]],"object_type":"FunctionObject","name":"monad","module_id":"Witchcraft.Monad","id":"monad/2","doc":"do-notation enhanced with a `return` operation.\n\n`return` is the simplest possible linking function, providing the correct `of/2`\ninstance for your monad.\n\n## Examples\n\n iex> monad [] do\n ...> [1, 2, 3]\n ...> end\n [1, 2, 3]\n\n iex> monad [] do\n ...> [1, 2, 3]\n ...> [4, 5, 6]\n ...> [7, 8, 9]\n ...> end\n [\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9,\n 7, 8, 9\n ]\n\n iex> monad [] do\n ...> Witchcraft.Applicative.of([], 1)\n ...> end\n [1]\n\n iex> monad [] do\n ...> a <- [1,2,3]\n ...> b <- [4,5,6]\n ...> return(a * b)\n ...> end\n [\n 4, 8, 12,\n 5, 10, 15,\n 6, 12, 18\n ]\n\n iex> monad [] do\n ...> a <- return 1\n ...> b <- return 2\n ...> return(a + b)\n ...> end\n [3]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/monad.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Monad.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monad.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Monad.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monad.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Monad.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monad.ex:135","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monad.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monad.ex:135","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monad.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monad.ex:135","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monad.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monad.ex:136","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monad.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monad.ex:136","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monad.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monad.ex:136","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monad.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monad.ex:138","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monad.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monad.ex:138","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monad.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monad.ex:138","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monad.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Monoid","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:30","signature":[["sample",[],null]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid","id":"empty/1","doc":"An \"emptied out\" or \"starting position\" of the passed data\n\n## Example\n\n iex> empty(10)\n 0\n\n iex> empty [1, 2, 3, 4, 5]\n []\n\n","arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:47","signature":[["monoid",[],null]],"object_type":"FunctionObject","name":"empty?","module_id":"Witchcraft.Monoid","id":"empty?/1","doc":"Check if a value is the empty element of that type\n\n## Examples\n\n iex> empty?([])\n true\n\n iex> empty?([1])\n false\n\n","arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:45","signature":[["sample",[],null]],"object_type":"FunctionObject","name":"zero","module_id":"Witchcraft.Monoid","id":"zero/1","doc":"See `Witchcraft.Monoid.empty/1`.","arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Monoid.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:30","signature":[["sample",[],null]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto","id":"empty/1","doc":"An \"emptied out\" or \"starting position\" of the passed data\n\n## Example\n\n iex> empty(10)\n 0\n\n iex> empty [1, 2, 3, 4, 5]\n []\n\n","arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Monoid.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Monoid.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:97","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.BitString","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:97","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.BitString","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:97","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.BitString","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:98","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.BitString","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:93","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.Float","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:93","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.Float","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:93","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.Float","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:94","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.Float","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:85","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:85","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:85","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:86","signature":[["sample",[],null]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.Function","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:89","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.Integer","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:89","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.Integer","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:89","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.Integer","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:90","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.Integer","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:101","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:101","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:101","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:102","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.List","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:105","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.Map","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:105","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.Map","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:105","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.Map","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:106","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.Map","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:109","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Monoid.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:109","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Monoid.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/monoid.ex:109","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Monoid.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/monoid.ex:114","signature":[["_",[],"Elixir"]],"object_type":"FunctionObject","name":"empty","module_id":"Witchcraft.Monoid.Proto.Tuple","id":"empty/1","doc":null,"arity":1},{"type":"def","source":"lib/witchcraft/ord.ex:154","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"<","module_id":"Witchcraft.Ord","id":" use Witchcraft.Ord\n ...> 1 <= 2\n true\n ...> [] <= [1, 2, 3]\n false\n ...> [1] <= [1, 2, 3]\n true\n ...> [4] <= [1, 2, 3]\n false\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:137","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":">","module_id":"Witchcraft.Ord","id":">/2","doc":"See `Witchcraft.Ord.greater?/2`.","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:176","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":">=","module_id":"Witchcraft.Ord","id":">=/2","doc":"Determine if an element is `:greater` or `:equal` to another\n\n## Examples\n\n iex> use Witchcraft.Ord\n ...> 2 >= 1\n true\n ...> [1, 2, 3] >= []\n true\n ...> [1, 2, 3] >= [1]\n true\n ...> [1, 2, 3] >= [4]\n false\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Ord","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord.ex:49","signature":[["ord_a",[],null],["ord_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord","id":"compare/2","doc":"Get the ordering relationship between two elements.\n\nPossible results are `:lesser`, `:equal`, and `:greater`\n\n## Examples\n\n iex> compare(1, 1)\n :equal\n\n iex> compare([1], [2])\n :lesser\n\n iex> compare([1, 2], [3])\n :lesser\n\n iex> compare([3, 2, 1], [1, 2, 3, 4, 5])\n :greater\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:107","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"equal?","module_id":"Witchcraft.Ord","id":"equal?/2","doc":"Determine if two elements are `:equal`\n\n## Examples\n\n iex> equal?(1, 1.0)\n true\n\n iex> equal?(1, 2)\n false\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:122","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"greater?","module_id":"Witchcraft.Ord","id":"greater?/2","doc":"Determine if an element is `:greater` than another\n\n## Examples\n\n iex> greater?(1, 1)\n false\n\n iex> greater?(1.1, 1)\n true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:139","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"lesser?","module_id":"Witchcraft.Ord","id":"lesser?/2","doc":"Determine if an element is `:lesser` than another\n\n## Examples\n\n iex> lesser?(1, 1)\n false\n\n iex> lesser?(1, 1.1)\n true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Ord.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord.ex:49","signature":[["ord_a",[],null],["ord_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto","id":"compare/2","doc":"Get the ordering relationship between two elements.\n\nPossible results are `:lesser`, `:equal`, and `:greater`\n\n## Examples\n\n iex> compare(1, 1)\n :equal\n\n iex> compare([1], [2])\n :lesser\n\n iex> compare([1, 2], [3])\n :lesser\n\n iex> compare([3, 2, 1], [1, 2, 3, 4, 5])\n :greater\n\n","arity":2},{"type":"def","source":"lib/witchcraft/ord.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Ord.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Ord.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/string.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Ord.Proto.BitString","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/string.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Ord.Proto.BitString","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/string.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Ord.Proto.BitString","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/string.ex:4","signature":[["string_a",[],null],["string_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto.BitString","id":"compare/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/ord/float.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Ord.Proto.Float","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/float.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Ord.Proto.Float","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/float.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Ord.Proto.Float","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/float.ex:4","signature":[["float_a",[],null],["float_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto.Float","id":"compare/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/ord/integer.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Ord.Proto.Integer","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/integer.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Ord.Proto.Integer","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/integer.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Ord.Proto.Integer","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/integer.ex:4","signature":[["int_a",[],null],["int_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto.Integer","id":"compare/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/ord/list.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Ord.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/list.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Ord.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/list.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Ord.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/list.ex:15","signature":[["arg1",[],"Elixir"],["b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto.List","id":"compare/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/ord/map.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Ord.Proto.Map","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/map.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Ord.Proto.Map","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/map.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Ord.Proto.Map","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/map.ex:18","signature":[["map_a",[],null],["map_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto.Map","id":"compare/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/ord/tuple.ex:3","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Ord.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/tuple.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Ord.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/ord/tuple.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Ord.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/ord/tuple.ex:12","signature":[["tuple_a",[],null],["tuple_b",[],null]],"object_type":"FunctionObject","name":"compare","module_id":"Witchcraft.Ord.Proto.Tuple","id":"compare/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:84","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"<>","module_id":"Witchcraft.Semigroup","id":"<>/2","doc":"See `Witchcraft.Semigroup.append/2`.","arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Semigroup","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:46","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup","id":"append/2","doc":"`append`enate two data of the same type. Can be chained an arbitrary number of times.\nThese can be chained together an arbitrary number of times. For example:\n\n iex> 1 |> append(2) |> append(3)\n 6\n\n iex> [1, 2, 3]\n ...> |> append([4, 5, 6])\n ...> |> append([7, 8, 9])\n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n iex> \"foo\" |> append(\" \") |> append(\"bar\")\n \"foo bar\"\n\n## Operator\n\n iex> use Witchcraft.Semigroup\n ...> 1 <> 2 <> 3 <> 5 <> 7\n 18\n\n iex> use Witchcraft.Semigroup\n ...> [1, 2, 3] <> [4, 5, 6] <> [7, 8, 9]\n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n iex> use Witchcraft.Semigroup\n ...> \"foo\" <> \" \" <> \"bar\"\n \"foo bar\"\n\nThere is an operator alias `a <> b`. Since this conflicts with `Kernel.<>/2`,\n`use Witchcraft,Semigroup` will automatically exclude the Kernel operator.\nThis is highly recommended, since `<>` behaves the same on bitstrings, but is\nnow available on more datatypes.\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:86","signature":[["semigroup_of_lists",[],null]],"object_type":"FunctionObject","name":"concat","module_id":"Witchcraft.Semigroup","id":"concat/1","doc":"Flatten a list of homogeneous semigroups to a single container\n\n## Example\n\n iex> concat [\n ...> [1, 2, 3],\n ...> [4, 5, 6]\n ...> ]\n [1, 2, 3, 4, 5, 6]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:103","signature":[["to_repeat",[],null],["list",[],"Elixir"]],"object_type":"FunctionObject","name":"repeat","module_id":"Witchcraft.Semigroup","id":"repeat/2","doc":"Repeat the contents of a semigroup a certain number of times\n\n## Examples\n\n iex> [1, 2, 3] |> repeat(times: 3)\n [1, 2, 3, 1, 2, 3, 1, 2, 3]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Semigroup.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:46","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto","id":"append/2","doc":"`append`enate two data of the same type. Can be chained an arbitrary number of times.\nThese can be chained together an arbitrary number of times. For example:\n\n iex> 1 |> append(2) |> append(3)\n 6\n\n iex> [1, 2, 3]\n ...> |> append([4, 5, 6])\n ...> |> append([7, 8, 9])\n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n iex> \"foo\" |> append(\" \") |> append(\"bar\")\n \"foo bar\"\n\n## Operator\n\n iex> use Witchcraft.Semigroup\n ...> 1 <> 2 <> 3 <> 5 <> 7\n 18\n\n iex> use Witchcraft.Semigroup\n ...> [1, 2, 3] <> [4, 5, 6] <> [7, 8, 9]\n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n iex> use Witchcraft.Semigroup\n ...> \"foo\" <> \" \" <> \"bar\"\n \"foo bar\"\n\nThere is an operator alias `a <> b`. Since this conflicts with `Kernel.<>/2`,\n`use Witchcraft,Semigroup` will automatically exclude the Kernel operator.\nThis is highly recommended, since `<>` behaves the same on bitstrings, but is\nnow available on more datatypes.\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Semigroup.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Semigroup.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:147","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.BitString","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:147","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.BitString","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:147","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.BitString","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:148","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.BitString","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:143","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.Float","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:143","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.Float","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:143","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.Float","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:144","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.Float","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:135","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:135","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:135","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:136","signature":[["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.Function","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:139","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.Integer","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:139","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.Integer","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:139","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.Integer","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:140","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.Integer","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:151","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:151","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:151","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:152","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.List","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:155","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.Map","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:155","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.Map","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:155","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.Map","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:156","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.Map","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroup.ex:159","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroup.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:159","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroup.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroup.ex:159","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroup.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroup.ex:167","signature":[["tuple_a",[],null],["tuple_b",[],null]],"object_type":"FunctionObject","name":"append","module_id":"Witchcraft.Semigroup.Proto.Tuple","id":"append/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:91","signature":[["g",[],null],["f",[],null]],"object_type":"FunctionObject","name":"<|>","module_id":"Witchcraft.Semigroupoid","id":"<|>/2","doc":"Composition operator \"the math way\"\n\n## Examples\n\n iex> times_ten_plus_one =\n ...> fn x -> x + 1 end\n ...> <|> fn y -> y * 10 end\n ...>\n ...> times_ten_plus_one.(5)\n 51\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:107","signature":[["f",[],null],["g",[],null]],"object_type":"FunctionObject","name":"<~>","module_id":"Witchcraft.Semigroupoid","id":"<~>/2","doc":"Composition operator \"the pipe way\"\n\n## Examples\n\n iex> times_ten_plus_one =\n ...> fn y -> y * 10 end\n ...> <~> fn x -> x + 1 end\n ...>\n ...> times_ten_plus_one.(5)\n 51\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Semigroupoid","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:50","signature":[["morphism",[],null],["arguments",[],null]],"object_type":"FunctionObject","name":"apply","module_id":"Witchcraft.Semigroupoid","id":"apply/2","doc":"Express how to apply a function to actual arguments, or \"run the morphism\"\n\n## Examples\n\n iex> Witchcraft.Semigroupoid.apply(&inspect/1, [42])\n \"42\"\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:37","signature":[["morphism_a",[],null],["morphism_b",[],null]],"object_type":"FunctionObject","name":"compose","module_id":"Witchcraft.Semigroupoid","id":"compose/2","doc":"Take some value and return it again\n\n## Examples\n\n iex> times_ten_plus_one = compose(fn x -> x + 1 end, fn y -> y * 10 end)\n ...> times_ten_plus_one.(5)\n 51\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:63","signature":[["data",[],null],["fun",[],null]],"object_type":"FunctionObject","name":"pipe","module_id":"Witchcraft.Semigroupoid","id":"pipe/2","doc":"Pipe some data through a morphism.\n\nSimilar to `apply/2`, but with a single argument, not needing to wrap\nthe argument in a list.\n\n## Examples\n\n iex> pipe(42, &(&1 + 1))\n 43\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:78","signature":[["b",[],null],["a",[],null]],"object_type":"FunctionObject","name":"pipe_compose","module_id":"Witchcraft.Semigroupoid","id":"pipe_compose/2","doc":"`Compose`, but with the arguments flipped (same direction as `|>`)\n\n## Examples\n\n iex> times_ten_plus_one = pipe_compose(fn y -> y * 10 end, fn x -> x + 1 end)\n ...> times_ten_plus_one.(5)\n 51\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Semigroupoid.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:50","signature":[["morphism",[],null],["arguments",[],null]],"object_type":"FunctionObject","name":"apply","module_id":"Witchcraft.Semigroupoid.Proto","id":"apply/2","doc":"Express how to apply a function to actual arguments, or \"run the morphism\"\n\n## Examples\n\n iex> Witchcraft.Semigroupoid.apply(&inspect/1, [42])\n \"42\"\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:37","signature":[["morphism_a",[],null],["morphism_b",[],null]],"object_type":"FunctionObject","name":"compose","module_id":"Witchcraft.Semigroupoid.Proto","id":"compose/2","doc":"Take some value and return it again\n\n## Examples\n\n iex> times_ten_plus_one = compose(fn x -> x + 1 end, fn y -> y * 10 end)\n ...> times_ten_plus_one.(5)\n 51\n\n","arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Semigroupoid.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Semigroupoid.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:137","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Semigroupoid.Proto.Function","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:137","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Semigroupoid.Proto.Function","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:137","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Semigroupoid.Proto.Function","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:138","signature":[["fun",[],null],["args",[],null]],"object_type":"FunctionObject","name":"apply","module_id":"Witchcraft.Semigroupoid.Proto.Function","id":"apply/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/semigroupoid.ex:139","signature":[["fun_a",[],null],["fun_b",[],null]],"object_type":"FunctionObject","name":"compose","module_id":"Witchcraft.Semigroupoid.Proto.Function","id":"compose/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:88","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"!=","module_id":"Witchcraft.Setoid","id":"!=/2","doc":"See `Witchcraft.Setoid.nonequivalent?/2`.","arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:74","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"==","module_id":"Witchcraft.Setoid","id":"==/2","doc":"See `Witchcraft.Setoid.equivalent?/2`.","arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Setoid","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:42","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid","id":"equivalent?/2","doc":"Compare two setoids and determine if they are equivalent\n\nAliased as `==`\n\n## Examples\n\n iex> equivalent?(1, 2)\n false\n\n iex> import Kernel, except: [==: 2, !=: 2]\n ...> %{a: 1} == %{a: 1, b: 2}\n false\n\n equivalent?(%Maybe.Just{just: 42}, %Maybe.Nothing{})\n #=> false\n\n### Equivalence not equality\n\n baby_harry = %Wizard{name: \"Harry Potter\", age: 10}\n old_harry = %Wizard{name: \"Harry Potter\", age: 17}\n\n def chosen_one?(some_wizard), do: equivalent?(baby_harry, some_wizard)\n\n chosen_one?(old_harry)\n #=> true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:76","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"nonequivalent?","module_id":"Witchcraft.Setoid","id":"nonequivalent?/2","doc":"The opposite of `equivalent?/2`\n\n## Examples\n\n iex> nonequivalent?(1, 2)\n true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Setoid.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:42","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto","id":"equivalent?/2","doc":"Compare two setoids and determine if they are equivalent\n\nAliased as `==`\n\n## Examples\n\n iex> equivalent?(1, 2)\n false\n\n iex> import Kernel, except: [==: 2, !=: 2]\n ...> %{a: 1} == %{a: 1, b: 2}\n false\n\n equivalent?(%Maybe.Just{just: 42}, %Maybe.Nothing{})\n #=> false\n\n### Equivalence not equality\n\n baby_harry = %Wizard{name: \"Harry Potter\", age: 10}\n old_harry = %Wizard{name: \"Harry Potter\", age: 17}\n\n def chosen_one?(some_wizard), do: equivalent?(baby_harry, some_wizard)\n\n chosen_one?(old_harry)\n #=> true\n\n","arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Setoid.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Setoid.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:118","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.BitString","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:118","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.BitString","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:118","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.BitString","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:119","signature":[["string_a",[],null],["string_b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.BitString","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:114","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.Float","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:114","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.Float","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:114","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.Float","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:115","signature":[["float",[],null],["num",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.Float","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:110","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.Integer","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:110","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.Integer","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:110","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.Integer","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:111","signature":[["int",[],null],["num",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.Integer","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:126","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:126","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:126","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:127","signature":[["list_a",[],null],["list_b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.List","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:130","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.Map","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:130","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.Map","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:130","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.Map","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:131","signature":[["map_a",[],null],["map_b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.Map","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:134","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.MapSet","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:134","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.MapSet","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:134","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.MapSet","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:135","signature":[["a",[],null],["b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.MapSet","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/setoid.ex:122","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Setoid.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:122","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Setoid.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/setoid.ex:122","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Setoid.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/setoid.ex:123","signature":[["tuple_a",[],null],["tuple_b",[],null]],"object_type":"FunctionObject","name":"equivalent?","module_id":"Witchcraft.Setoid.Proto.Tuple","id":"equivalent?/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:3","signature":[],"object_type":"FunctionObject","name":"__force_type_class__","module_id":"Witchcraft.Traversable","id":"__force_type_class__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/traversable.ex:155","signature":[["link",[],null],["traversable",[],null]],"object_type":"FunctionObject","name":"across","module_id":"Witchcraft.Traversable","id":"across/2","doc":"`traverse/2` with arguments reversed.\n\n## Examples\n\n iex> fn x -> {x, x * 2, x * 10} end |> across([1, 2, 3])\n {6, 12, [10, 20, 30]}\n\n iex> fn x -> [x] end |> across({1, 2, 3})\n [{1, 2, 3}]\n\n iex> fn x -> [x, x * 5, x * 10] end |> across({1, 2, 3})\n [\n {1, 2, 3},\n {1, 2, 15},\n {1, 2, 30}\n ]\n\n iex> fn x -> [x, x * 5, x * 10] end |> across([1, 2, 3])\n [\n #\n [1, 2, 3], [1, 2, 15], [1, 2, 30],\n [1, 10, 3], [1, 10, 15], [1, 10, 30],\n [1, 20, 3], [1, 20, 15], [1, 20, 30],\n #\n [5, 2, 3], [5, 2, 15], [5, 2, 30],\n [5, 10, 3], [5, 10, 15], [5, 10, 30],\n [5, 20, 3], [5, 20, 15], [5, 20, 30],\n #\n [10, 2, 3], [10, 2, 15], [10, 2, 30],\n [10, 10, 3], [10, 10, 15], [10, 10, 30],\n [10, 20, 3], [10, 20, 15], [10, 20, 30]\n ]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:251","signature":[["traversable",[],null]],"object_type":"FunctionObject","name":"sequence","module_id":"Witchcraft.Traversable","id":"sequence/1","doc":"Run each action/effect in sequence (from left to right),\nand accumulate values along the way.\n\n## Examples\n\n iex> sequence([{1, 2, 3}, {4, 5, 6}])\n {5, 7, [3, 6]}\n\n iex> [\n ...> [1, 2, 3],\n ...> [4, 5, 6]\n ...> ]\n ...> |> sequence()\n [\n [1, 4],\n [1, 5],\n [1, 6],\n [2, 4],\n [2, 5],\n [2, 6],\n [3, 4],\n [3, 5],\n [3, 6]\n ]\n\n","arity":1},{"type":"def","source":"lib/witchcraft/traversable.ex:225","signature":[["link",[],null],["traversable",[],null]],"object_type":"FunctionObject","name":"then_across","module_id":"Witchcraft.Traversable","id":"then_across/2","doc":"The same as `then_traverse`, but with the arguments flipped.\n\n## Examples\n\n iex> fn x -> [x, x * 5, x * 10] end\n ...> |> then_across([1, 2, 3])\n [\n #\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n #\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n #\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}\n ]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:193","signature":[["traversable",[],null],["link",[],null]],"object_type":"FunctionObject","name":"then_traverse","module_id":"Witchcraft.Traversable","id":"then_traverse/2","doc":"`traverse` actions over data, but ignore the results.\n\n## Examples\n\n iex> [1, 2, 3]\n ...> |> then_traverse(fn x -> [x, x * 5, x * 10] end)\n [\n #\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n #\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n #\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{},\n %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}\n ]\n\n","arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:45","signature":[["data",[],null],["link",[],null]],"object_type":"FunctionObject","name":"traverse","module_id":"Witchcraft.Traversable","id":"traverse/2","doc":"Convert elements to actions, and then evaluate the actions from left-to-right,\nand accumulate the results.\n\nFor a version without accumulation, see `then_traverse/2`.\n\n## Examples\n\n iex> traverse([1, 2, 3], fn x -> {x, x * 2, x * 10} end)\n {6, 12, [10, 20, 30]}\n\n iex> traverse({1, 2, 3}, fn x -> [x] end)\n [{1, 2, 3}]\n\n iex> traverse({1, 2, 3}, fn x -> [x, x * 5, x * 10] end)\n [\n {1, 2, 3},\n {1, 2, 15},\n {1, 2, 30}\n ]\n\n iex> traverse([1, 2, 3], fn x -> [x, x * 5, x * 10] end)\n [\n #\n [1, 2, 3], [1, 2, 15], [1, 2, 30],\n [1, 10, 3], [1, 10, 15], [1, 10, 30],\n [1, 20, 3], [1, 20, 15], [1, 20, 30],\n #\n [5, 2, 3], [5, 2, 15], [5, 2, 30],\n [5, 10, 3], [5, 10, 15], [5, 10, 30],\n [5, 20, 3], [5, 20, 15], [5, 20, 30],\n #\n [10, 2, 3], [10, 2, 15], [10, 2, 30],\n [10, 10, 3], [10, 10, 15], [10, 10, 30],\n [10, 20, 3], [10, 20, 15], [10, 20, 30]\n ]\n\n traverse([1, 2, 3], fn x -> %Algae.Maybe.Just{just: x} end)\n #=> %Algae.Maybe.Just{just: [1, 2, 3]}\n\n traverse(%Algae.Maybe.Just{just: 4}, fn x -> [x, x * 10] end)\n #=> [\n # %Algae.Maybe.Just{just: 4},\n # %Algae.Maybe.Just{just: 40}\n # ]\n\n traverse([1, 2, 3], fn x ->\n if is_even(x) do\n %Algae.Maybe.Just{just: x}\n else\n %Algae.Maybe.Nothing{}\n end\n end)\n #=> %Algae.Maybe.Nothing{}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:3","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__protocol__","module_id":"Witchcraft.Traversable.Proto","id":"__protocol__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/traversable.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for","module_id":"Witchcraft.Traversable.Proto","id":"impl_for/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/traversable.ex:3","signature":[["data",[],null]],"object_type":"FunctionObject","name":"impl_for!","module_id":"Witchcraft.Traversable.Proto","id":"impl_for!/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/traversable.ex:45","signature":[["data",[],null],["link",[],null]],"object_type":"FunctionObject","name":"traverse","module_id":"Witchcraft.Traversable.Proto","id":"traverse/2","doc":"Convert elements to actions, and then evaluate the actions from left-to-right,\nand accumulate the results.\n\nFor a version without accumulation, see `then_traverse/2`.\n\n## Examples\n\n iex> traverse([1, 2, 3], fn x -> {x, x * 2, x * 10} end)\n {6, 12, [10, 20, 30]}\n\n iex> traverse({1, 2, 3}, fn x -> [x] end)\n [{1, 2, 3}]\n\n iex> traverse({1, 2, 3}, fn x -> [x, x * 5, x * 10] end)\n [\n {1, 2, 3},\n {1, 2, 15},\n {1, 2, 30}\n ]\n\n iex> traverse([1, 2, 3], fn x -> [x, x * 5, x * 10] end)\n [\n #\n [1, 2, 3], [1, 2, 15], [1, 2, 30],\n [1, 10, 3], [1, 10, 15], [1, 10, 30],\n [1, 20, 3], [1, 20, 15], [1, 20, 30],\n #\n [5, 2, 3], [5, 2, 15], [5, 2, 30],\n [5, 10, 3], [5, 10, 15], [5, 10, 30],\n [5, 20, 3], [5, 20, 15], [5, 20, 30],\n #\n [10, 2, 3], [10, 2, 15], [10, 2, 30],\n [10, 10, 3], [10, 10, 15], [10, 10, 30],\n [10, 20, 3], [10, 20, 15], [10, 20, 30]\n ]\n\n traverse([1, 2, 3], fn x -> %Algae.Maybe.Just{just: x} end)\n #=> %Algae.Maybe.Just{just: [1, 2, 3]}\n\n traverse(%Algae.Maybe.Just{just: 4}, fn x -> [x, x * 10] end)\n #=> [\n # %Algae.Maybe.Just{just: 4},\n # %Algae.Maybe.Just{just: 40}\n # ]\n\n traverse([1, 2, 3], fn x ->\n if is_even(x) do\n %Algae.Maybe.Just{just: x}\n else\n %Algae.Maybe.Nothing{}\n end\n end)\n #=> %Algae.Maybe.Nothing{}\n\n","arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:295","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Traversable.Proto.List","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/traversable.ex:295","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Traversable.Proto.List","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/traversable.ex:295","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Traversable.Proto.List","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/traversable.ex:299","signature":[["list",[],null],["link",[],null]],"object_type":"FunctionObject","name":"traverse","module_id":"Witchcraft.Traversable.Proto.List","id":"traverse/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/traversable.ex:282","signature":[],"object_type":"FunctionObject","name":"__custom_generator__","module_id":"Witchcraft.Traversable.Proto.Tuple","id":"__custom_generator__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/traversable.ex:282","signature":[],"object_type":"FunctionObject","name":"__force_type_instance__","module_id":"Witchcraft.Traversable.Proto.Tuple","id":"__force_type_instance__/0","doc":false,"arity":0},{"type":"def","source":"lib/witchcraft/traversable.ex:282","signature":[["atom",[],"Elixir"]],"object_type":"FunctionObject","name":"__impl__","module_id":"Witchcraft.Traversable.Proto.Tuple","id":"__impl__/1","doc":false,"arity":1},{"type":"def","source":"lib/witchcraft/traversable.ex:285","signature":[["tuple",[],null],["link",[],null]],"object_type":"FunctionObject","name":"traverse","module_id":"Witchcraft.Traversable.Proto.Tuple","id":"traverse/2","doc":null,"arity":2},{"type":"def","source":"lib/witchcraft/unit.ex:15","signature":[],"object_type":"FunctionObject","name":"new","module_id":"Witchcraft.Unit","id":"new/0","doc":"Helper to summon the singleton `Unit` struct","arity":0}],"language":"elixir","git_repo_url":"https://github.com/expede/witchcraft.git","client_version":"0.5.6","client_name":"inch_ex","branch_name":"invert-algae-dep","args":[]} \ No newline at end of file diff --git a/lib/witchcraft.ex b/lib/witchcraft.ex index d0cc480..5f73c40 100644 --- a/lib/witchcraft.ex +++ b/lib/witchcraft.ex @@ -1,14 +1,46 @@ defmodule Witchcraft do - @moduledoc "Convenient top-level `use`" + @moduledoc """ + Top level module - defmacro __using__(_) do - quote do - use Witchcraft.Monoid + ## Hierarchy + + Semigroupoid Semigroup Setoid Foldable Functor -----------┐ + ↓ ↓ ↓ ↓ ↙ ↓ ↘ | + Category Monoid Ord Traversable Apply Bifunctor | + ↓ ↙ ↘ ↓ + Arrow Applicative Chain Extend + ↘ ↙ ↓ + Monad Comonad + + ## `use Wicthcraft` + + There is a convenient `use` macro to import *all* functions in the library. + + use Witchcraft + + This recursively calls `use` on all children modules. - use Witchcraft.Functor - use Witchcraft.Traversable - use Witchcraft.Applicative - use Witchcraft.Monad + Any options passed to `use` will be passed down to all dependencies. + + use Witchcraft, execpt: [right_fold: 2] + + If you would like to not override the functions and operators from `Kernel`, + you can pass the special option `override_kernel: false`. + + use Witchcraft, override_kernel: false + + This same style of `use` is also available on all submodules, and follow + the dependency chart (above). + """ + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Arrow, unquote(opts) + use Witchcraft.Monoid, unquote(opts) + use Witchcraft.Bifunctor, unquote(opts) + use Witchcraft.Traversable, unquote(opts) + use Witchcraft.Monad, unquote(opts) + use Witchcraft.Comonad, unquote(opts) end end end diff --git a/lib/witchcraft/applicative.ex b/lib/witchcraft/applicative.ex index 7f58bf6..54b9ae3 100644 --- a/lib/witchcraft/applicative.ex +++ b/lib/witchcraft/applicative.ex @@ -1,27 +1,137 @@ import TypeClass defclass Witchcraft.Applicative do + @moduledoc """ + `Applicative` extends `Apply` with the ability to lift value into a + particular data type or "context". + + This fills in the connection between regular function application and `Apply` + + data --------------- function ---------------> result + | | | + of(Container, data) of(Container, function) of(Container, result) + ↓ ↓ ↓ + %Container --- %Container ---> %Container + + ## Type Class + + An instance of `Witchcraft.Applicative` must also implement `Witchcraft.Apply`, + and define `Witchcraft.Applicative.of/2`. + + Functor [map/2] + ↓ + Apply [ap/2] + ↓ + Applicative [of/2] + """ + + alias __MODULE__ extend Witchcraft.Apply - defmacro __using__(_) do + @type t :: any() + + defmacro __using__(opts \\ []) do quote do - import Witchcraft.Apply - import unquote(__MODULE__) + use Witchcraft.Apply, unquote(opts) + import unquote(__MODULE__), unquote(opts) end end where do + @doc """ + Bring a value into the same data type as some sample + + ## Examples + + iex> of([], 42) + [42] + + iex> of([1, 2, 3], 42) + [42] + + iex> of({"a", "b", 155}, 42) + {"", "", 42} + + iex> of(fn -> nil end, 42).(55) + 42 + + iex> of(fn(a, b, c) -> a + b - c end, 42).(55) + 42 + + iex> import Witchcraft.Apply + ...> + ...> [] + ...> |> of(&+/2) + ...> |> curried_ap([1, 2, 3]) + ...> |> ap(of([], 42)) + [43, 44, 45] + + """ + @spec of(Applicative.t(), any()) :: Applicative.t() def of(sample, to_wrap) end + @doc """ + Partially apply `of/2`, generally as a way to bring many values into the same context + + ## Examples + + iex> to_tuple = of({"very example", "much wow"}) + ...> [to_tuple.(42), to_tuple.("hello"), to_tuple.([1, 2, 3])] + [{"", 42}, {"", "hello"}, {"", [1, 2, 3]}] + + """ + @spec of(Applicative.t()) :: (any() -> Applicative.t()) def of(sample), do: fn to_wrap -> of(sample, to_wrap) end + @doc """ + Alias for `of/2`, for cases that this helps legibility or style + + ## Example + + iex> wrap({":|", "^.~"}, 42) + {"", 42} + + iex> [] |> wrap(42) + [42] + + """ + @spec wrap(Applicative.t(), any()) :: Applicative.t() defalias wrap(sample, to_wrap), as: :of + + @doc """ + Alias for `of/2`, for cases that this helps legibility or style + + ## Example + + iex> pure({"ohai", "thar"}, 42) + {"", 42} + + iex> [] |> pure(42) + [42] + + """ + @spec pure(Applicative.t(), any()) :: Applicative.t() defalias pure(sample, to_wrap), as: :of + + @doc """ + Alias for `of/2`, for cases that this helps legibility or style + + ## Example + + iex> unit({":)", ":("}, 42) + {"", 42} + + iex> [] |> unit(42) + [42] + + """ + @spec unit(Applicative.t(), any()) :: Applicative.t() defalias unit(sample, to_wrap), as: :of properties do - use Witchcraft.Apply + import Witchcraft.Functor + import Witchcraft.Apply def identity(data) do a = generate(data) @@ -31,31 +141,50 @@ defclass Witchcraft.Applicative do end def homomorphism(data) do + arg = 42 a = generate(data) f = &inspect/1 - left = Applicative.of(data, f) <<~ Applicative.of(data, a) - right = Applicative.of(data, f.(a)) + left = Applicative.of(a, arg) ~>> Applicative.of(a, f) + right = Applicative.of(a, f.(arg)) equal?(left, right) end def interchange(data) do + arg = 42 as = generate(data) fs = replace(as, &inspect/1) - left = fs <<~ Applicative.of(fs, as) - right = Applicative.of(fs, fn g -> g.(as) end) <<~ fs + left = Applicative.of(as, arg) ~>> fs + right = fs ~>> Applicative.of(as, fn g -> g.(arg) end) equal?(left, right) end end end +definst Witchcraft.Applicative, for: Function do + def of(_, unwrapped), do: &Quark.SKI.k(unwrapped, &1) +end + definst Witchcraft.Applicative, for: List do def of(_, unwrapped), do: [unwrapped] end -# definst Witchcraft.Applicative, for: Functio💯n do -# def wrap(fun) when is_function(fun), do: &Quark.SKI.k/1 -# end +definst Witchcraft.Applicative, for: Tuple do + custom_generator(_) do + import TypeClass.Property.Generator, only: [generate: 1] + {generate(0), generate(0)} + end + + def of(sample, unwrapped) do + size = tuple_size(sample) + + sample + |> elem(0) + |> Witchcraft.Monoid.empty() + |> Tuple.duplicate(size) + |> put_elem(size - 1, unwrapped) + end +end diff --git a/lib/witchcraft/apply.ex b/lib/witchcraft/apply.ex index db8115c..9f34da7 100644 --- a/lib/witchcraft/apply.ex +++ b/lib/witchcraft/apply.ex @@ -1,71 +1,376 @@ import TypeClass defclass Witchcraft.Apply do + @moduledoc """ + An extension of `Witchcraft.Functor`, `Apply` provides a way to map functions + to their arguments when both are wrapped in the same kind of container. + + For a nice, illustrated introduction, + see [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html). + + ## Graphically + + If function application looks like like + + data |> function == result + + and a functor looks like this + + %Container ~> function == %Container + + then an apply looks like + + %Container ~>> %Container == %Container + + which is similar to function application inside containers, plus the ability to + attach special effects to applications. + + data --------------- function ---------------> result + %Container --- %Container ---> %Container + + This lets us do functorial things like + + * continue applying values to a curried function resulting from a `Witchcraft.Functor.lift/2` + * apply multiple functions to multiple arguments (with lists) + * propogate some state (like [`Nothing`](https://hexdocs.pm/algae/Algae.Maybe.Nothing.html#content) + in [`Algae.Maybe`](https://hexdocs.pm/algae/Algae.Maybe.html#content)) + + but now with a much larger number of arguments, reuse partially applied functions, + and run effects with the function container as well as the data container. + + ## Examples + + iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3]) + [2, 3, 4, 10, 20, 30] + + iex> import Witchcraft.Functor + ...> [100, 200] + ...> ~> fn(x, y, z) -> + ...> x * y / z + ...> end <<~ [5, 2] + ...> <<~ [100, 50] + [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0] + # ↓ ↓ + # 100 * 5 / 100 200 * 5 / 50 + + %Algae.Maybe.Just{just: 42} + ~> fn(x, y, z) -> + x * y / z + end <<~ %Algae.Maybe.Nothing{} + <<~ %Algae.Maybe.Just{just: 99} + #=> %Algae.Maybe.Nothing{} + + ## Type Class + + An instance of `Witchcraft.Apply` must also implement `Witchcraft.Functor`, + and define `Witchcraft.Apply.ap/2`. + + Functor [map/2] + ↓ + Apply [ap/2] + """ + extend Witchcraft.Functor - import Witchcraft.Functor + alias __MODULE__ + alias Witchcraft.Functor + + import Witchcraft.Functor, only: [lift: 2] + + use Quark - defmacro __using__(_) do + @type t :: any() + @type fun :: any() + + defmacro __using__(opts \\ []) do quote do - import Witchcraft.Functor - import unquote(__MODULE__) + use Witchcraft.Functor, unquote(opts) + import unquote(__MODULE__), unquote(opts) end end where do - def ap(wrapped_funs, wrapped) - end + @doc """ + Apply argumnets to a function, when both are wrapped in the same data structure + + ## Examples - def reverse_ap(wrapped, wrapped_funs), do: ap(wrapped_funs, wrapped) + iex> ap([fn x -> x + 1 end, fn y -> y * 10 end], [1, 2, 3]) + [2, 3, 4, 10, 20, 30] - defalias wrapped_funs <<~ wrapped, as: :ap - defalias wrapped ~>> wrapped_funs, as: :reverse_ap + iex> [100, 200] + ...> |> Witchcraft.Functor.lift(fn(x, y, z) -> x * y / z end) + ...> |> ap([5, 2]) + ...> |> ap([100, 50]) + [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0] + # ↓ ↓ + # 100 * 5 / 100 200 * 5 / 50 - def lift(a, fun, b), do: fun <~ a <<~ b - def lift(a, fun, b, c), do: fun <~ a <<~ b <<~ c - def lift(a, fun, b, c, d), do: fun <~ a <<~ b <<~ c <<~ d - def lift(a, fun, b, c, d, e), do: fun <~ a <<~ b <<~ c <<~ d <<~ e - def lift(a, fun, b, c, d, e, f), do: fun <~ a <<~ b <<~ c <<~ d <<~ e <<~ f - def lift(a, fun, b, c, d, e, f, g), do: fun <~ a <<~ b <<~ c <<~ d <<~ e <<~ f <<~ g - def lift(a, fun, b, c, d, e, f, g, h), do: fun <~ a <<~ b <<~ c <<~ d <<~ e <<~ f <<~ g <<~ h - def lift(a, fun, b, c, d, e, f, g, h, i), do: fun <~ a <<~ b <<~ c <<~ d <<~ e <<~ f <<~ g <<~ h <<~ i + """ + @spec ap(Apply.fun(), Apply.t()) :: Apply.t() + def ap(wrapped_funs, wrapped_args) + end properties do def composition(data) do alias Witchcraft.Functor - alias Witchcraft.Apply use Quark - as = data |> generate |> Functor.map(&inspect/1) - fs = data |> generate |> Functor.replace(fn x -> x <> x end) - gs = data |> generate |> Functor.replace(fn y -> y <> "foo" end) + as = data |> generate() |> Functor.map(&inspect/1) + fs = data |> generate() |> Functor.replace(fn x -> x <> x end) + gs = data |> generate() |> Functor.replace(fn y -> y <> "foo" end) left = Apply.ap(fs, Apply.ap(gs, as)) - right = fs |> Functor.lift(&compose/2) |> Apply.ap(gs) |> Apply.ap(as) + + right = + fs + |> Functor.lift(&compose/2) + |> Apply.ap(gs) + |> Apply.ap(as) equal?(left, right) end end + + @doc """ + Pipe-ordered application. NOT just a flipped version of `ap/2`. + + This isn't just a flipped `ap` in order to get correct effect sequencing. + + ## Examples + + iex> [1, 2, 3] + ...> |> pipe_ap([fn x -> x + 1 end, fn y -> y * 10 end]) + [2, 10, 3, 20, 4, 30] + + """ + @spec pipe_ap(Apply.t(), Applt.fun()) :: Apply.t() + def pipe_ap(wrapped, wrapped_funs), do: lift(wrapped, wrapped_funs, fn(x, f) -> f.(x) end) + + @doc """ + Operator alias for `ap/2` + + Moves against the pipe direction, but in the order of normal function application + + ## Examples + + iex> [fn x -> x + 1 end, fn y -> y * 10 end] <<~ [1, 2, 3] + [2, 3, 4, 10, 20, 30] + + iex> import Witchcraft.Functor + ...> + ...> [100, 200] + ...> ~> fn(x, y, z) -> x * y / z + ...> end <<~ [5, 2] + ...> <<~ [100, 50] + ...> ~> fn x -> x + 1 end + [6.0, 11.0, 3.0, 5.0, 11.0, 21.0, 5.0, 9.0] + + iex> import Witchcraft.Functor, only: [<~: 2] + ...> fn(a, b, c, d) -> a * b - c + d end <~ [1, 2] <<~ [3, 4] <<~ [5, 6] <<~ [7, 8] + [5, 6, 4, 5, 6, 7, 5, 6, 8, 9, 7, 8, 10, 11, 9, 10] + + """ + defalias wrapped_funs <<~ wrapped, as: :curried_ap + + @doc """ + Operator alias for `pipe_ap/2`, moving in the pipe direction + + ## Examples + + iex> [1, 2, 3] ~>> [fn x -> x + 1 end, fn y -> y * 10 end] + [2, 3, 4, 10, 20, 30] + + iex> import Witchcraft.Functor + ...> + ...> [100, 50] + ...> ~>> ([5, 2] # Note the bracket + ...> ~>> ([100, 200] # on both `Apply` lines + ...> ~> fn(x, y, z) -> x * y / z end)) + [5.0, 10.0, 2.0, 4.0, 10.0, 20.0, 4.0, 8.0] + + """ + defalias wrapped ~>> wrapped_funs, as: :curried_pipe_ap + + @doc """ + Same as `ap/2`, but with all functions curried + + ## Examples + + iex> [&+/2, &*/2] + ...> |> curried_ap([1, 2, 3]) + ...> |> ap([4, 5, 6]) + [5, 6, 7, 6, 7, 8, 7, 8, 9, 4, 5, 6, 8, 10, 12, 12, 15, 18] + + """ + @spec curried_ap(Apply.fun(), Apply.t()) :: Apply.t() + def curried_ap(wrapped_funs, wrapped_args) do + wrapped_funs + |> Functor.map(&curry/1) + |> Apply.ap(wrapped_args) + end + + @doc """ + Same as `pipe_ap/2`, but with all functions curried + + ## Examples + + iex> [1, 2, 3] + ...> |> curried_pipe_ap([fn x -> x + 1 end, fn y -> y * 10 end]) + [2, 3, 4, 10, 20, 30] + + """ + @spec curried_pipe_ap(Apply.t(), Apply.fun()) :: Apply.t() + def curried_pipe_ap(wrapped_args, wrapped_funs) do + wrapped_funs + |> Functor.map(&curry/1) + |> Apply.ap(wrapped_args) + end + + @doc """ + Sequence actions, replacing the first/previous values with the last argument + + This is essentially a sequence of actions forgetting the first argument + + ## Examples + + iex> [1, 2, 3] + ...> |> then([4, 5, 6]) + ...> |> then([7, 8, 9]) + [ + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9 + ] + + iex> {1, 2, 3} |> then({4, 5, 6}) |> then({7, 8, 9}) + {12, 15, 9} + + """ + @spec then(Apply.t(), Apply.t()) :: Apply.t() + def then(wrapped_a, wrapped_b), do: lift(wrapped_a, wrapped_b, &Quark.constant(&2, &1)) + + @doc """ + Sequence actions, replacing the last argument with the first argument's values + + This is essentially a sequence of actions forgetting the second argument + + ## Examples + + iex> [1, 2, 3] + ...> |> following([3, 4, 5]) + ...> |> following([5, 6, 7]) + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3 + ] + + iex> {1, 2, 3} |> following({4, 5, 6}) |> following({7, 8, 9}) + {12, 15, 3} + + """ + @spec following(Apply.t(), Apply.t()) :: Apply.t() + def following(wrapped_a, wrapped_b), do: lift(wrapped_a, wrapped_b, &Quark.constant/2) + + @doc """ + Extends `Functor.lift/2` to apply arguments to a binary function + + ## Examples + + iex> lift([1, 2], [3, 4], &+/2) + [4, 5, 5, 6] + + iex> [1, 2] + ...> |> lift([3, 4], &*/2) + [3, 4, 6, 8] + + """ + @spec lift(Apply.t(), Apply.t(), fun()) :: Apply.t() + def lift(a, b, fun), do: a |> lift(fun) |> ap(b) + + @doc """ + Extends `lift` to apply arguments to a ternary function + + ## Examples + + iex> lift([1, 2], [3, 4], [5, 6], fn(a, b, c) -> a * b - c end) + [-2, -3, -1, -2, 1, 0, 3, 2] + + """ + @spec lift(Apply.t(), Apply.t(), Apply.t(), fun()) :: Apply.t() + def lift(a, b, c, fun) do + a + |> lift(fun) + |> ap(b) + |> ap(c) + end + + @doc """ + Extends `lift` to apply arguments to a quaternary function + + ## Examples + + iex> lift([1, 2], [3, 4], [5, 6], [7, 8], fn(a, b, c, d) -> a * b - c + d end) + [5, 6, 4, 5, 6, 7, 5, 6, 8, 9, 7, 8, 10, 11, 9, 10] + + """ + @spec lift(Apply.t(), Apply.t(), Apply.t(), Apply.t(), fun()) :: Apply.t() + def lift(a, b, c, d, fun), do: a |> lift(b, c, fun) |> ap(d) end -# definst Witchcraft.Apply, for: Function do -# use Quark -# def ap(f, g) when is_function(g), do: fn x -> curry(f).(x).(curry(g).(x)) end -# end +definst Witchcraft.Apply, for: Function do + use Quark + def ap(f, g), do: fn x -> curry(f).(x).(curry(g).(x)) end +end definst Witchcraft.Apply, for: List do - def ap(fun_list, list) when is_list(list) do - Witchcraft.Foldable.right_fold(fun_list, [], fn(fun, acc) -> - acc ++ Witchcraft.Functor.lift(list, fun) + def ap(fun_list, val_list) when is_list(val_list) do + Enum.flat_map(fun_list, fn(fun) -> + Enum.map(val_list, fun) end) end end -# definst Witchcraft.Apply, for: Tuple do -# def ap(fun_tuple, arg_tuple) when is_tuple(arg_tuple) do -# fun_tuple -# |> Tuple.to_list -# |> Witchcraft.Apply.ap(Tuple.to_list(arg_tuple)) -# |> List.to_tuple -# end -# end +# Contents must be semigroups +definst Witchcraft.Apply, for: Tuple do + import TypeClass.Property.Generator, only: [generate: 1] + use Witchcraft.Semigroup + + custom_generator(_) do + {generate(""), generate(1), generate(0), generate(""), generate(""), generate("")} + end + + def ap({a, fun}, {y, z}), do: {a <> y, fun.(z)} + def ap({a, b, fun}, {x, y, z}), do: {a <> x, b <> y, fun.(z)} + def ap({a, b, c, fun}, {w, x, y, z}), do: {a <> w, b <> x, c <> y, fun.(z)} + def ap({a, b, c, d, fun}, {v, w, x, y, z}) do + { + a <> v, + b <> w, + c <> x, + d <> y, + fun.(z) + } + end + + def ap(tuple_a, tuple_b) when tuple_size(tuple_a) == tuple_size(tuple_b) do + last_index = tuple_size(tuple_a) - 1 + + tuple_a + |> Tuple.to_list() + |> Enum.zip(Tuple.to_list(tuple_b)) + |> Enum.with_index() + |> Enum.map(fn + {{fun, arg}, ^last_index} -> fun.(arg) + {{left, right}, _} -> left <> right + end) + |> List.to_tuple() + end +end diff --git a/lib/witchcraft/arrow.ex b/lib/witchcraft/arrow.ex new file mode 100644 index 0000000..965909a --- /dev/null +++ b/lib/witchcraft/arrow.ex @@ -0,0 +1,437 @@ +import TypeClass + +defclass Witchcraft.Arrow do + @moduledoc """ + Arrows abstract the idea of computations, potentially with a context. + + Arrows are in fact an abstraction above monads, and can be used both to + express all other type classes in Witchcraft. They also enable some nice + flow-based reasoning about computation. + + For a nice illustrated explination, + see [Haskell/Understanding arrows](https://en.wikibooks.org/wiki/Haskell/Understanding_arrows) + + Arrows let you think diagrammatically, and is a powerful way of thinking + about flow programming, concurrency, and more. + + ┌---> f --------------------------┐ + | v + input ---> split unsplit ---> result + | ^ + | ┌--- h ---┐ | + | | v | + └---> g ---> split unsplit ---┘ + | ^ + └--- i ---┘ + + ## Type Class + + An instance of `Witchcraft.Arrow` must also implement `Witchcraft.Category`, + and define `Witchcraft.Arrow.arrowize/2`. + + Semigroupoid [compose/2] + ↓ + Category [identity/1] + ↓ + Arrow [arrowize/2] + + """ + + alias __MODULE__ + extend Witchcraft.Category + use Witchcraft.Category + + @type t :: fun() + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Category, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + end + + where do + @doc """ + Lift a function into an arrow, much like how `of/2` does with data. + + Essentially a label for composing functions end-to-end, where instances + may have their own special idea of what composition means. The simplest example + is a regular function. Others are possible, such as Kleisli arrows. + + ## Examples + + iex> use Witchcraft.Arrow + ...> times_ten = arrowize(fn -> nil end, &(&1 * 10)) + ...> 5 |> pipe(times_ten) + 50 + + """ + @spec arrowize(Arrow.t(), fun()) :: Arrow.t() + def arrowize(sample, fun) + end + + properties do + def arrow_identity(sample) do + a = generate(nil) + + left = Arrow.arrowize(sample, &Quark.id/1) + right = &Quark.id/1 + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def arrow_composition(sample) do + use Witchcraft.Category + + a = generate(nil) + + f = fn x -> "#{x}-#{x}" end + g = &inspect/1 + + left = Arrow.arrowize(sample, f) <|> Arrow.arrowize(sample, g) + right = Arrow.arrowize(sample, f <|> g) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def first_commutativity(sample) do + a = {generate(nil), generate(nil)} + f = &inspect/1 + + left = Witchcraft.Arrow.first(Arrow.arrowize(sample, f)) + right = Arrow.arrowize(sample, Witchcraft.Arrow.first(f)) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def first_composition(sample) do + a = {generate(nil), generate(nil)} + + f = Arrow.arrowize(sample, fn x -> "#{x}-#{x}" end) + g = Arrow.arrowize(sample, &inspect/1) + + left = Witchcraft.Arrow.first(f <|> g) + right = Witchcraft.Arrow.first(f) <|> Witchcraft.Arrow.first(g) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def second_arrow_commutativity(sample) do + a = {generate(nil), generate(nil)} + f = &inspect/1 + + left = Witchcraft.Arrow.second(Arrow.arrowize(sample, f)) + right = Arrow.arrowize(sample, Witchcraft.Arrow.second(f)) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def second_composition(sample) do + a = {generate(nil), generate(nil)} + + f = Arrow.arrowize(sample, fn x -> "#{x}-#{x}" end) + g = Arrow.arrowize(sample, &inspect/1) + + left = Witchcraft.Arrow.second(f <|> g) + right = Witchcraft.Arrow.second(f) <|> Witchcraft.Arrow.second(g) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def product_composition(sample) do + a = {generate(nil), generate(nil)} + + f = &inspect/1 + g = fn x -> "#{inspect x}-#{inspect x}" end + + left = + Witchcraft.Arrow.product( + Arrow.arrowize(sample, f), + Arrow.arrowize(sample, g) + ) + + right = Arrow.arrowize(sample, Witchcraft.Arrow.product(f, g)) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def fanout_composition(sample) do + a = generate(nil) + + f = &inspect/1 + g = fn x -> "#{inspect x}-#{inspect x}" end + + left = + Witchcraft.Arrow.fanout( + Arrow.arrowize(sample, f), + Arrow.arrowize(sample, g) + ) + + right = Arrow.arrowize(sample, Witchcraft.Arrow.fanout(f, g)) + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def first_reassociaton(sample) do + a = {{generate(nil), generate(nil)}, {generate(nil), generate(nil)}} + f = fn x -> "#{inspect x}-#{inspect x}" end + + x = Witchcraft.Arrow.first(Arrow.arrowize(sample, f)) + y = Arrow.arrowize(sample, &Witchcraft.Arrow.reassociate/1) + + left = Witchcraft.Arrow.first(x) <~> y + right = y <~> x + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def first_identity(sample) do + a = {generate(nil), generate(nil)} + f = fn x -> "#{inspect x}-#{inspect x}" end + + left = Witchcraft.Arrow.first(f) <~> Arrow.arrowize(sample, fn {x, _} -> x end) + right = Arrow.arrowize(sample, fn {x, _} -> x end) <~> f + + equal?(a |> pipe(left), a |> pipe(right)) + end + + def first_product_commutativity(sample) do + a = {generate(nil), generate(nil)} + + f = &inspect/1 + g = fn x -> "#{inspect x}-#{inspect x}" end + + x = Arrow.arrowize(sample, Witchcraft.Arrow.product(&Quark.id/1, g)) + y = Witchcraft.Arrow.first(f) + + left = x <|> y + right = y <|> x + + equal?(a |> pipe(left), a |> pipe(right)) + end + end + + @doc """ + Take two arguments (as a 2-tuple), and run one function on the left side (first element), + and run a different function on the right side (second element). + + ┌------> f.(a) = x -------┐ + | v + {a, b} {x, y} + | ^ + └------> g.(b) = y -------┘ + + ## Examples + + iex> product(&(&1 - 10), &(&1 <> "!")).({42, "Hi"}) + {32, "Hi!"} + + """ + @spec product(Arrow.t(), Arrow.t()) :: Arrow.t() + def product(arrow_f, arrow_g), do: first(arrow_f) <~> (second(arrow_g)) + + @doc """ + Alias for `product/2`, meant to invoke a spacial metaphor + """ + @spec product(Arrow.t(), Arrow.t()) :: Arrow.t() + defalias beside(a, b), as: :product + + @spec Arrow.t() ^^^ Arrow.t() :: Arrow.t() + defalias a ^^^ b, as: :product + + @spec swap({any(), any()}) :: {any(), any()} + def swap({x, y}), do: {y, x} + + @doc """ + Target the first element of a tuple + + ## Examples + + iex> first(fn x -> x * 50 end).({1, 1}) + {50, 1} + + """ + @spec first(Arrow.t()) :: Arrow.t() + def first(arrow) do + arrowize(arrow, fn({x, y}) -> + { + x |> pipe(arrow), + y |> pipe(id_arrow(arrow)) + } + end) + end + + @doc """ + Target the second element of a tuple + + ## Examples + + iex> second(fn x -> x * 50 end).({1, 1}) + {1, 50} + + """ + @spec second(Arrow.t()) :: Arrow.t() + def second(arrow) do + arrowize(arrow, fn({x, y}) -> + { + x |> pipe(id_arrow(arrow)), + y |> pipe(arrow) + } + end) + end + + @doc """ + The identity function lifted into an arrow of the correct type + + ## Examples + + iex> id_arrow(fn -> nil end).(99) + 99 + + """ + @spec id_arrow(Arrow.t()) :: (any() -> Arrow.t()) + def id_arrow(sample), do: arrowize(sample, &Quark.id/1) + + @doc """ + Duplicate incoming data into both halves of a 2-tuple, and run one function + on the left copy, and a different function on the right copy. + + ┌------> f.(a) = x ------┐ + | v + a ---> split = {a, a} {x, y} + | ^ + └------> g.(a) = y ------┘ + + ## Examples + + iex> Witchcraft.Semigroupoid.pipe(42, fanout(&(&1 - 10), &(inspect(&1) <> "!"))) + {32, "42!"} + + """ + @spec fanout(Arrow.t(), Arrow.t()) :: Arrow.t() + def fanout(arrow_f, arrow_g) do + arrow_f |> arrowize(&split/1) <~> arrow_f ^^^ arrow_g + end + + @doc """ + Operator alias for `fanout/2` + + ## Examples + + iex> fanned = fn x -> x - 10 end &&& fn y -> inspect(y) <> "!" end + ...> fanned.(42) + {32, "42!"} + + iex> fanned = + ...> fn x -> x - 10 end + ...> &&& fn y -> inspect(y) <> "!" end + ...> &&& fn z -> inspect(z) <> "?" end + ...> &&& fn d -> inspect(d) <> inspect(d) end + ...> &&& fn e -> e / 2 end + ...> + ...> fanned.(42) + {{{{32, "42!"}, "42?"}, "4242"}, 21.0} + + """ + @spec Arrow.t() &&& Arrow.t() :: Arrow.t() + defalias a &&& b, as: :fanout + + @doc """ + Copy a single value into both positions of a 2-tuple. + + This is useful is you want to run functions on the input separately. + + ## Examples + + iex> split(42) + {42, 42} + + iex> import Witchcraft.Semigroupoid, only: [<~>: 2] + ...> 5 + ...> |> split() + ...> |> (second(fn x -> x - 2 end) + ...> <~> first(fn y -> y * 10 end) + ...> <~> second(&inspect/1)).() + {50, "3"} + + iex> use Witchcraft.Arrow + ...> 5 + ...> |> split() + ...> |> pipe(second(fn x -> x - 2 end)) + ...> |> pipe(first(fn y -> y * 10 end)) + ...> |> pipe(second(&inspect/1)) + {50, "3"} + + """ + @spec split(any()) :: {any(), any()} + def split(x), do: {x, x} + + @doc """ + Merge two tuple values with a combining function. + + ## Examples + + iex> unsplit({1, 2}, &+/2) + 3 + + """ + @spec unsplit({any(), any()}, ((any(), any()) -> any())) :: any() + def unsplit({x, y}, combine), do: combine.(x, y) + + @doc """ + Switch te associativity of a nested tuple. Helpful since many arrows act + on a subset of a tuple, and you may want to move portions in and oit of that stream. + + ## Examples + + iex> reassociate({1, {2, 3}}) + {{1, 2}, 3} + + iex> reassociate({{1, 2}, 3}) + {1, {2, 3}} + + """ + @spec reassociate({any(), {any(), any()}} | {{any(), any()}, any()}) + :: {{any(), any()}, any()} | {any(), {any(), any()}} + def reassociate({{a, b}, c}), do: {a, {b, c}} + def reassociate({a, {b, c}}), do: {{a, b}, c} + + @doc """ + Compose a function (left) with an arrow (right) to produce a new arrow. + + ## Examples + + iex> f = precompose( + ...> fn x -> x + 1 end, + ...> arrowize(fn _ -> nil end, fn y -> y * 10 end) + ...> ) + ...> f.(42) + 430 + + """ + @spec precompose(fun(), Arrow.t()) :: Arrow.t() + def precompose(fun, arrow), do: arrowize(arrow, fun) <~> arrow + + @doc """ + Compose an arrow (left) with a function (right) to produce a new arrow. + + ## Examples + + iex> f = postcompose( + ...> arrowize(fn _ -> nil end, fn x -> x + 1 end), + ...> fn y -> y * 10 end + ...> ) + ...> f.(42) + 430 + + """ + @spec precompose(Arrow.t(), fun()) :: Arrow.t() + def postcompose(arrow, fun), do: arrow <~> arrowize(arrow, fun) +end + +definst Witchcraft.Arrow, for: Function do + use Quark + + def arrowize(_, fun), do: curry(fun) + def first(arrow), do: fn({target, unchanged}) -> {arrow.(target), unchanged} end +end diff --git a/lib/witchcraft/bifunctor.ex b/lib/witchcraft/bifunctor.ex index fec9680..f7cd1a8 100644 --- a/lib/witchcraft/bifunctor.ex +++ b/lib/witchcraft/bifunctor.ex @@ -1,14 +1,67 @@ import TypeClass defclass Witchcraft.Bifunctor do + @moduledoc """ + Similar to `Witchcraft.Functor`, but able to map two functions over two + separate portions of some data structure (some product type). + + Especially helpful when you need different hebaviours on different fields. + + ## Type Class + + An instance of `Witchcraft.Bifunctor` must also implement `Witchcraft.Functor`, + and define `Witchcraft.Apply.ap/2`. + + Functor [map/2] + ↓ + Bifunctor [bimap/2] + + """ + extend Witchcraft.Functor - where do - def bimap(functor, f, g) + alias __MODULE__ + use Quark + + @type t :: any() + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Functor, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end end - def map_first(functor, f), do: Bifunctor.bimap(functor, f, &Quark.id/1) - def map_second(functor, g), do: Bifunctor.bimap(functor, &Quark.id/1, g) + where do + @doc """ + `map` separate fuctions over two fields in a product type. + + The order of fields doesn't always matter in the map. + The first/second function application is determined by the instance. + It also does not have to map all fields in a product type. + + ## Diagram + + ┌------------------------------------┐ + ↓ | + %Combo{a: 50, b: :ok, c: "hello"} |> bimap(&(&1 * 100), &String.upcase/1) + ↑ | + └---------------------------------┘ + #=> %Combo{a: 500, b: :ok, c: "HELLO"} + + ## Examples + + iex> {1, "a"} |> bimap(&(&1 * 100), &(&1 <> "!")) + {100, "a!"} + + iex> {:msg, 42, "number is below 50"} + ...> |> bimap(&(%{subject: &1}), &String.upcase/1) + {:msg, %{subject: 42}, "NUMBER IS BELOW 50"} + + """ + @spec bimap(Bifunctor.t(), (any() -> any()), (any() -> any())) :: Bifunctor.t() + def bimap(data, f, g) + end properties do def identity(data) do @@ -33,20 +86,108 @@ defclass Witchcraft.Bifunctor do equal?(left, right) end end + + @doc """ + The same as `bimap/3`, but with the functions curried + + ## Examples + + iex> {:ok, 2, "hi"} + ...> |> bilift(&*/2, &<>/2) + ...> |> bimap(fn f -> f.(9) end, fn g -> g.("?!") end) + {:ok, 18, "hi?!"} + + """ + @spec bilift(Bifunctor.t(), fun(), fun()) :: Bifunctor.t() + def bilift(data, f, g), do: bimap(data, curry(f), curry(g)) + + @doc """ + `map` a function over the first value only + + ## Examples + + iex> {:ok, 2, "hi"} |> map_first(&(&1 * 100)) + {:ok, 200, "hi"} + + """ + @spec map_first(Bifunctor.t(), (any() -> any())) :: Bifunctor.t() + def map_first(data, f), do: Bifunctor.bimap(data, f, &Quark.id/1) + + @doc """ + The same as `map_first`, but with a curried function + + ## Examples + + iex> {:ok, 2, "hi"} + ...> |> lift_first(&*/2) + ...> |> map_first(fn f -> f.(9) end) + {:ok, 18, "hi"} + + """ + @spec lift_first(Bifunctor.t(), fun()) :: Bifunctor.t() + def lift_first(data, f), do: map_first(data, curry(f)) + + @doc """ + `map` a function over the second value only + + ## Examples + + iex> {:ok, 2, "hi"} |> map_second(&(&1 <> "!?")) + {:ok, 2, "hi!?"} + + """ + @spec map_second(Bifunctor.t(), (any() -> any())) :: Bifunctor.t() + def map_second(data, g), do: Bifunctor.bimap(data, &Quark.id/1, g) + + @doc """ + The same as `map_second`, but with a curried function + + ## Examples + + iex> {:ok, 2, "hi"} + ...> |> lift_second(&<>/2) + ...> |> map_second(fn f -> f.("?!") end) + {:ok, 2, "hi?!"} + + """ + @spec lift_second(Bifunctor.t(), fun()) :: Bifunctor.t() + def lift_second(data, g), do: map_second(data, curry(g)) end -# definst Witchcraft.Bifunctor, for: Tuple do -# def bimap({ a, b}, f, g), do: { f.(a), g.(b)} -# def bimap({x, a, b}, f, g), do: {x, f.(a), g.(b)} -# def bimap({x, y, a, b}, f, g), do: {x, y, f.(a), g.(b)} -# def bimap({x, y, z, a, b}, f, g), do: {x, y, z, f.(a), g.(b)} - -# def bimap(tuple, _, _) do -# raise %Protocol.UndefinedError{ -# value: tuple, -# description: "Witchcraft.Bifunctor not defined for #{inspect tuple}" -# } -# end -# end - -# instance Bifunctor Either where +definst Witchcraft.Bifunctor, for: Tuple do + # credo:disable-for-lines:6 Credo.Check.Refactor.PipeChainStart + custom_generator(_) do + fn -> TypeClass.Property.Generator.generate(nil) end + |> Stream.repeatedly() + |> Enum.take(Enum.random(2..12)) + |> List.to_tuple() + end + + def bimap(tuple, f, g) do + case tuple do + {a, b} -> + {f.(a), g.(b)} + + {x, a, b} -> + {x, f.(a), g.(b)} + + {x, y, a, b} -> + {x, y, f.(a), g.(b)} + + {x, y, z, a, b} -> + {x, y, z, f.(a), g.(b)} + + big_tuple when tuple_size(big_tuple) > 5 -> + index_a = tuple_size(big_tuple) - 2 + + mapped_a = + big_tuple + |> elem(index_a) + |> f.() + + big_tuple + |> Witchcraft.Functor.map(g) + |> put_elem(index_a, mapped_a) + end + end +end diff --git a/lib/witchcraft/category.ex b/lib/witchcraft/category.ex index 8119ee5..2ee32c0 100644 --- a/lib/witchcraft/category.ex +++ b/lib/witchcraft/category.ex @@ -1,42 +1,72 @@ import TypeClass defclass Witchcraft.Category do - @moduledoc ~S""" - In the context of Elixir: abstract over enforcing data types between morphisms + @moduledoc """ + A category is some collection of objects and relationships (morphisms) between them. + + This idea is captured by the idea of an identity function for objects, + and the ability to compose relationships between objects. In most cases, + these are very straightforward and composition and identity are the standard + functions from the `Quark` package or similar. + + ## Type Class + + An instance of `Witchcraft.Category` must also implement `Witchcraft.Semigroupoid`, + and define `Witchcraft.Category.identity/1`. + + Semigroupoid [compose/2] + ↓ + Category [identity/1] """ + + alias __MODULE__ + alias Witchcraft.Semigroupoid + + extend Witchcraft.Semigroupoid + + @type t :: any() + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Semigroupoid, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + end + where do - def identity(morphism) - def compose(morphism_a, morphism_b) + @doc """ + Take some value and return it again + + ## Examples + + iex> classic_id = identity(fn -> nil end) + ...> classic_id.(42) + 42 + + """ + @spec identity(Category.t()) :: Category.t() + def identity(category) end - def reverse_compose(b, a), do: compose(a, b) + defalias id(category), as: :identity properties do - # Same as monoid, but adjusted for categories - def left_identity(data) do a = generate(data) - ident = Category.compose(Category.identity(a), a) + ident = Semigroupoid.compose(Category.identity(a), a) equal?(a, ident) end def right_identity(data) do a = generate(data) - ident = Category.compose(a, Category.identity(a)) + ident = Semigroupoid.compose(a, Category.identity(a)) equal?(a, ident) end - - def associative(data) do - a = generate(data) - b = generate(data) - c = generate(data) - - left = a |> Category.compose(b) |> Category.compose(c) - right = Category.compose(a, Category.compose(b, c)) - - equal?(left, right) - end end end + +definst Witchcraft.Category, for: Function do + def identity(_), do: &Quark.id/1 +end diff --git a/lib/witchcraft/chain.ex b/lib/witchcraft/chain.ex new file mode 100644 index 0000000..40ea04a --- /dev/null +++ b/lib/witchcraft/chain.ex @@ -0,0 +1,473 @@ +import TypeClass + +defclass Witchcraft.Chain do + @moduledoc """ + Chain function applications on contained data that may have some additional effect + + As a diagram: + + %Container --- (data -> %Container) ---> %Container + + ## Examples + + iex> chain([1, 2, 3], fn x -> [x, x] end) + [1, 1, 2, 2, 3, 3] + + alias Algae.Maybe.{Nothing, Just} + + %Just{just: 42} >>> fn x -> %Just{just: x + 1} end + #=> %Just{just: 43} + + %Just{just: 42} + >>> fn x -> if x > 50, do: %Just{just: x + 1}, else: %Nothing{} end + >>> fn y -> y * 100 end + #=> %Nothing{} + + ## Type Class + + An instance of `Witchcraft.Chain` must also implement `Witchcraft.Apply`, + and define `Witchcraft.Chain.chain/2`. + + Functor [map/2] + ↓ + Apply [ap/2] + ↓ + Chain [chain/2] + """ + + alias __MODULE__ + extend Witchcraft.Apply + + @type t :: any() + @type link :: (any() -> Chain.t()) + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Apply, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + end + + where do + @doc """ + Sequentially compose actions, piping values through successive function chains. + + The applied linking function must be unary and return data in the same + type of container as the input. The chain function essentially "unwraps" + a contained value, applies a linking function that returns + the initial (wrapped) type, and collects them into a flat(ter) structure. + + `chain/2` is sometimes called "flat map", since it can also + be expressed as `data |> map(link_fun) |> flatten()`. + + As a diagram: + + %Container --- (data -> %Container) ---> %Container + + ## Examples + + iex> chain([1, 2, 3], fn x -> [x, x] end) + [1, 1, 2, 2, 3, 3] + + iex> [1, 2, 3] + ...> |> chain(fn x -> [x, x] end) + ...> |> chain(fn y -> [y, 2 * y, 3 * y] end) + [1, 2, 3, 1, 2, 3, 2, 4, 6, 2, 4, 6, 3, 6, 9, 3, 6, 9] + + iex> chain([1, 2, 3], fn x -> + ...> chain([x + 1], fn y -> + ...> chain([y + 2, y + 10], fn z -> + ...> [x, y, z] + ...> end) + ...> end) + ...> end) + [1, 2, 4, 1, 2, 12, 2, 3, 5, 2, 3, 13, 3, 4, 6, 3, 4, 14] + + """ + @spec chain(Chain.t(), Chain.link()) :: Chain.t() + def chain(chainable, link_fun) + end + + @doc """ + `chain/2` but with the arguments flipped + + ## Examples + + iex> reverse_chain(fn x -> [x, x] end, [1, 2, 3]) + [1, 1, 2, 2, 3, 3] + + """ + @spec reverse_chain(Chain.link(), Chain.t()) :: Chain.t() + def reverse_chain(chain_fun, chainable), do: chain(chainable, chain_fun) + + @doc """ + An alias for `chain/2`. + + Provided as a convenience for those coming from other languages. + """ + @spec bind(Chain.t(), Chain.link()) :: Chain.t() + defalias bind(chainable, binder), as: :chain + + @doc """ + An alias for `reverse_chain/2`. + + Provided as a convenience for those coming from other languages. + """ + @spec reverse_bind(Chain.t(), Chain.link()) :: Chain.t() + defalias reverse_bind(chainable, binder), as: :reverse_chain + + @doc """ + Operator alias for `chain/2` + + Extends the `~>` / `~>>` heirarchy with one more level of power / abstraction + + ## Examples + + iex> to_monad = fn x -> (fn _ -> x end) end + ...> bound = to_monad.(&(&1 * 10)) >>> to_monad.(&(&1 + 10)) + ...> bound.(10) + 20 + + In Haskell, this is the famous `>>=` operator, but Elixir doesn't allow that + infix operator. + + """ + @spec Chain.t() >>> Chain.link() :: Chain.t() + defalias chainable >>> chain_fun, as: :chain + + @doc """ + Operator alias for `reverse_chain/2` + + Extends the `<~` / `<<~` heirarchy with one more level of power / abstraction + + ## Examples + + iex> to_monad = fn x -> (fn _ -> x end) end + ...> bound = to_monad.(&(&1 + 10)) <<< to_monad.(&(&1 * 10)) + ...> bound.(10) + 20 + + In Haskell, this is the famous `=<<` operator, but Elixir doesn't allow that + infix operator. + + """ + @spec Chain.t() <<< Chain.link() :: Chain.t() + defalias chain_fun <<< chainable, as: :reverse_chain + + @doc """ + Join together one nested level of a data structure that contains itself + + ## Examples + + iex> join([[1, 2, 3]]) + [1, 2, 3] + + iex> join([[1, 2, 3], [4, 5, 6]]) + [1, 2, 3, 4, 5, 6] + + iex> join([[[1, 2, 3], [4, 5, 6]]]) + [[1, 2, 3], [4, 5, 6]] + + alias Algae.Maybe.{Nothing, Just} + %Just{ + just: %Just{ + just: 42 + } + } |> join() + #=> %Just{just: 42} + + join %Just{just: %Nothing{}} + #=> %Nothing{} + + join %Just{just: %Just{just: %Nothing{}}} + #=> %Just{just: %Nothing{}} + + %Nothing{} |> join() |> join() |> join() # ...and so on, forever + #=> %Nothing{} + + Joining tuples is a bit counterintuitive, as it requires a very specific format: + + iex> join { # Outer 2-tuple + ...> {1, 2}, # Inner 2-tuple + ...> { + ...> {3, 4}, # Doubly inner 2-tuple + ...> {5, 6, 7} + ...> } + ...> } + {{4, 6}, {5, 6, 7}} + + iex> join { + ...> {"a", "b"}, + ...> { + ...> {"!", "?"}, + ...> {:ok, 123} + ...> } + ...> } + {{"a!", "b?"}, {:ok, 123}} + + """ + @spec join(Chain.t()) :: Chain.t() + def join(nested), do: nested >>> &Quark.id/1 + + @spec flatten(Chain.t()) :: Chain.t() + defalias flatten(nested), as: :join + + @doc """ + Compose link functions to create a new link function. + + Note that this runs the same direction as `<|>` ("the math way"). + + This is `pipe_compose_link/2` with arguments flipped. + + ## Examples + + iex> links = + ...> fn x -> [x, x] end + ...> |> compose_link(fn y -> [y * 10] end) + ...> |> compose_link(fn z -> [z + 42] end) + ...> + ...> [1, 2, 3] >>> links + [430, 430, 440, 440, 450, 450] + + """ + @spec compose_link(Chain.link(), Chain.link()) :: Chain.link() + def compose_link(action_g, action_f), do: pipe_compose_link(action_f, action_g) + + @doc """ + Compose link functions to create a new link function. + + This is `compose_link/2` with arguments flipped. + + ## Examples + + iex> links = + ...> fn x -> [x, x] end + ...> |> pipe_compose_link(fn y -> [y * 10] end) + ...> |> pipe_compose_link(fn z -> [z + 42] end) + ...> + ...> [1, 2, 3] >>> links + [52, 52, 62, 62, 72, 72] + + """ + @spec pipe_compose_link(Chain.link(), Chain.link()) :: Chain.link() + def pipe_compose_link(action_f, action_g) do + fn data -> action_f.(data) >>> action_g end + end + + @doc ~S""" + `do` notation sugar + + Sequences chainable actions. Note that each line must be of the same type. + + For a version with `return`, please see `Witchcraft.Monad.do/2` + + ## Examples + + iex> chain do + ...> [1] + ...> end + [1] + + iex> chain do + ...> [1, 2, 3] + ...> [4, 5, 6] + ...> [7, 8, 9] + ...> end + [ + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9 + ] + + iex> chain do + ...> a <- [1, 2, 3] + ...> b <- [4, 5, 6] + ...> [a * b] + ...> end + [ + 4, 8, 12, + 5, 10, 15, + 6, 12, 18 + ] + + Normal functions are fine within the `do` as well, as long as each line + ends up being the same chainable type + + iex> import Witchcraft.{Functor, Applicative} + ...> chain do + ...> map([1, 2, 3], fn x -> x + 1 end) + ...> of([], 42) + ...> [7, 8, 9] ~> fn x -> x * 10 end + ...> end + [ + 70, 80, 90, + 70, 80, 90, + 70, 80, 90 + ] + + Or with a custom type + + alias Algae.Maybe.{Nothing, Just} + + chain do + %Just{just: 4} + %Just{just: 5} + %Just{just: 6} + end + #=> %Just{just: 6} + + chain do + %Just{just: 4} + %Nothing{} + %Just{just: 6} + end + #=> %Nothing{} + + ## `let` bindings + + `let`s allow you to hold static values inside a do-block, much like normal assignment + + iex> chain do + ...> let a = 4 + ...> [a] + ...> end + [4] + + This is somewhat limited, though, as values drawn from a chianable structure + with `<-` are not in scope when desugared. For example, this is not possible + due to the recursive binding on `x`: + + chain do + x <- [1, 2, 3] + y <- [4, 5, 6] + let will_fail = x + 1 + [y * will_fail] + end + + ## Desugaring + + ### Sequencing + + The most basic form + + chain do + [1, 2, 3] + [4, 5, 6] + [7, 8, 9] + end + + is equivalent to + + [1, 2, 3] + |> then([4, 5, 6]) + |> then([7, 8, 9]) + + ### `<-` ("drawn from") + + Drawing values from within a chainable structure is similar feels similar + to assignmet, but it is pulling each value separately in a chain link function. + + For instance + + chain do + a <- [1, 2, 3] + b <- [4, 5, 6] + [a * b] + end + + desugars to this + + [1, 2, 3] >>> fn a -> + [4, 5, 6] >>> fn b -> + [a + b] + end + end + + but is often much cleaner to read in do-notation, as it cleans up all of the + nested functions (especially when the chain is very long). + + """ + # credo:disable-for-lines:31 Credo.Check.Refactor.Nesting + defmacro chain(do: input) do + input + |> Chain.AST.normalize() + |> Enum.reverse() + |> Witchcraft.Foldable.right_fold(fn + ({:<-, _, [{left_sym, left_ctx, _}, right]}, acc) -> + left = {left_sym, left_ctx, nil} + + case acc do + {:fn, _, _} -> + quote do + Witchcraft.Chain.chain(unquote(right), fn unquote(left) -> + unquote(acc).(unquote(left)) + end) + end + + acc -> + quote do + Witchcraft.Chain.chain(unquote(right), fn unquote(left) -> + unquote(acc) + end) + end + end + + ({:let, _, [{:=, _, [{var_name, var_ctx, _}, value]}]}, acc) -> + left = {var_name, var_ctx, nil} + quote do: fn unquote(left) -> unquote(acc) end.(unquote(value)) + + (ast, acc) -> + quote do: Witchcraft.Apply.then(unquote(ast), unquote(acc)) + end) + end + + properties do + def associativity(data) do + a = generate(data) + f = fn x -> Witchcraft.Applicative.of(a, inspect(x)) end + g = fn y -> Witchcraft.Applicative.of(a, y <> y) end + + left = (a |> Chain.chain(f)) |> Chain.chain(g) + right = a |> Chain.chain(fn x -> x |> f.() |> Chain.chain(g) end) + + equal?(left, right) + end + end +end + +definst Witchcraft.Chain, for: Function do + alias Witchcraft.Chain + use Quark + + @spec chain(Chain.t(), (any() -> any())) :: Chain.t() + def chain(fun, chain_fun), do: fn(r) -> curry(chain_fun).(fun.(r)).(r) end +end + +definst Witchcraft.Chain, for: List do + def chain(list, chain_fun) do + list + |> Witchcraft.Functor.lift(chain_fun) + |> Witchcraft.Semigroup.concat() + end +end + +definst Witchcraft.Chain, for: Tuple do + use Witchcraft.Semigroup + + custom_generator(_) do + import TypeClass.Property.Generator, only: [generate: 1] + seed = fn -> Enum.random([0, 1.1, "", []]) end + {generate(seed.()), generate(seed.())} + end + + def chain({a, b}, chain_fun) do + {c, d} = chain_fun.(b) + {a <> c, d} + end +end diff --git a/lib/witchcraft/chain/ast.ex b/lib/witchcraft/chain/ast.ex new file mode 100644 index 0000000..9d53a33 --- /dev/null +++ b/lib/witchcraft/chain/ast.ex @@ -0,0 +1,8 @@ +defmodule Witchcraft.Chain.AST do + @moduledoc false + + @doc false + def normalize({:__block__, _, inner}), do: inner + def normalize(single) when is_list(single), do: [single] + def normalize(plain), do: List.wrap(plain) +end diff --git a/lib/witchcraft/chainable.ex b/lib/witchcraft/chainable.ex deleted file mode 100644 index 782822d..0000000 --- a/lib/witchcraft/chainable.ex +++ /dev/null @@ -1,55 +0,0 @@ -import TypeClass - -defclass Witchcraft.Chainable do - extend Witchcraft.Apply - - @type t :: any - - defmacro __using__(_) do - quote do - use Witchcraft.Apply - import unquote(__MODULE__) - end - end - - where do - def chain(chainable, chain_fun) - end - - def reverse_chain(chain_fun, chainable), do: chain(chainable, chain_fun) - - defalias bind(chainable, binder), as: :chain - defalias reverse_bind(chainable, binder), as: :reverse_chain - - defalias chainable >>> chain_fun, as: :chain - defalias chain_fun <<< chainable, as: :reverse_chain - - def bind_forget(chainable_a, chainable_b), do: chain(chainable_a, fn _ -> chainable_b end) - # def reverse_bind_(chainable_b, chainable_a), do: bind_(chainable_a, chainable_b) - - def join(nested), do: nested >>> &Quark.id/1 - defalias flatten(nested), as: :join - - properties do - # def associative(data) do - # a = generate(data) - # b = generate(data) - - # f = fn x -> Witchcraft.Applicative.of(data, inspect(x)) end - # g = fn y -> Witchcraft.Applicative.of(data, y <> y) end - - # left = a |> Chainable.chain(f) |> Chainable.chain(g) - # right = a |> Chainable.chain(fn x -> x |> f.() |> Chainable.chain(g) end) - - # equal?(left, right) - # end - end -end - -definst Witchcraft.Chainable, for: List do - def chain(list, chain_fun) do - list - |> Witchcraft.Functor.lift(chain_fun) - |> Witchcraft.Semigroup.concat - end -end diff --git a/lib/witchcraft/comonad.ex b/lib/witchcraft/comonad.ex new file mode 100644 index 0000000..56c9217 --- /dev/null +++ b/lib/witchcraft/comonad.ex @@ -0,0 +1,103 @@ +import TypeClass + +defclass Witchcraft.Comonad do + @moduledoc """ + The dual of monads, `Comonad` brings an unwrapping function to `Extend`able data. + + Note that the unwrapping function (`extract`) *must return a value*, and is not + available on many data structres that have an empty element. For example, + there is no `Comonad` instance for `List` because we cannot pull a value + out of `[]`. + + ## Type Class + + An instance of `Witchcraft.Comonad` must also implement `Witchcraft.Extend`, + and define `Witchcraft.Comonad.extract/1`. + + Functor [map/2] + ↓ + Extend [nest/1] + ↓ + Comonad [extract/1] + """ + + alias __MODULE__ + extend Witchcraft.Extend + use Quark + + @type t :: any() + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Extend, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + end + + where do + @doc """ + Extract a value out of some context / data structure. This is the opposite + of `Witchcraft.Applicative.of/2`. + + ## Examples + + iex> extract({1, 2}) + 2 + + extract(%Id{id: 42}) + #=> 42 + + """ + @spec extract(Comonad.t()) :: any() + def extract(nested) + end + + @doc """ + Alias for `extract/1` + + ## Examples + + iex> unwrap({1, 2}) + 2 + + unwrap(%Id{id: 42}) + #=> 42 + + """ + @spec unwrap(Comonad.t()) :: any() + def unwrap(nested), do: extract(nested) + + properties do + def left_identity(data) do + a = generate(data) + + a + |> Witchcraft.Extend.extend(&Comonad.extract/1) + |> equal?(a) + end + + def right_identity(data) do + a = generate(data) + + f = fn x -> + x + |> Comonad.extract() + |> inspect() + end + + a + |> Witchcraft.Extend.extend(f) + |> Comonad.extract() + |> equal?(f.(a)) + end + end +end + +definst Witchcraft.Comonad, for: Tuple do + custom_generator(_) do + import TypeClass.Property.Generator, only: [generate: 1] + {generate(nil), generate(nil)} + end + + def extract({_x, y}), do: y +end diff --git a/lib/witchcraft/extend.ex b/lib/witchcraft/extend.ex new file mode 100644 index 0000000..7c225d1 --- /dev/null +++ b/lib/witchcraft/extend.ex @@ -0,0 +1,340 @@ +import TypeClass + +defclass Witchcraft.Extend do + @moduledoc """ + `Extend` is essentially "co`Chain`", meaning that it reverses the relationships + in `Chain`. + + Instead of a flattening operation, we have `nest` which wraps the data in + an additional layer of itsef. + + Instead of a `chain`ing function that acts on raw data and wraps it, + we have `extend` which unwraps data, may modify it, and returns the unwrapped value + + ## Type Class + + An instance of `Witchcraft.Extend` must also implement `Witchcraft.Functor`, + and define `Witchcraft.Extend.nest/1`. + + Functor [map/2] + ↓ + Extend [nest/1] + + """ + + alias __MODULE__ + alias Witchcraft.Functor + + extend Witchcraft.Functor + + use Quark + + @type t :: any() + @type colink :: (Extend.t() -> any()) + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Functor, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + end + + where do + @doc """ + Wrap some nestable data structure in another layer of itself + + ## Examples + + iex> nest([1, 2, 3]) + [[1, 2, 3], [2, 3], [3]] + + """ + @spec nest(Extend.t()) :: Extend.t() + def nest(data) + end + + properties do + def extend_composition(data) do + if is_function(data) do + use Witchcraft.Semigroup + + a = &inspect/1 + + monoid = Enum.random([1, [], ""]) + + arg1 = generate(monoid) + arg2 = generate(monoid) + arg3 = generate(monoid) + + f = fn x -> x <|> fn a -> a <> a end end + g = fn y -> y <|> fn b -> b <> b <> b end end + + left = + a + |> Witchcraft.Extend.extend(g) + |> Witchcraft.Extend.extend(f) + + right = + Witchcraft.Extend.curried_extend(a, fn x -> + x + |> Witchcraft.Extend.curried_extend(g) + |> f.() + end) + + equal?(left.(arg1).(arg2).(arg3), right.(arg1).(arg2).(arg3)) + else + a = generate(data) + + f = fn x -> "#{inspect x}-#{inspect x}" end + g = fn y -> "#{inspect y} / #{inspect y} / #{inspect y}" end + + left = + a + |> Witchcraft.Extend.curried_extend(g) + |> Witchcraft.Extend.curried_extend(f) + + right = + Witchcraft.Extend.curried_extend(a, fn x -> + x + |> Witchcraft.Extend.curried_extend(g) + |> f.() + end) + + equal?(left, right) + end + end + + def naturality(data) do + a = generate(data) + + if is_function(data) do + fun = &inspect/1 + + monoid = Enum.random([1, [], ""]) + + arg1 = generate(monoid) + arg2 = generate(monoid) + arg3 = generate(monoid) + + left = + fun + |> Extend.nest() + |> Functor.lift(&Extend.nest/1) + + right = + fun + |> Extend.nest() + |> Extend.nest() + + equal?(left.(arg1).(arg2).(arg3), right.(arg1).(arg2).(arg3)) + else + a + |> Extend.nest() + |> Functor.lift(&Extend.nest/1) + |> equal?(a |> Extend.nest() |> Extend.nest()) + end + end + + def extend_as_nest(data) do + if is_function(data) do + fun = &inspect/1 + + monoid = Enum.random([1, [], ""]) + + arg1 = generate(monoid) + arg2 = generate(monoid) + + left = Witchcraft.Extend.extend(fun, &Quark.id/1) + right = Witchcraft.Extend.nest(fun) + + equal?(left.(arg1).(arg2), right.(arg1).(arg2)) + true + else + a = generate(data) + + a + |> Witchcraft.Extend.extend(&Quark.id/1) + |> equal?(Witchcraft.Extend.nest(a)) + end + end + + def nest_as_extend(data) do + if is_function(data) do + f = fn x -> x <> x end + g = &inspect/1 + + monoid = Enum.random([1, [], ""]) + + arg1 = generate(monoid) + arg2 = generate(monoid) + + left = + g + |> Extend.nest() + |> Functor.lift(&Functor.lift(&1, f)) + + right = Extend.nest(Functor.lift(g, f)) + + equal?(left.(arg1).(arg2), right.(arg1).(arg2)) + else + a = generate(data) + f = &inspect/1 + + a + |> Extend.nest() + |> Functor.lift(&Functor.lift(&1, f)) + |> equal?(Extend.nest(Functor.lift(a, f))) + end + end + end + + @doc """ + Similar to `Witchcraft.Chain.chain/2`, except that it reverses the input and output + types of the colinking function. + + ## Examples + + Chain: + + iex> Witchcraft.Chain.chain([1, 2, 3], fn x -> [x * 10] end) + [10, 20, 30] + + Extend: + + iex> extend([1, 2, 3], fn list -> List.first(list) * 10 end) + [10, 20, 30] + + """ + @spec extend(Extend.t(), Extend.colink()) :: Extend.t() + def extend(data, colink) do + data + |> nest() + |> Functor.map(colink) + end + + @doc """ + `extend/2` with arguments flipped. + + Makes piping composed colinks easier (see `compose_colink/2` and `pipe_compose_colink/2`). + + ## Examples + + iex> fn list -> List.first(list) * 10 end + ...> |> reverse_extend([1, 2, 3]) + [10, 20, 30] + + """ + @spec reverse_extend(Extend.colink(), Extend.t()) :: Extend.t() + def reverse_extend(colink, data), do: Extend.extend(data, colink) + + @doc """ + The same as `extend/2`, but with the colinking function curried. + + ## Examples + + iex> [1, 2, 3] + ...> |> curried_extend(fn(list, coeff) -> List.first(list) * coeff end) + ...> |> extend(fn(funs) -> List.first(funs).(10) end) + [10, 20, 30] + + """ + @spec curried_extend(Extend.t(), fun()) :: Extend.t() + def curried_extend(data, colink), do: Extend.extend(data, curry(colink)) + + @doc """ + The same as `extend/2`, but with the colinking function curried. + + ## Examples + + iex> fn(list) -> List.first(list) * 10 end + ...> |> reverse_curried_extend([1, 2, 3]) + [10, 20, 30] + + """ + @spec reverse_curried_extend(Extend.t(), fun()) :: Extend.t() + def reverse_curried_extend(colink, data), do: curried_extend(data, colink) + + @doc """ + + ## Examples + + iex> composed = + ...> fn xs -> List.first(xs) * 10 end + ...> |> compose_colink(fn ys -> List.first(ys) - 10 end) + ...> + ...> extend([1, 2, 3], composed) + [-90, -80, -70] + + iex> fn xs -> List.first(xs) * 10 end + ...> |> compose_colink(fn ys -> List.first(ys) - 10 end) + ...> |> compose_colink(fn zs -> List.first(zs) * 50 end) + ...> |> reverse_extend([1, 2, 3]) + [400, 900, 1400] + + iex> fn xs -> List.first(xs) * 10 end + ...> |> compose_colink(fn ys -> List.first(ys) - 10 end) + ...> |> compose_colink(fn zs -> List.first(zs) * 50 end) + ...> |> compose_colink(fn zs -> List.first(zs) + 12 end) + ...> |> reverse_extend([1, 2, 3]) + [6400, 6900, 7400] + + """ + @spec compose_colink(Extend.colink(), Extend.colink()) :: (Extend.t() -> any()) + def compose_colink(g, f), do: fn x -> x |> curried_extend(f) |> g.() end + + @doc """ + + ## Examples + + iex> fn xs -> List.first(xs) * 10 end + ...> |> pipe_compose_colink(fn ys -> List.first(ys) - 2 end) + ...> |> reverse_extend([1, 2, 3]) + [8, 18, 28] + + iex> composed = + ...> fn xs -> List.first(xs) * 10 end + ...> |> pipe_compose_colink(fn ys -> List.first(ys) - 2 end) + ...> |> pipe_compose_colink(fn zs -> List.first(zs) * 5 end) + ...> + ...> extend([1, 2, 3], composed) + [40, 90, 140] + + iex> fn xs -> List.first(xs) * 10 end + ...> |> pipe_compose_colink(fn ys -> List.first(ys) - 2 end) + ...> |> pipe_compose_colink(fn zs -> List.first(zs) * 5 end) + ...> |> pipe_compose_colink(fn zs -> List.first(zs) + 1 end) + ...> |> reverse_extend([1, 2, 3]) + [41, 91, 141] + + """ + @spec pipe_compose_colink(Extend.colink(), Extend.colink()) :: (Extend.t() -> any()) + def pipe_compose_colink(f, g), do: compose_colink(g, f) +end + +definst Witchcraft.Extend, for: Function do + def nest(fun) do + use Quark + + fn left -> + fn right -> + left + |> Witchcraft.Semigroup.append(right) + |> curry(fun).() + end + end + end +end + +definst Witchcraft.Extend, for: List do + def nest([]), do: [] + def nest(entire = [_head | tail]), do: [entire | nest(tail)] # Could be improved +end + +definst Witchcraft.Extend, for: Tuple do + custom_generator(_) do + import TypeClass.Property.Generator, only: [generate: 1] + {generate(nil), generate(nil)} + end + + def nest({x, y}), do: {x, {x, y}} +end diff --git a/lib/witchcraft/extendable.ex b/lib/witchcraft/extendable.ex deleted file mode 100644 index a154149..0000000 --- a/lib/witchcraft/extendable.ex +++ /dev/null @@ -1,35 +0,0 @@ -import TypeClass - -defclass Witchcraft.Extendable do - where do - def nest(extendable) - end - - properties do - - end -end - -definst Witchcraft.Extendable, for: List do - def nest(list) do - list - |> Enum.reverse - |> Enum.scan([], fn(x, acc) -> [x | acc] end) - |> Enum.reverse - # Obviously make a cleaner version ^^^ - end -end - -# instance Extend Maybe where -# duplicated Nothing = Nothing -# duplicated j = Just j - -# instance Extend (Either a) where -# duplicated (Left a) = Left a -# duplicated r = Right r - -# instance Extend ((,)e) where -# duplicated p = (fst p, p) - -# instance Semigroup m => Extend ((->)m) where -# duplicated f m = f . (<>) m diff --git a/lib/witchcraft/foldable.ex b/lib/witchcraft/foldable.ex index 58eb376..3ba7272 100644 --- a/lib/witchcraft/foldable.ex +++ b/lib/witchcraft/foldable.ex @@ -1,46 +1,61 @@ import TypeClass defclass Witchcraft.Foldable do - @moduledoc ~S""" + @moduledoc """ Data that can be folded over to change its structure by altering or combining elements ## Examples - iex> sum = &right_fold(&1, 0, &+/2) - ...> sum.([1, 2, 3]) + iex> right_fold([1, 2, 3], 0, &+/2) # sum 6 - ...> sum.([4, 5, 6]) - 15 ## Properties People are working on Foldable properties. This is one of the exceptions to - there needing to conform to properties. - The best closest property so far is abstract and strongly typed, so not - easily expressible in Elixir. - Ex. https://mail.haskell.org/pipermail/libraries/2015-February/024943.html + there needing to conform to properties. In the meantime, we are testing that + naturality is preserved, which is be a free theorm. + + If that fails, something is very wrong with the instance. + + ## Type Class + + An instance of `Witchcraft.Foldable` define `Witchcraft.Foldable.right_fold/3`. + Foldable [right_fold/3] """ alias __MODULE__ - - alias Witchcraft.Orderable - alias Witchcraft.Orderable.Order - alias Witchcraft.Semigroup + alias Witchcraft.{Ord, Monad, Monoid, Semigroup, Unit} import Kernel, except: [length: 1, max: 2, min: 2] + import Exceptional.Safe, only: [safe: 1] require Foldable.EmptyError - use Exceptional + use Witchcraft.Monad use Quark - @type t :: any + @type t :: any() + + defmacro __using__(opts \\ []) do + {:ok, new_opts} = + Keyword.get_and_update(opts, :except, fn except -> + {:ok, [length: 1, max: 2, min: 2] ++ (except || [])} + end) - defmacro __using__(_) do - quote do - import Kernel, except: [length: 1, max: 2, min: 2] - import unquote(__MODULE__) + if Access.get(opts, :override_kernel, true) do + quote do + import Kernel, unquote(new_opts) + import Witchcraft.Semigroup, unquote(opts) + import Witchcraft.Ord, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + else + quote do + import Witchcraft.Semigroup, unquote(opts) + import Witchcraft.Ord, unquote(opts) + import unquote(__MODULE__), unquote(new_opts) + end end end @@ -50,121 +65,351 @@ defclass Witchcraft.Foldable do it to a single summary value. The right-association makes it possible to cease computation on infinite streams of data. - The reducer must be a binary function, with the second argument being the + The folder must be a binary function, with the second argument being the accumulated value thus far. ## Examples - iex> sum = &right_fold(&1, 0, &+/2) end + iex> sum = fn xs -> right_fold(xs, 0, &+/2) end ...> sum.([1, 2, 3]) 6 ...> sum.([4, 5, 6]) 15 """ - @spec right_fold(Foldable.t, any, ((any, any) -> any)) :: any - def right_fold(foldable, seed, reducer) + @spec right_fold(Foldable.t(), any(), ((any(), any()) -> any())) :: any() + def right_fold(foldable, seed, folder) + end + + properties do + # Free theorm + def naturality(data) do + foldable = generate(data) + seed = "seed" + + f = &Quark.constant/2 + g = &Quark.id/1 + + left = + foldable + |> Foldable.right_fold(seed, f) + |> g.() + + right = + foldable + |> g.() + |> Witchcraft.Foldable.right_fold(fn(x, acc) -> f.((g.(acc)), x) end) + + equal?(left, right) + end end @doc ~S""" The same as `right_fold/3`, but uses the first element as the seed + + ## Examples + + iex> right_fold([1, 2, 3], &+/2) + 6 + + iex> right_fold([100, 2, 5], &//2) + 40.0 # (2 / (5 / 100)) + + iex> right_fold([[], 1, 2, 3], fn(x, acc) -> [x | acc] end) + [1, 2, 3] + """ - @spec right_fold(Foldable.t, fun) :: any - def right_fold(foldable, reducer) do + @spec right_fold(Foldable.t(), fun()) :: any() + def right_fold(foldable, folder) do case to_list(foldable) do [] -> [] - [a | as] -> right_fold(as, a, reducer) + [a | as] -> right_fold(as, a, folder) end end - @spec fold_map(Foldable.t, fun) :: any - def fold_map(foldable, fun) do - import Witchcraft.Monoid - foldable |> right_fold(empty(foldable), fn(x, acc) -> fun.(x) <> acc end) - end + @doc ~S""" + Left-associative fold over a structure to alter the structure and/or reduce + it to a single summary value. - @spec fold(Foldable.t) :: any - def fold(foldable), do: fold_map(foldable, &Quark.id/1) + The folder must be a binary function, with the second argument being the + accumulated value thus far. - # Left-associative fold of a structure. + ## Examples - # In the case of lists, left_fold, when applied to a binary operator, a starting value (typically the left-identity of the operator), and a list, reduces the list using the binary operator, from left to right: + iex> sum = fn xs -> right_fold(xs, 0, &+/2) end + ...> sum.([1, 2, 3]) + 6 + ...> sum.([4, 5, 6]) + 15 - # left_fold f z [x1, x2, ..., xn] == (...((z `f` x1) `f` x2) `f`...) `f` xn - # Note that to produce the outermost application of the operator the entire input list must be traversed. This means that left_fold' will diverge if given an infinite list. + iex> left_fold([1, 2, 3], [], fn(x, acc) -> [x | acc] end) + [[[[] | 1] | 2] | 3] - # Also note that if you want an efficient left-fold, you probably want to use left_fold' instead of left_fold. The reason for this is that latter does not force the "inner" results (e.g. z f x1 in the above example) before applying them to the operator (e.g. to (f x2)). This results in a thunk chain O(n) elements long, which then must be evaluated from the outside-in. + """ + def left_fold(foldable, seed, folder) do + right_fold(foldable, &Quark.id/1, fn(b, g) -> + fn(x) -> + x + |> folder.(b) + |> g.() + end + end).(seed) + end - # For a general Foldable structure this should be semantically identical to, + @doc ~S""" + The same as `left_fold/3`, but uses the first element as the seed - # left_fold f z = left_fold f z . toList - # def left_fold(foldable, seed, reducer) do - # right_fold(foldable, &Quark.id/1, fn(seed_focus, acc) -> - # fn focus -> seed_focus.(reducer.(focus, acc)) end - # end).(seed) - # end - # left_fold f a bs = right_fold (\b g x -> g (f x b)) id bs a - def left_fold(foldable, seed, reducer) do - right_fold(foldable, &Quark.id/1, fn(x, g) -> fn (b) -> g.(reducer.(x, b)) end end).(seed) - end + ## Examples + + iex> left_fold([1, 2, 3], &+/2) + 6 + + iex> left_fold([100, 2, 5], &//2) + 10.0 # ((100 / 2) / 5) + + iex> left_fold([1 | [2 | [3]]], fn(x, acc) -> [x | acc] end) + [[1 | 2] | 3] - # left_fold1 :: (a -> a -> a) -> t a -> a Source # - # A variant of left_fold that has no base case, and thus may only be applied to non-empty structures. - # left_fold1 f = left_fold1 f . toList - def left_fold(foldable, reducer) do + """ + def left_fold(foldable, folder) do [x | xs] = to_list(foldable) - left_fold(xs, x, reducer) + left_fold(xs, x, folder) end - # toList :: t a -> [a] Source # - # List of elements of a structure, from left to right. - @spec to_list(Foldable.t) :: [any] + @doc """ + Combine all elements using monoidal append + + ## Examples + + iex> fold([1, 2, 3]) + 6 + + iex> fold([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + + """ + @spec fold(Foldable.t()) :: any() + def fold(foldable), do: left_fold(foldable, &Semigroup.append/2) + + @doc """ + Map a functional over all elements and `fold` them together + + ## Examples + + iex> fold_map([1, 2, 3], fn x -> [x, x * 10] end) + [1, 10, 2, 20, 3, 30] + + iex> fold_map([[1, 2, 3], [4, 5, 6], [7, 8, 9]], fn x -> [x, x] end) + [ + [1, 2, 3], [1, 2, 3], + [4, 5, 6], [4, 5, 6], + [7, 8, 9], [7, 8, 9] + ] + + """ + @spec fold_map(Foldable.t(), fun()) :: any() + def fold_map(foldable, fun) do + right_fold(foldable, Monoid.empty(foldable), fn(element, acc) -> + element + |> fun.() + |> Semigroup.append(acc) + end) + end + + @doc """ + Turn any `Foldable` into a `List` + + ## Example + + iex> to_list({1, 2, 3}) + [1, 2, 3] + + iex> to_list(%{a: 1, b: 2, c: 3}) + [c: 3, b: 2, a: 1] + + """ + @spec to_list(Foldable.t()) :: [any()] def to_list(foldable), do: right_fold(foldable, [], fn(x, acc) -> [x | acc] end) - @spec empty?(Foldable.t) :: boolean + @doc """ + Check if a foldable data structure is empty + + ## Examples + + iex> empty?("") + true + + iex> empty?("hi") + false + + iex> empty?(%{}) + true + + """ + @spec empty?(Foldable.t()) :: boolean def empty?(foldable), do: right_fold(foldable, true, fn(_focus, _acc) -> false end) - # Returns the size/length of a finite structure as an Int. The default implementation is optimized for structures that are similar to cons-lists, because there is no general way to do better. - @spec length(Foldable.t) :: non_neg_integer + @doc """ + Count the number of elements in a foldable structure + + ## Examples + + iex> use Witchcraft.Foldable + ...> length(%{}) + 0 + iex> length(%{a: 1, b: 2}) + 2 + iex> length("ࠀabc") + 4 + + """ + @spec length(Foldable.t()) :: non_neg_integer() def length(list) when is_list(list), do: Kernel.length(list) def length(foldable), do: right_fold(foldable, 0, fn(_, acc) -> 1 + acc end) defalias count(foldable), as: :length defalias size(foldable), as: :length - def elem(foldable, target) do + @doc """ + Check if a foldable structure contains a particular element + + ## Examples + + iex> member?([1, 2, 3], 2) + true + + iex> member?([1, 2, 3], 99) + false + + iex> member?(%{a: 1, b: 2}, 2) + false + + iex> member?(%{a: 1, b: 2}, {:b, 2}) + true + + """ + @spec member?(Foldable.t(), any()) :: boolean() + def member?(foldable, target) do right_fold(foldable, false, fn(focus, acc) -> acc or (focus == target) end) end - @spec max(Foldable.t, by: ((any, any) -> Order.t)) :: Maybe.t - def! max(foldable, by: comparator) do - right_fold(foldable, fn(focus, acc) -> + @doc """ + Find the maximum element in a foldable structure using a custom comparitor + + Elements must implement `Witchcraft.Ord`. + + Comes in both a safe and unsafe(`!`) version + + ## Examples + + iex> use Witchcraft.Foldable + ...> [1, 2, 7] + ...> |> max(by: fn(x, y) -> + ...> x + ...> |> Integer.mod(3) + ...> |> Witchcraft.Ord.compare(Integer.mod(y, 3)) + ...> end) + 2 + + """ + @spec max(Foldable.t(), by: ((any, any) -> Order.ordering())) :: Ord.t() + def max(foldable, by: comparator) do + Witchcraft.Foldable.right_fold(foldable, fn(focus, acc) -> case comparator.(focus, acc) do - %Order.Greater{} -> focus + :greater -> focus _ -> acc end end) end - @spec max(Foldable.t) :: any - def! max(foldable_comparable), do: max(foldable_comparable, by: &Orderable.compare/2) + @doc """ + Find the maximum element in a foldable structure using the default ordering + from `Witchcraft.Ord`. + + Elements must implement `Witchcraft.Ord`. + + ## Examples + + iex> use Witchcraft.Foldable + ...> max([2, 3, 1]) + 3 + ...> max([[4], [1, 2, 3, 4]]) + [4] + + %BinaryTree{ + node: 1, + left: %BinaryTree{ + node: 3 + left: 4 + }, + right: 2 + } + |> max() + #=> 4 - # The largest element of a non-empty structure. + """ + @spec max(Foldable.t()) :: Ord.t() + def max(foldable_comparable), do: max(foldable_comparable, by: &Ord.compare/2) + + @doc """ + Find the maximum element in a foldable structure using a custom comparitor + + Elements must implement `Witchcraft.Ord`. + + Comes in both a safe and unsafe(`!`) version + + ## Examples - @spec min(Foldable.t, by: ((any, any) -> Order.t)) :: any | Maybe.t - def! min(foldable, by: comparitor) do + iex> use Witchcraft.Foldable + ...> [8, 2, 1] + ...> |> min(by: fn(x, y) -> + ...> x + ...> |> Integer.mod(4) + ...> |> Witchcraft.Ord.compare(Integer.mod(y, 4)) + ...> end) + 8 + + """ + @spec min(Foldable.t(), by: ((any(), any()) -> Order.t())) :: any() | Maybe.t() + def min(foldable, by: comparitor) do right_fold(foldable, fn(focus, acc) -> case comparitor.(focus, acc) do - %Order.Greater{} -> focus + :lesser -> focus _ -> acc end end) end - def! min(foldable), do: min(foldable, by: &Orderable.compare/2) + @doc """ + Find the minimum element in a foldable structure using the default ordering + from `Witchcraft.Ord`. + + Elements must implement `Witchcraft.Ord`. + + ## Examples - @spec random(Foldable.t) :: any | Foldable.EmptyError.t - def! random(foldable) do + iex> use Witchcraft.Foldable + ...> min([2, 3, 1]) + 1 + ...> min([[4], [1, 2, 3, 4]]) + [1, 2, 3, 4] + + %BinaryTree{ + node: 4, + left: %BinaryTree{ + node: 3 + left: 1 + }, + right: 2 + } + |> min() + #=> 1 + + """ + def min(foldable), do: min(foldable, by: &Ord.compare/2) + + @spec random(Foldable.t()) :: any() | Foldable.EmptyError.t() + def random(foldable) do foldable |> to_list |> safe(&Enum.random/1).() @@ -179,20 +424,20 @@ defclass Witchcraft.Foldable do ## Examples - iex> sum [1, 2, 3] + iex> sum([1, 2, 3]) 6 - iex> %BinaryTree{ - ...> left: 4, - ...> right: %BinaryTree{ - ...> left: 2, - ...> right: 10 - ...> } - ...> } |> sum - 16 + %BinaryTree{ + left: 4, + right: %BinaryTree{ + left: 2, + right: 10 + } + } |> sum() + #=> 16 """ - @spec sum(Foldable.t) :: number + @spec sum(Foldable.t()) :: number() def sum(foldable), do: right_fold(foldable, 0, &+/2) @doc ~S""" @@ -200,95 +445,112 @@ defclass Witchcraft.Foldable do ## Examples - iex> product [1, 2, 3] + iex> product([1, 2, 3]) 6 - iex> %BinaryTree{ - ...> left: 4, - ...> right: %BinaryTree{ - ...> left: 2, - ...> right: 10 - ...> } - ...> } |> product - 80 + %BinaryTree{ + left: 4, + right: %BinaryTree{ + left: 2, + right: 10 + } + } + |> product() + #=> 80 """ - @spec product(Foldable.t) :: number - def product(foldable), do: right_fold(foldable, 0, &*/2) + @spec product(Foldable.t()) :: number() + def product(foldable), do: right_fold(foldable, &*/2) @doc ~S""" Concatenate all lists in a foldable structure ## Examples - iex> [[1, 2, 3], [4, 5, 6]] - [1, 2, 3, 4, 5, 6] + iex> concat([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + [1, 2, 3, 4, 5, 6, 7, 8, 9] - iex> %BinaryTree{ - ...> left: [1, 2, 3], - ...> right: %BinaryTree{ - ...> left: [4, 5], - ...> right: [6] - ...> } - ...> } - [1, 2, 3, 4, 5, 6] + %BinaryTree{ + left: [1, 2, 3], + right: %BinaryTree{ + left: [4, 5], + right: [6] + } + } + |> concat() + #=> [1, 2, 3, 4, 5, 6] """ - @spec concat(Foldable.t) :: [any] + @spec concat(Foldable.t()) :: [any()] def concat(contained_lists) do - contained_lists - |> right_fold([], Quark.flip(&Semigroup.append/2)) - |> Semigroup.concat + right_fold(contained_lists, [], &Semigroup.append/2) end - # Map a function over all the elements of a container and concatenate the resulting lists. @doc ~S""" Lift a function over a foldable structure generating lists of results, and then concatenate the resulting lists ## Examples - iex> [1, 2, 3, 4, 5, 6] - ...> |> concat_map(fn x -> [x, x] end) + iex> concat_map([1, 2, 3, 4, 5, 6], fn x -> [x, x] end) [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6] - iex> %BinaryTree{ - ...> left: 1, - ...> right: %BinaryTree{ - ...> left: 2, - ...> right: 3 - ...> } - ...> } - ...> |> concat_map(fn x -> [x, x] end) - [1, 1, 2, 2, 3, 3] + %BinaryTree{ + left: 1, + right: %BinaryTree{ + left: 2, + right: 3 + } + } + |> concat_map(fn x -> [x, x] end) + #=> [1, 1, 2, 2, 3, 3] """ - @spec concat_map(Foldable.t, (any -> [any])) :: [any] - def concat_map(foldable, a_to_list_b) do + @spec concat_map(Foldable.t(), (any() -> [any()])) :: [any()] + def concat_map(foldable, mapper) do foldable - |> right_fold(fn(inner_focus, acc) -> a_to_list_b.(inner_focus) <> acc end) - |> concat + |> right_fold([], fn(inner_focus, acc) -> + [mapper.(inner_focus) | acc] + end) + |> concat() end + @doc """ + Test whether the structure is empty. The default implementation is + optimized for structures that are similar to lists, because there + is no general way to do better. + + ## Examples + + iex> null?([]) + true + + iex> null?([1, 2, 3]) + false + + """ + @spec null?(Foldable.t()) :: boolean() + def null?(foldable), do: right_fold(foldable, true, fn(_, _) -> false end) + @doc ~S""" Check if a foldable is full of only `true`s ## Examples - iex> all? [true, true, false] + iex> all?([true, true, false]) false - iex> %BinaryTree{ - ...> left: true, - ...> right: %BinaryTree{ - ...> left: true, - ...> right: false - ...> } - ...> } |> all? - false + %BinaryTree{ + left: true, + right: %BinaryTree{ + left: true, + right: false + } + } |> all?() + #=> false """ - @spec all?(Foldable.t) :: boolean + @spec all?(Foldable.t()) :: boolean() def all?(foldable_bools), do: right_fold(foldable_bools, true, &and/2) @doc ~S""" @@ -296,21 +558,22 @@ defclass Witchcraft.Foldable do ## Examples - iex> all?([1, 2, 3], &Integer.is_odd?/1) + iex> import Integer + ...> all?([1, 2, 3], &is_odd/1) false - iex> %BinaryTree{ - ...> left: 1, - ...> right: %BinaryTree{ - ...> left: 2, - ...> right: 3 - ...> } - ...> } - ...> |> all?(&Integer.is_odd?/1) - false + %BinaryTree{ + left: 1, + right: %BinaryTree{ + left: 2, + right: 3 + } + } + |> all?(&Integer.is_odd?/1) + #=> false """ - @spec all?(Foldable.t, (any -> boolean)) :: boolean + @spec all?(Foldable.t(), (any() -> boolean())) :: boolean() def all?(foldable, predicate) do right_fold(foldable, true, fn(focus, acc) -> predicate.(focus) and acc end) end @@ -323,17 +586,17 @@ defclass Witchcraft.Foldable do iex> any? [true, true, false] true - iex> %BinaryTree{ - ...> left: true, - ...> right: %BinaryTree{ - ...> left: true, - ...> right: false - ...> } - ...> } |> any? - true + %BinaryTree{ + left: true, + right: %BinaryTree{ + left: true, + right: false + } + } |> any?() + #=> true """ - @spec any?(Foldable.t) :: boolean + @spec any?(Foldable.t()) :: boolean() def any?(foldable_bools), do: right_fold(foldable_bools, false, &or/2) @doc ~S""" @@ -341,94 +604,49 @@ defclass Witchcraft.Foldable do ## Examples - iex> any?([1, 2, 3], &Integer.is_odd?/1) + iex> require Integer + ...> any?([1, 2, 3], &Integer.is_odd/1) true - iex> %BinaryTree{ - ...> left: 1, - ...> right: %BinaryTree{ - ...> left: 2, - ...> right: 3 - ...> } - ...> } - ...> |> any(&Integer.is_odd?/1) - true + %BinaryTree{ + left: 1, + right: %BinaryTree{ + left: 2, + right: 3 + } + } + |> any(&Integer.is_odd?/1) + #=> true """ - @spec any?(Foldable.t, (any -> boolean)) :: boolean + @spec any?(Foldable.t(), (any() -> boolean())) :: boolean() def any?(foldable, predicate) do right_fold(foldable, false, fn(focus, acc) -> predicate.(focus) or acc end) end - properties do - end -end - -definst Witchcraft.Foldable, for: Tuple do - def right_fold(tuple, seed, reducer), do: tuple |> Tuple.to_list |> right_fold(seed, reducer) -end - -definst Witchcraft.Foldable, for: List do - def right_fold(list, seed, reducer), do: List.foldr(list, seed, reducer) -end - -definst Witchcraft.Foldable, for: Map do - def right_fold(map, seed, reducer), do: Enum.reduce(map, seed, reducer) -end - - - - - - # WHEN WE HAVE MONAD - # right_foldM :: (Foldable t, Monad m) => (a -> b -> m b) -> b -> t a -> m b Source # + @doc """ + Run each action from left to right, discarding all values. - # Monadic fold over the elements of a structure, associating to the right, i.e. from right to left. + Always returns `%Witchcraft.Unit{}` in the same foldbale structure that you started with. - # left_foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b Source # - - # Monadic fold over the elements of a structure, associating to the left, i.e. from left to right. - -# mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m () Source # - -# Map each element of a structure to a monadic action, evaluate these actions from left to right, and ignore the results. For a version that doesn't ignore the results see mapM. - -# As of base 4.8.0.0, mapM_ is just traverse_, specialized to Monad. - -# forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m () Source # - -# forM_ is mapM_ with its arguments flipped. For a version that doesn't ignore the results see forM. - -# As of base 4.8.0.0, forM_ is just for_, specialized to Monad. - -# sequence_ :: (Foldable t, Monad m) => t (m a) -> m () Source # - -# Evaluate each monadic action in the structure from left to right, and ignore the results. For a version that doesn't ignore the results see sequence. - -# As of base 4.8.0.0, sequence_ is just sequenceA_, specialized to Monad. - -# msum :: (Foldable t, MonadPlus m) => t (m a) -> m a Source # - -# The sum of a collection of actions, generalizing concat. As of base 4.8.0.0, msum is just asum, specialized to MonadPlus. -# WHEN APPLICATIVE -# Applicative actions -# traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f () Source # - -# Map each element of a structure to an action, evaluate these actions from left to right, and ignore the results. For a version that doesn't ignore the results see traverse. - -# for_ :: (Foldable t, Applicative f) => t a -> (a -> f b) -> f () Source # - -# for_ is traverse_ with its arguments flipped. For a version that doesn't ignore the results see for. - -# >>> for_ [1..4] print -# 1 -# 2 -# 3 -# 4 -# sequenceA_ :: (Foldable t, Applicative f) => t (f a) -> f () Source # - -# Evaluate each action in the structure from left to right, and ignore the results. For a version that doesn't ignore the results see sequenceA. + ## Examples -# asum :: (Foldable t, Alternative f) => t (f a) -> f a Source # + iex> then_sequence([[1, 2, 3], [4, 5, 6]]) + [ + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{}, + %Witchcraft.Unit{} + ] -# The sum of a collection of actions, generalizing concat. + """ + @spec then_sequence(Foldable.t()) :: Monad.t() + def then_sequence(foldable_monad) do + right_fold(foldable_monad, of(foldable_monad, %Unit{}), &then/2) + end +end diff --git a/lib/witchcraft/foldable/bitstring.ex b/lib/witchcraft/foldable/bitstring.ex new file mode 100644 index 0000000..0f1ae7a --- /dev/null +++ b/lib/witchcraft/foldable/bitstring.ex @@ -0,0 +1,9 @@ +import TypeClass + +definst Witchcraft.Foldable, for: BitString do + def right_fold(string, seed, reducer) do + string + |> String.to_charlist() + |> Witchcraft.Foldable.right_fold(seed, reducer) + end +end diff --git a/lib/witchcraft/foldable/empty_error.ex b/lib/witchcraft/foldable/empty_error.ex index 30a5c54..5ee1da3 100644 --- a/lib/witchcraft/foldable/empty_error.ex +++ b/lib/witchcraft/foldable/empty_error.ex @@ -1,22 +1,40 @@ defmodule Witchcraft.Foldable.EmptyError do + @moduledoc """ + Represent the error state of trying to fold over an empty structure + + ## Examples + + iex> %Witchcraft.Foldable.EmptyError{} + %Witchcraft.Foldable.EmptyError{ + message: "Unable to process empty data", + plug_status: 500 + } + + """ + alias __MODULE__ - @type t :: %EmptyError{message: String.t, data: any, plug_status: pos_integer} + @type t :: %EmptyError{ + message: String.t(), + data: any(), + plug_status: pos_integer() + } + @base_message "Unable to process empty data" defexception message: @base_message, data: nil, plug_status: 500 defmacro new(data) do quote do - module_name = __MODULE__ case __ENV__.function do {fun_name, arity} -> %EmptyError{ - data: unquote(data), - message: "Unable to process empty data in #{module_name}.#{fun_name}/#{arity}" + data: unquote(data), + message: "Unable to process empty data in #{__MODULE__}.#{fun_name}/#{arity}" } - nil -> %EmptyError{data: unquote(data)} + nil -> + %EmptyError{data: unquote(data)} end end end diff --git a/lib/witchcraft/foldable/list.ex b/lib/witchcraft/foldable/list.ex new file mode 100644 index 0000000..545dca1 --- /dev/null +++ b/lib/witchcraft/foldable/list.ex @@ -0,0 +1,5 @@ +import TypeClass + +definst Witchcraft.Foldable, for: List do + def right_fold(list, seed, reducer), do: List.foldr(list, seed, reducer) +end diff --git a/lib/witchcraft/foldable/map.ex b/lib/witchcraft/foldable/map.ex new file mode 100644 index 0000000..597ed6e --- /dev/null +++ b/lib/witchcraft/foldable/map.ex @@ -0,0 +1,5 @@ +import TypeClass + +definst Witchcraft.Foldable, for: Map do + def right_fold(map, seed, reducer), do: Enum.reduce(map, seed, reducer) +end diff --git a/lib/witchcraft/foldable/tuple.ex b/lib/witchcraft/foldable/tuple.ex new file mode 100644 index 0000000..154381d --- /dev/null +++ b/lib/witchcraft/foldable/tuple.ex @@ -0,0 +1,9 @@ +import TypeClass + +definst Witchcraft.Foldable, for: Tuple do + def right_fold(tuple, seed, reducer) do + tuple + |> Tuple.to_list() + |> Witchcraft.Foldable.right_fold(seed, reducer) + end +end diff --git a/lib/witchcraft/functor.ex b/lib/witchcraft/functor.ex index 33ff1bb..7d9a449 100644 --- a/lib/witchcraft/functor.ex +++ b/lib/witchcraft/functor.ex @@ -3,18 +3,30 @@ import TypeClass defclass Witchcraft.Functor do @moduledoc ~S""" Functors are datatypes that allow the application of functions to their interior values. - Always returns data in the same structure (same size, leaves, &c) + Always returns data in the same structure (same size, tree layout, and so on). Please note that bitstrings are not functors, as they fail the functor composition constraint. They change the structure of the underlying data, and thus composed lifting does not equal lifing a composed function. If you need to map over a bitstring, convert it to and from a charlist. + + ## Type Class + + An instance of `Witchcraft.Functor` must define `Witchcraft.Functor.map/2`. + + Functor [map/2] """ alias __MODULE__ use Quark - @type t :: any + @type t :: any() + + defmacro __using__(opts \\ []) do + quote do + import unquote(__MODULE__), unquote(opts) + end + end where do @doc ~S""" @@ -23,10 +35,10 @@ defclass Witchcraft.Functor do ## Examples - iex> [1, 2, 3] |> map(fn x -> x + 1 end) + iex> map([1, 2, 3], fn x -> x + 1 end) [2, 3, 4] - iex> %{a: 1, b: 2} |> fn x -> x * 10 end + iex> %{a: 1, b: 2} ~> fn x -> x * 10 end %{a: 10, b: 20} iex> map(%{a: 2, b: [1, 2, 3]}, fn @@ -39,10 +51,49 @@ defclass Witchcraft.Functor do def map(wrapped, fun) end + properties do + def identity(data) do + wrapped = generate(data) + + wrapped + |> Functor.map(&id/1) + |> equal?(wrapped) + end + + def composition(data) do + wrapped = generate(data) + + f = fn x -> inspect(wrapped == x) end + g = fn x -> inspect(wrapped != x) end + + left = Functor.map(wrapped, fn x -> x |> g.() |> f.() end) + right = wrapped |> Functor.map(g) |> Functor.map(f) + + equal?(left, right) + end + end + @doc ~S""" `map/2` but with the function automatically curried + + ## Examples + + iex> lift([1, 2, 3], fn x -> x + 1 end) + [2, 3, 4] + + iex> [1, 2, 3] + ...> |> lift(fn x -> x + 55 end) + ...> |> lift(fn y -> y * 10 end) + [560, 570, 580] + + iex> [1, 2, 3] + ...> |> lift(fn(x, y) -> x + y end) + ...> |> List.first() + ...> |> apply([9]) + 10 + """ - @spec lift(Functor.t, fun) :: Functor.t + @spec lift(Functor.t(), fun()) :: Functor.t() def lift(wrapped, fun), do: Functor.map(wrapped, curry(fun)) @doc ~S""" @@ -50,22 +101,42 @@ defclass Witchcraft.Functor do ## Example - iex> [1,2,3] - ...> ~> fn x -> x + 5 end + iex> [1, 2, 3] + ...> ~> fn x -> x + 55 end ...> ~> fn y -> y * 10 end - [60, 70, 80] + [560, 570, 580] + + iex> [1, 2, 3] + ...> ~> fn(x, y) -> x + y end + ...> |> List.first() + ...> |> apply([9]) + 10 """ defalias data ~> fun, as: :lift @doc ~S""" - `<~/2` with arguments flipped - - ## Examples + `<~/2` with arguments flipped. iex> (fn x -> x + 5 end) <~ [1,2,3] [6, 7, 8] + Note that the mnemonic is flipped from `|>`, and combinging directions can + be confusing. It's generally recommended to use `~>`, or to keep `<~` on + the same line both of it's arguments: + + iex> fn(x, y) -> x + y end <~ [1, 2, 3] + ...> |> List.first() + ...> |> apply([9]) + 10 + + ...or in an expression that's only pointing left: + + iex> fn y -> y * 10 end + ...> <~ fn x -> x + 55 end + ...> <~ [1, 2, 3] + [560, 570, 580] + """ def fun <~ data, do: data ~> fun @@ -74,59 +145,73 @@ defclass Witchcraft.Functor do ## Examples - iex> [1, 2, 3] |> replace("hi") + iex> replace([1, 2, 3], "hi") ["hi", "hi", "hi"] """ - @spec replace(Functor.t, any) :: Functor.t + @spec replace(Functor.t(), any()) :: Functor.t() def replace(wrapped, replace_with), do: wrapped ~> &constant(replace_with, &1) +end - properties do - def identity(data) do - wrapped = generate(data) - - wrapped - |> Functor.map(&id/1) - |> equal?(wrapped) - end +definst Witchcraft.Functor, for: Function do + use Quark - def composition(data) do - wrapped = generate(data) + @doc """ + Compose functions - f = fn x -> inspect(wrapped == x) end - g = fn x -> inspect(wrapped != x) end + ## Example - left = wrapped |> Functor.map(fn x -> x |> g.() |> f.() end) - right = wrapped |> Functor.map(g) |> Functor.map(f) + iex> ex = Witchcraft.Functor.lift(fn x -> x * 10 end, fn x -> x + 2 end) + ...> ex.(2) + 22 - equal?(left, right) - end - end + """ + def map(f, g), do: compose(g, f) end -# definst Witchcraft.Functor, for: Function do -# use Quark -# def map(f, g), do: compose(g, f) -# end - definst Witchcraft.Functor, for: List do def map(list, fun), do: Enum.map(list, fun) end -# definst Witchcraft.Functor, for: Tuple do -# def map(tuple, fun) do -# tuple -# |> Tuple.to_list -# |> Witchcraft.Functor.map(fun) -# |> List.to_tuple -# end -# end - -# definst Witchcraft.Functor, for: Map do -# def map(hashmap, fun) do -# hashmap -# |> Map.to_list -# |> Witchcraft.Functor.map(fn {key, value} -> {key, fun.(value)} end) -# |> Enum.into(%{}) -# end -# end +definst Witchcraft.Functor, for: Tuple do + def map(tuple, fun) do + case tuple do + {} -> + {} + + {first} -> + {fun.(first)} + + {first, second} -> + {first, fun.(second)} + + {first, second, third} -> + {first, second, fun.(third)} + + {first, second, third, fourth} -> + {first, second, third, fun.(fourth)} + + {first, second, third, fourth, fifth} -> + {first, second, third, fourth, fun.(fifth)} + + big_tuple -> + last_index = tuple_size(big_tuple) - 1 + + mapped = + big_tuple + |> elem(last_index) + |> fun.() + + put_elem(big_tuple, last_index, mapped) + end + end +end + +definst Witchcraft.Functor, for: Map do + def map(hashmap, fun) do + hashmap + |> Map.to_list() + |> Witchcraft.Functor.map(fn {key, value} -> {key, fun.(value)} end) + |> Enum.into(%{}) + end +end diff --git a/lib/witchcraft/monad.ex b/lib/witchcraft/monad.ex index 682407b..8f3bb0f 100644 --- a/lib/witchcraft/monad.ex +++ b/lib/witchcraft/monad.ex @@ -1,68 +1,121 @@ import TypeClass defclass Witchcraft.Monad do + @moduledoc """ + Very similar to `Chain`, `Monad` provides a way to link actions, and a way + to bring plain values into the correct context (`Applicative`). + + This allows us to view actions in a full framework along the lines of + functor and applicative: + + data ---------------- function ----------------------------> result + | | | + of(Container, data) of/2, or similar of(Container, result) + ↓ ↓ ↓ + %Container --- (data -> %Container) ---> %Container + + As you can see, the linking function may just be `of` now that we have that. + + For a nice, illustrated introduction, + see [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html). + + Having `of` also lets us enhance do-notation with a convenuenct `return` function (see `monad/2`) + + ## Type Class + + An instance of `Witchcraft.Monad` must also implement `Witchcraft.Applicative` + and `Wicthcraft.Chainable`. + + Functor [map/2] + ↓ + Apply [ap/2] + ↓ ↓ + [of/2] Applicative Chain [chain/2] + ↓ ↓ + Monad + [_] + """ + extend Witchcraft.Applicative - extend Witchcraft.Chainable + extend Witchcraft.Chain alias Witchcraft.Monad.AST - import Witchcraft.Chainable + import Witchcraft.Chain - defmacro __using__(_) do + defmacro __using__(opts \\ []) do quote do - use Witchcraft.Applicative - use Witchcraft.Chainable - - import unquote(__MODULE__) + use Witchcraft.Applicative, unquote(opts) + use Witchcraft.Chain, unquote(opts) + import unquote(__MODULE__), unquote(opts) end end @doc ~S""" - monad do - a <- [1,2,3] - b <- [4,5,6] - pure([], a + b) - end - #=> [5, 6, 7, 6, 7, 8, 7, 8, 9] - """ - defmacro monad(do: input) do - Witchcraft.Foldable.right_fold(Enum.reverse(AST.normalize(input)), fn - (ast = {:<-, ctx, inner = [old_left = {lt, lc, lb}, right]}, acc) -> - left = {lt, lc, nil} - case acc do - {:fn, _, _} -> - quote do: unquote(right) >>> fn unquote(left) -> unquote(acc).(unquote(left)) end - - acc -> - quote do: unquote(right) >>> fn unquote(left) -> unquote(acc) end - end - - (ast, acc) -> quote do: bind_forget(unquote(ast), unquote(acc)) - end) - end + do-notation enhanced with a `return` operation. + + `return` is the simplest possible linking function, providing the correct `of/2` + instance for your monad. + + ## Examples + + iex> monad [] do + ...> [1, 2, 3] + ...> end + [1, 2, 3] + + iex> monad [] do + ...> [1, 2, 3] + ...> [4, 5, 6] + ...> [7, 8, 9] + ...> end + [ + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9, + 7, 8, 9 + ] + + iex> monad [] do + ...> Witchcraft.Applicative.of([], 1) + ...> end + [1] + + iex> monad [] do + ...> a <- [1,2,3] + ...> b <- [4,5,6] + ...> return(a * b) + ...> end + [ + 4, 8, 12, + 5, 10, 15, + 6, 12, 18 + ] + + iex> monad [] do + ...> a <- return 1 + ...> b <- return 2 + ...> return(a + b) + ...> end + [3] - @doc ~S""" - monad [] do - a <- [1,2,3] - b <- [4,5,6] - return(a + b) - end - #=> [5, 6, 7, 6, 7, 8, 7, 8, 9] """ - defmacro monad(datatype, do: input) do - quote do: monad(do: unquote(AST.preprocess(input, datatype))) + defmacro monad(sample, do: input) do + preprocessed = AST.preprocess(input, sample) + quote do: Witchcraft.Chain.chain(do: unquote(preprocessed)) end - defmacro let(left, do: right), do: quote do: unquote(left) = unquote(right) - properties do import Witchcraft.Applicative - import Witchcraft.Chainable + import Witchcraft.Chain def left_identity(data) do a = generate(data) - b = generate(data) - - f = &Witchcraft.Functor.replace(b, inspect(&1)) + f = &Witchcraft.Functor.replace(a, inspect(&1)) left = a |> of(a) |> chain(f) right = f.(a) @@ -72,10 +125,21 @@ defclass Witchcraft.Monad do def right_identity(data) do a = generate(data) - chain(a, of(a)) |> equal?(a) + left = a >>> &of(a, &1) + + equal?(a, left) end end end -# List |> conforms(to: Witchcraft.Monad) -# Function |> conforms(to: Monad) +definst Witchcraft.Monad, for: Function +definst Witchcraft.Monad, for: List + +definst Witchcraft.Monad, for: Tuple do + use Witchcraft.Semigroup + import TypeClass.Property.Generator, only: [generate: 1] + + custom_generator(_) do + {generate(""), generate("")} + end +end diff --git a/lib/witchcraft/monad/ast.ex b/lib/witchcraft/monad/ast.ex index 524bf6b..b0d8a27 100644 --- a/lib/witchcraft/monad/ast.ex +++ b/lib/witchcraft/monad/ast.ex @@ -1,13 +1,16 @@ defmodule Witchcraft.Monad.AST do - import Witchcraft.Applicative + @moduledoc false + @doc false def preprocess(input, datatype) do - Macro.prewalk(normalize(input), fn - {:return, _ctx, [inner]} -> quote do: pure(unquote(datatype), unquote(inner)) - ast -> ast + # input + # |> Witchcraft.Chain.AST.normalize() + Macro.prewalk(input, fn + {:return, _ctx, [inner]} -> + quote do: Witchcraft.Applicative.pure(unquote(datatype), unquote(inner)) + + ast -> + ast end) end - - def normalize({:__block__, _, inner}), do: inner - def normalize(plain), do: List.wrap(plain) end diff --git a/lib/witchcraft/monoid.ex b/lib/witchcraft/monoid.ex index a69049a..b92222a 100644 --- a/lib/witchcraft/monoid.ex +++ b/lib/witchcraft/monoid.ex @@ -4,38 +4,25 @@ defclass Witchcraft.Monoid do @moduledoc ~S""" Monoid extends the semigroup with the concept of an "empty" or "zero" element. - ## Examples - - iex> empty(10) - 0 + ## Type Class - iex> empty [1, 2, 3, 4, 5] - [] + An instance of `Witchcraft.Monoid` must also implement `Witchcraft.Semigroup`, + and define `Witchcraft.Monoid.empty/1`. + Semigroup [append/2] + ↓ + Monoid [empty/1] """ - alias Witchcraft.Monoid + alias __MODULE__ extend Witchcraft.Semigroup, alias: true - @type t :: any + @type t :: any() - defmacro __using__(alias: true) do + defmacro __using__(opts \\ []) do quote do - use Witchcraft.Monoid, alias: Monoid - end - end - - defmacro __using__(alias: as_alias) do - quote do - alias Witchcraft.Semigroup, as: unquote(as_alias) - alias Witchcraft.Monoid, as: unquote(as_alias) - end - end - - defmacro __using__(_) do - quote do - import Witchcraft.Semigroup - import Witchcraft.Monoid + use Witchcraft.Semigroup, unquote(opts) + import unquote(__MODULE__), unquote(opts) end end @@ -55,8 +42,20 @@ defclass Witchcraft.Monoid do def empty(sample) end - defdelegate zero(sample), to: Proto, as: :empty + defalias zero(sample), as: :empty + + @doc """ + Check if a value is the empty element of that type + + ## Examples + iex> empty?([]) + true + + iex> empty?([1]) + false + + """ @spec empty?(Monoid.t) :: boolean def empty?(monoid), do: empty(monoid) == monoid @@ -65,9 +64,9 @@ defclass Witchcraft.Monoid do a = generate(data) if is_function(a) do - Semigroup.append(Monoid.empty(a), a).("foo") == a.("foo") + equal?(Semigroup.append(Monoid.empty(a), a).("foo"), a.("foo")) else - Semigroup.append(Monoid.empty(a), a) == a + equal?(Semigroup.append(Monoid.empty(a), a), a) end end @@ -83,27 +82,34 @@ defclass Witchcraft.Monoid do end end -# definst Witchcraft.Monoid, for: Any do -# def empty(sample) when is_function(sample), do: &Quark.id/1 -# end +definst Witchcraft.Monoid, for: Function do + def empty(sample) when is_function(sample), do: &Quark.id/1 +end definst Witchcraft.Monoid, for: Integer do def empty(_), do: 0 end -# definst Witchcraft.Monoid, for: Float do -# def empty(_), do: 0.0 -# end - +definst Witchcraft.Monoid, for: Float do + def empty(_), do: 0.0 +end -# definst Witchcraft.Monoid, for: BitString do -# def empty(_), do: "" -# end +definst Witchcraft.Monoid, for: BitString do + def empty(_), do: "" +end definst Witchcraft.Monoid, for: List do def empty(_), do: [] end -# definst Witchcraft.Monoid, for: Map do -# def empty(_), do: %{} -# end +definst Witchcraft.Monoid, for: Map do + def empty(_), do: %{} +end + +definst Witchcraft.Monoid, for: Tuple do + custom_generator(_) do + {} + end + + def empty(_), do: {} +end diff --git a/lib/witchcraft/ord.ex b/lib/witchcraft/ord.ex new file mode 100644 index 0000000..72bc2e2 --- /dev/null +++ b/lib/witchcraft/ord.ex @@ -0,0 +1,195 @@ +import TypeClass + +defclass Witchcraft.Ord do + @moduledoc ~S""" + `Ord` describes how to order elements of a data type + + This is a total order, so all elements are either `:equal`, `:greater`, or `:lesser` + than each other. + + ## Type Class + + An instance of `Witchcraft.Ord` must also implement `Witchcraft.Setoid`, + and define `Witchcraft.Ord.compare/2`. + + Setoid [equivalent?/2] + ↓ + Ord [compare/2] + """ + + extend Witchcraft.Setoid + + @type t :: any() + @type ordering :: :lesser | :equal | :greater + + alias __MODULE__ + import Kernel, except: [<: 2, >: 2, <=: 2, >=: 2] + + defmacro __using__(opts \\ []) do + {:ok, new_opts} = + Keyword.get_and_update(opts, :except, fn except -> + {:ok, [<: 2, >: 2, <=: 2, >=: 2] ++ (except || [])} + end) + + if Access.get(opts, :override_kernel, true) do + quote do + import Kernel, unquote(new_opts) + use Witchcraft.Semigroupoid, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + else + quote do + use Witchcraft.Semigroupoid, unquote(opts) + import unquote(__MODULE__), unquote(new_opts) + end + end + end + + where do + @doc """ + Get the ordering relationship between two elements. + + Possible results are `:lesser`, `:equal`, and `:greater` + + ## Examples + + iex> compare(1, 1) + :equal + + iex> compare([1], [2]) + :lesser + + iex> compare([1, 2], [3]) + :lesser + + iex> compare([3, 2, 1], [1, 2, 3, 4, 5]) + :greater + + """ + @spec compare(Ord.t(), Ord.t()) :: Ord.ordering() + def compare(ord_a, ord_b) + end + + properties do + def reflexivity(data) do + a = generate(data) + comparison = Ord.compare(a, a) + equal?(comparison, :equal) or equal?(comparison, :lesser) + end + + def transitivity(data) do + x = generate(data) + y = generate(data) + z = generate(data) + + x_y = Ord.compare(x, y) + y_z = Ord.compare(y, z) + x_z = Ord.compare(x, z) + + if x_y != :greater and y_z != :greater do + equal?(x_z, :lesser) or equal?(x_z, :equal) + else + true + end + end + + def antisymmetry(data) do + a = generate(data) + b = generate(data) + + a_b = Ord.compare(a, b) + b_a = Ord.compare(b, a) + + if a_b != :greater and b_a != :greater, do: a_b == :equal, else: true + end + end + + @doc """ + Determine if two elements are `:equal` + + ## Examples + + iex> equal?(1, 1.0) + true + + iex> equal?(1, 2) + false + + """ + @spec equal?(Ord.t(), Ord.t()) :: boolean() + def equal?(a, b), do: compare(a, b) == :equal + + @doc """ + Determine if an element is `:greater` than another + + ## Examples + + iex> greater?(1, 1) + false + + iex> greater?(1.1, 1) + true + + """ + @spec greater?(Ord.t(), Ord.t()) :: boolean() + def greater?(a, b), do: compare(a, b) == :greater + + defalias a > b, [as: :greater?] + + @doc """ + Determine if an element is `:lesser` than another + + ## Examples + + iex> lesser?(1, 1) + false + + iex> lesser?(1, 1.1) + true + + """ + @spec lesser?(Ord.t(), Ord.t()) :: boolean() + def lesser?(a, b), do: compare(a, b) == :lesser + + defalias a < b, [as: :lesser?] + + @doc """ + Determine if an element is `:lesser` or `:equal` to another + + ## Examples + + iex> use Witchcraft.Ord + ...> 1 <= 2 + true + ...> [] <= [1, 2, 3] + false + ...> [1] <= [1, 2, 3] + true + ...> [4] <= [1, 2, 3] + false + + """ + # credo:disable-for-next-line Credo.Check.Warning.OperationOnSameValues + @spec Ord.t() <= Ord.t() :: boolean() + def a <= b, do: compare(a, b) != :greater + + @doc """ + Determine if an element is `:greater` or `:equal` to another + + ## Examples + + iex> use Witchcraft.Ord + ...> 2 >= 1 + true + ...> [1, 2, 3] >= [] + true + ...> [1, 2, 3] >= [1] + true + ...> [1, 2, 3] >= [4] + false + + """ + # credo:disable-for-next-line Credo.Check.Warning.OperationOnSameValues + @spec Ord.t() >= Ord.t() :: boolean() + def a >= b, do: compare(a, b) != :lesser +end diff --git a/lib/witchcraft/ord/float.ex b/lib/witchcraft/ord/float.ex new file mode 100644 index 0000000..21c110c --- /dev/null +++ b/lib/witchcraft/ord/float.ex @@ -0,0 +1,7 @@ +import TypeClass + +definst Witchcraft.Ord, for: Float do + def compare(float_a, float_b) when float_a == float_b, do: :equal + def compare(float_a, float_b) when float_a > float_b, do: :greater + def compare(float_a, float_b) when float_a < float_b, do: :lesser +end diff --git a/lib/witchcraft/ord/integer.ex b/lib/witchcraft/ord/integer.ex new file mode 100644 index 0000000..68b10c9 --- /dev/null +++ b/lib/witchcraft/ord/integer.ex @@ -0,0 +1,7 @@ +import TypeClass + +definst Witchcraft.Ord, for: Integer do + def compare(int_a, int_b) when int_a == int_b, do: :equal + def compare(int_a, int_b) when int_a > int_b, do: :greater + def compare(int_a, int_b) when int_a < int_b, do: :lesser +end diff --git a/lib/witchcraft/ord/list.ex b/lib/witchcraft/ord/list.ex new file mode 100644 index 0000000..40ffc7a --- /dev/null +++ b/lib/witchcraft/ord/list.ex @@ -0,0 +1,25 @@ +import TypeClass + +definst Witchcraft.Ord, for: List do + custom_generator(_) do + 1 + |> Stream.unfold(fn acc -> + next = TypeClass.Property.Generator.generate(1) + {acc, next} + end) + |> Stream.drop(1) + |> Stream.take(:rand.uniform(4)) + |> Enum.to_list() + end + + def compare([], []), do: :equal + def compare(_a, []), do: :greater + def compare([], b) when is_list(b), do: :lesser + + def compare([head_a | tail_a], [head_b | tail_b]) do + case Witchcraft.Ord.compare(head_a, head_b) do + :equal -> Witchcraft.Ord.compare(tail_a, tail_b) + order -> order + end + end +end diff --git a/lib/witchcraft/ord/map.ex b/lib/witchcraft/ord/map.ex new file mode 100644 index 0000000..442a258 --- /dev/null +++ b/lib/witchcraft/ord/map.ex @@ -0,0 +1,21 @@ +import TypeClass + +definst Witchcraft.Ord, for: Map do + # credo:disable-for-lines:12 Credo.Check.Refactor.PipeChainStart + custom_generator(_) do + values = + fn -> TypeClass.Property.Generator.generate("") end + |> Stream.repeatedly() + |> Enum.take(Enum.random(0..100)) + + fn -> TypeClass.Property.Generator.generate("") end + |> Stream.repeatedly() + |> Enum.take(Enum.random(0..100)) + |> Enum.zip(values) + |> Enum.into(%{}) + end + + def compare(map_a, map_b) do + Witchcraft.Ord.compare(Map.to_list(map_a), Map.to_list(map_b)) + end +end diff --git a/lib/witchcraft/ord/string.ex b/lib/witchcraft/ord/string.ex new file mode 100644 index 0000000..b751ae1 --- /dev/null +++ b/lib/witchcraft/ord/string.ex @@ -0,0 +1,7 @@ +import TypeClass + +definst Witchcraft.Ord, for: BitString do + def compare(string_a, string_b) do + Witchcraft.Ord.compare(String.to_charlist(string_a), String.to_charlist(string_b)) + end +end diff --git a/lib/witchcraft/ord/tuple.ex b/lib/witchcraft/ord/tuple.ex new file mode 100644 index 0000000..6da947a --- /dev/null +++ b/lib/witchcraft/ord/tuple.ex @@ -0,0 +1,15 @@ +import TypeClass + +definst Witchcraft.Ord, for: Tuple do + # credo:disable-for-lines:6 Credo.Check.Refactor.PipeChainStart + custom_generator(_) do + fn -> TypeClass.Property.Generator.generate("") end + |> Stream.repeatedly() + |> Enum.take(Enum.random(0..100)) + |> List.to_tuple() + end + + def compare(tuple_a, tuple_b) do + Witchcraft.Ord.compare(Tuple.to_list(tuple_a), Tuple.to_list(tuple_b)) + end +end diff --git a/lib/witchcraft/orderable.ex b/lib/witchcraft/orderable.ex deleted file mode 100644 index ab28b4f..0000000 --- a/lib/witchcraft/orderable.ex +++ /dev/null @@ -1,89 +0,0 @@ -import TypeClass - -defclass Witchcraft.Orderable do - extend Witchcraft.Setoid - - @type t :: any - - where do - def compare(a, b) - end - - def greater_than?(a, b) do - case compare(a, b) do - Order.Greater -> true - Order.Lesser -> false - Order.Equal -> false - end - end - - def lesser_than?(a, b) do - case compare(a, b) do - Order.Lesser -> true - Order.Greater -> false - Order.Equal -> false - end - end - - def equal?(a, b) do - case compare(a, b) do - Order.Equal -> true - Order.Lesser -> false - Order.Greater -> false - end - end - - properties do - - end -end - -definst Witchcraft.Orderable, for: Integer do - alias Witchcraft.Orderable.Order - - def compare(a, b) when is_number(b) do - case a do - ^b -> Order.equal - a when a > b -> Order.greater - a when a < b -> Order.lesser - end - end -end - -definst Witchcraft.Orderable, for: Float do - alias Witchcraft.Orderable.Order - - def compare(a, b) when is_number(b) do - case a do - ^b -> Order.equal - a when a > b -> Order.greater - a when a < b -> Order.lesser - end - end -end - -definst Witchcraft.Orderable, for: List do - alias Witchcraft.Orderable.Order - - def compare([], []), do: Order.equal - def compare(_a, []), do: Order.greater - def compare([], b) when is_list(b), do: Order.lesser - def compare(a, b) when is_list(b) do - case compare(a, b) do - %Order.Equal{} -> - [_head_a, tail_a] = a - [_head_b, tail_b] = b - compare(tail_a, tail_b) - - order -> order - end - end -end - -definst Witchcraft.Orderable, for: BitString do - def compare(a, b) do - a - |> String.to_charlist - |> compare(String.to_charlist(b)) - end -end diff --git a/lib/witchcraft/orderable/order.ex b/lib/witchcraft/orderable/order.ex deleted file mode 100644 index d90e165..0000000 --- a/lib/witchcraft/orderable/order.ex +++ /dev/null @@ -1,95 +0,0 @@ -import TypeClass - -defmodule Witchcraft.Orderable.Order do - @type t :: Equal.t | Greater.t | Lesser.t - - defmodule Greater do - @type t :: %Greater{} - defstruct [] - - def new(), do: %Greater{} - end - - defmodule Lesser do - @type t :: %Lesser{} - defstruct [] - - def new(), do: %Lesser{} - end - - defmodule Equal do - @type t :: %Equal{} - defstruct [] - - def new(), do: %Equal{} - end - - def greater, do: Greater.new - def lesser, do: Lesser.new - def equal, do: Equal.new - - defmacro is_order(item) do - quote do - unquote(item) == %Witchcraft.Orderable.Order.Greater{} - or unquote(item) == %Witchcraft.Orderable.Order.Lesser{} - or unquote(item) == %Witchcraft.Orderable.Order.Equal{} - end - end -end - -defimpl TypeClass.Property.Generator, for: Witchcraft.Orderable.Order.Greater do - alias Witchcraft.Orderable.Order - def generate(_), do: Order.greater -end - -defimpl TypeClass.Property.Generator, for: Witchcraft.Orderable.Order.Lesser do - alias Witchcraft.Orderable.Order - def generate(_), do: Order.lesser -end - -defimpl TypeClass.Property.Generator, for: Witchcraft.Orderable.Order.Equal do - alias Witchcraft.Orderable.Order - def generate(_), do: Order.equal -end - -definst Witchcraft.Setoid, for: Witchcraft.Orderable.Order.Greater do - alias Witchcraft.Orderable.Order - def equal?(_greater, %Order.Greater{}), do: true - def equal?(_greater, _other), do: false -end - -definst Witchcraft.Setoid, for: Witchcraft.Orderable.Order.Lesser do - alias Witchcraft.Orderable.Order - def equal?(_lesser, %Order.Lesser{}), do: true - def equal?(_lesser, _other), do: false -end - -definst Witchcraft.Setoid, for: Witchcraft.Orderable.Order.Equal do - alias Witchcraft.Orderable.Order - def equal?(_equal, %Order.Equal{}), do: true - def equal?(_equal, _other), do: false -end - -definst Witchcraft.Orderable, for: Witchcraft.Orderable.Order.Greater do - alias Witchcraft.Orderable.Order - - def compare(_greater, %Order.Greater{}), do: Order.equal - def compare(_greater, %Order.Lesser{}), do: Order.greater - def compare(_greater, %Order.Equal{}), do: Order.greater -end - -definst Witchcraft.Orderable, for: Witchcraft.Orderable.Order.Lesser do - alias Witchcraft.Orderable.Order - - def compare(_lesser, %Order.Greater{}), do: Order.lesser - def compare(_lesser, %Order.Lesser{}), do: Order.equal - def compare(_lesser, %Order.Equal{}), do: Order.lesser -end - -definst Witchcraft.Orderable, for: Witchcraft.Orderable.Order.Equal do - alias Witchcraft.Orderable.Order - - def compare(_equal, %Order.Greater{}), do: Order.lesser - def compare(_equal, %Order.Lesser{}), do: Order.greater - def compare(_equal, %Order.Equal{}), do: Order.equal -end diff --git a/lib/witchcraft/semigroup.ex b/lib/witchcraft/semigroup.ex index 643ca5b..818bff9 100644 --- a/lib/witchcraft/semigroup.ex +++ b/lib/witchcraft/semigroup.ex @@ -13,17 +13,32 @@ defclass Witchcraft.Semigroup do "foo" <> " " <> "bar" == "foo bar" This generalizes the idea of a monoid, as it does not require an `empty` version. + + ## Type Class + + An instance of `Witchcraft.Semigroup` must define `Witchcraft.Semigroup.append/2`. + + Semigroup [append/2] """ alias __MODULE__ import Kernel, except: [<>: 2] - @type t :: any - - defmacro __using__(_) do - quote do - import Kernel, except: [<>: 2] - import unquote(__MODULE__) + @type t :: any() + + defmacro __using__(opts \\ []) do + {:ok, new_opts} = + Keyword.get_and_update(opts, :except, fn except -> + {:ok, [<>: 2] ++ (except || [])} + end) + + if Access.get(opts, :override_kernel, true) do + quote do + import Kernel, unquote(new_opts) + import unquote(__MODULE__), unquote(opts) + end + else + quote do: import unquote(__MODULE__), unquote(new_opts) end end @@ -73,16 +88,16 @@ defclass Witchcraft.Semigroup do ## Example - iex> concat [[1, 2, 3], [4, 5, 6]] + iex> concat [ + ...> [1, 2, 3], + ...> [4, 5, 6] + ...> ] [1, 2, 3, 4, 5, 6] - iex> concat [%{a: 1, b: 2}, %{c: 3. d: 4}, %{e: 5, f: 6}] - %{a: 1, b: 2, c: 3: d: 4, e: 5, f: 6} - """ - @spec concat([Semigroup.t]) :: Semigroup.t - def concat(list_xs) when is_list(list_xs) do - Witchcraft.Foldable.left_fold(list_xs, Quark.flip(&Semigroup.append/2)) + @spec concat(Semigroup.t()) :: [Semigroup.t()] + def concat(semigroup_of_lists) do + Enum.reduce(semigroup_of_lists, [], &Semigroup.append(&2, &1)) end @doc ~S""" @@ -94,11 +109,13 @@ defclass Witchcraft.Semigroup do [1, 2, 3, 1, 2, 3, 1, 2, 3] """ - @spec repeat(Semigroup.t, [times: pos_integer]) :: Semigroup.t + @spec repeat(Semigroup.t(), [times: non_neg_integer()]) :: Semigroup.t() + # credo:disable-for-lines:6 Credo.Check.Refactor.PipeChainStart def repeat(to_repeat, times: times) do - Stream.repeatedly(fn _ -> to_repeat end) + fn -> to_repeat end + |> Stream.repeatedly() |> Stream.take(times) - |> Enum.reduce(&Semigroup.append/2) + |> Enum.reduce(&Semigroup.append(&2, &1)) end properties do @@ -116,25 +133,42 @@ defclass Witchcraft.Semigroup do end definst Witchcraft.Semigroup, for: Function do - def append(f, g) when is_function(f), do: Quark.compose(g, f) + def append(f, g) when is_function(g), do: Quark.compose(g, f) end definst Witchcraft.Semigroup, for: Integer do - def append(a, b) when is_integer(b), do: a + b + def append(a, b), do: a + b end definst Witchcraft.Semigroup, for: Float do - def append(a, b) when is_float(b), do: a + b + def append(a, b), do: a + b end definst Witchcraft.Semigroup, for: BitString do - def append(a, b) when is_bitstring(b), do: Kernel.<>(a, b) + def append(a, b), do: Kernel.<>(a, b) end definst Witchcraft.Semigroup, for: List do - def append(a, b) when is_list(b), do: a ++ b + def append(a, b), do: a ++ b end definst Witchcraft.Semigroup, for: Map do - def append(a, b) when is_map(b), do: Map.merge(a, b) + def append(a, b), do: Map.merge(a, b) +end + +definst Witchcraft.Semigroup, for: Tuple do + # credo:disable-for-lines:5 Credo.Check.Refactor.PipeChainStart + custom_generator(_) do + Stream.repeatedly(fn -> TypeClass.Property.Generator.generate(%{}) end) + |> Enum.take(10) + |> List.to_tuple() + end + + def append(tuple_a, tuple_b) when tuple_size(tuple_a) == tuple_size(tuple_b) do + tuple_a + |> Tuple.to_list() + |> Enum.zip(Tuple.to_list(tuple_b)) + |> Enum.map(fn({x, y}) -> Witchcraft.Semigroup.append(x, y) end) + |> List.to_tuple() + end end diff --git a/lib/witchcraft/semigroupoid.ex b/lib/witchcraft/semigroupoid.ex new file mode 100644 index 0000000..5974e1d --- /dev/null +++ b/lib/witchcraft/semigroupoid.ex @@ -0,0 +1,140 @@ +import TypeClass + +defclass Witchcraft.Semigroupoid do + @moduledoc """ + A semigroupoid describes some way of composing morphisms on between some + collection of objects. + + ## Type Class + + An instance of `Witchcraft.Semigroupoid` must define `Witchcraft.Semigroupoid.compose/2`. + + Semigroupoid [compose/2] + """ + + alias __MODULE__ + import Kernel, except: [apply: 2] + + @type t :: any() + + defmacro __using__(opts \\ []) do + {:ok, new_opts} = + Keyword.get_and_update(opts, :except, fn except -> + {:ok, [apply: 2] ++ (except || [])} + end) + + if Access.get(opts, :override_kernel, true) do + quote do + import Kernel, unquote(new_opts) + import unquote(__MODULE__), unquote(opts) + end + else + quote do: import unquote(__MODULE__), unquote(new_opts) + end + end + + where do + @doc """ + Take some value and return it again + + ## Examples + + iex> times_ten_plus_one = compose(fn x -> x + 1 end, fn y -> y * 10 end) + ...> times_ten_plus_one.(5) + 51 + + """ + @spec compose(Semigroupoid.t(), Semigroupoid.t()) :: Semigroupoid.t() + def compose(morphism_a, morphism_b) + + @doc """ + Express how to apply a function to actual arguments, or "run the morphism" + + ## Examples + + iex> Witchcraft.Semigroupoid.apply(&inspect/1, [42]) + "42" + + """ + @spec apply(Semigroupoid.t(), [any()]) :: Semigroupoid.t() | any() + def apply(morphism, arguments) + end + + @doc """ + Pipe some data through a morphism. + + Similar to `apply/2`, but with a single argument, not needing to wrap + the argument in a list. + + ## Examples + + iex> pipe(42, &(&1 + 1)) + 43 + + """ + @spec pipe(any(), Semigroupoid.t()) :: any() + def pipe(data, fun), do: apply(fun, [data]) + + @doc """ + `Compose`, but with the arguments flipped (same direction as `|>`) + + ## Examples + + iex> times_ten_plus_one = pipe_compose(fn y -> y * 10 end, fn x -> x + 1 end) + ...> times_ten_plus_one.(5) + 51 + + """ + @spec pipe_compose(t(), t()) :: t() + def pipe_compose(b, a), do: compose(a, b) + + @doc """ + Composition operator "the math way" + + ## Examples + + iex> times_ten_plus_one = + ...> fn x -> x + 1 end + ...> <|> fn y -> y * 10 end + ...> + ...> times_ten_plus_one.(5) + 51 + + """ + @spec t() <|> any() :: t() + def g <|> f, do: compose(g, f) + + @doc """ + Composition operator "the pipe way" + + ## Examples + + iex> times_ten_plus_one = + ...> fn y -> y * 10 end + ...> <~> fn x -> x + 1 end + ...> + ...> times_ten_plus_one.(5) + 51 + + """ + @spec t() <~> any() :: t() + def f <~> g, do: compose(g, f) + + properties do + def associativity(data) do + a = generate(data) + b = generate(data) + c = generate(data) + + left = Semigroupoid.compose(Semigroupoid.compose(a, b), c) + right = Semigroupoid.compose(a, Semigroupoid.compose(b, c)) + + equal?(left, right) + end + end +end + +definst Witchcraft.Semigroupoid, for: Function do + def apply(fun, args), do: Kernel.apply(fun, args) + def compose(fun_a, fun_b), do: Quark.compose(fun_a, fun_b) +end diff --git a/lib/witchcraft/setoid.ex b/lib/witchcraft/setoid.ex index 600a1c1..bc044c2 100644 --- a/lib/witchcraft/setoid.ex +++ b/lib/witchcraft/setoid.ex @@ -1,73 +1,136 @@ import TypeClass defclass Witchcraft.Setoid do - @modueldoc ~S""" - A setoid is a type with an equivalence relation (able to compare of equality). + @moduledoc ~S""" + A setoid is a type with an equivalence relation + + This is most useful when equivalence of some data is not the same as equality. + Since some types have differing concepts of equality, this allows overriding the behaviour from `Kernel.==/2`. To get the Setoid `==` operator override, simply `use Witchcraft.Setoid`. + + ## Type Class + + An instance of `Witchcraft.Setoid` must define `Witchcraft.Setoid.equivalent?/2` + + Setoid [equivalent?/2] """ alias __MODULE__ - import Kernel, except: [==: 2] - - defmacro __using__(_) do - quote do - import Kernel, except: [==: 2] - import unquote(__MODULE__) + import Kernel, except: [==: 2, !=: 2] + + defmacro __using__(opts \\ []) do + {:ok, new_opts} = + Keyword.get_and_update(opts, :except, fn except -> + {:ok, [==: 2, !=: 2] ++ (except || [])} + end) + + if Access.get(opts, :override_kernel, true) do + quote do + import Kernel, unquote(new_opts) + import unquote(__MODULE__), unquote(opts) + end + else + quote do: import unquote(__MODULE__), unquote(new_opts) end end + @type t :: any() + where do @doc ~S""" - Compare two + Compare two setoids and determine if they are equivalent + + Aliased as `==` + + ## Examples + + iex> equivalent?(1, 2) + false + + iex> import Kernel, except: [==: 2, !=: 2] + ...> %{a: 1} == %{a: 1, b: 2} + false + + equivalent?(%Maybe.Just{just: 42}, %Maybe.Nothing{}) + #=> false + + ### Equivalence not equality + + baby_harry = %Wizard{name: "Harry Potter", age: 10} + old_harry = %Wizard{name: "Harry Potter", age: 17} + + def chosen_one?(some_wizard), do: equivalent?(baby_harry, some_wizard) + + chosen_one?(old_harry) + #=> true + """ - def equal?(a, b) + @spec equivalent?(Setoid.t(), Setoid.t()) :: boolean() + def equivalent?(a, b) end - defalias a == b, [as: :equal?] + defalias a == b, [as: :equivalent?] + + @doc """ + The opposite of `equivalent?/2` + + ## Examples + + iex> nonequivalent?(1, 2) + true + + """ + @spec nonequivalent?(Setoid.t(), Setoid.t()) :: boolean() + def nonequivalent?(a, b), do: not equivalent?(a, b) + + defalias a != b, [as: :nonequivalent?] properties do def reflexivity(data) do a = generate(data) - a |> Setoid.equal?(a) + Setoid.equivalent?(a, a) end def symmetry(data) do a = generate(data) b = generate(data) - Kernel.==(Setoid.equal?(a, b), Setoid.equal?(b, a)) + equal?(Setoid.equivalent?(a, b), Setoid.equivalent?(b, a)) end def transitivity(data) do a = b = c = generate(data) - - Setoid.equal?(a, b) and Setoid.equal?(b, c) and Setoid.equal?(a, c) + Setoid.equivalent?(a, b) and Setoid.equivalent?(b, c) and Setoid.equivalent?(a, c) end end end definst Witchcraft.Setoid, for: Integer do - def equal?(a, b) when is_integer(b), do: Kernel.==(a, b) + def equivalent?(int, num), do: Kernel.==(int, num) end definst Witchcraft.Setoid, for: Float do - def equal?(a, b) when is_float(b), do: Kernel.==(a, b) + def equivalent?(float, num), do: Kernel.==(float, num) end definst Witchcraft.Setoid, for: BitString do - def equal?(a, b) when is_bitstring(b), do: Kernel.==(a, b) + def equivalent?(string_a, string_b), do: Kernel.==(string_a, string_b) end definst Witchcraft.Setoid, for: Tuple do - def equal?(a, b) when is_tuple(b), do: Kernel.==(a, b) + def equivalent?(tuple_a, tuple_b), do: Kernel.==(tuple_a, tuple_b) end definst Witchcraft.Setoid, for: List do - def equal?(a, b) when is_list(b), do: Kernel.==(a, b) + def equivalent?(list_a, list_b), do: Kernel.==(list_a, list_b) end definst Witchcraft.Setoid, for: Map do - def equal?(a, b) when is_map(b), do: Kernel.==(a, b) + def equivalent?(map_a, map_b), do: Kernel.==(map_a, map_b) +end + +definst Witchcraft.Setoid, for: MapSet do + def equivalent?(a, b), do: MapSet.equal?(a, b) end diff --git a/lib/witchcraft/traversable.ex b/lib/witchcraft/traversable.ex new file mode 100644 index 0000000..d6b1548 --- /dev/null +++ b/lib/witchcraft/traversable.ex @@ -0,0 +1,307 @@ +import TypeClass + +defclass Witchcraft.Traversable do + @moduledoc """ + Walk across a data structure from left to right, + running some action on each element in turn. + + Similar to applicatives, it can be used to do things like collecting some effect + while performing other actions. + + ## Type Class + + An instance of `Witchcraft.Traversable` must also implement `Witchcraft.Foldable` + and `Witchcraft.Functor`, and define `Witchcraft.Foldable.right_fold/3`. + + [right_fold/3] Foldable Functor [map/2] + ↓ ↓ + Traversable + [right_fold/3] + """ + + alias __MODULE__ + alias Witchcraft.{Foldable, Unit} + + extend Witchcraft.Foldable + extend Witchcraft.Functor + + use Witchcraft.Applicative + use Witchcraft.Foldable, except: [equal?: 2] + + use Quark + + @type t :: any() + @type link :: (any() -> Traversable.t()) + + defmacro __using__(opts \\ []) do + quote do + use Witchcraft.Foldable, unquote(opts) + use Witchcraft.Functor, unquote(opts) + import unquote(__MODULE__), unquote(opts) + end + end + + where do + @doc """ + Convert elements to actions, and then evaluate the actions from left-to-right, + and accumulate the results. + + For a version without accumulation, see `then_traverse/2`. + + ## Examples + + iex> traverse([1, 2, 3], fn x -> {x, x * 2, x * 10} end) + {6, 12, [10, 20, 30]} + + iex> traverse({1, 2, 3}, fn x -> [x] end) + [{1, 2, 3}] + + iex> traverse({1, 2, 3}, fn x -> [x, x * 5, x * 10] end) + [ + {1, 2, 3}, + {1, 2, 15}, + {1, 2, 30} + ] + + iex> traverse([1, 2, 3], fn x -> [x, x * 5, x * 10] end) + [ + # + [1, 2, 3], [1, 2, 15], [1, 2, 30], + [1, 10, 3], [1, 10, 15], [1, 10, 30], + [1, 20, 3], [1, 20, 15], [1, 20, 30], + # + [5, 2, 3], [5, 2, 15], [5, 2, 30], + [5, 10, 3], [5, 10, 15], [5, 10, 30], + [5, 20, 3], [5, 20, 15], [5, 20, 30], + # + [10, 2, 3], [10, 2, 15], [10, 2, 30], + [10, 10, 3], [10, 10, 15], [10, 10, 30], + [10, 20, 3], [10, 20, 15], [10, 20, 30] + ] + + traverse([1, 2, 3], fn x -> %Algae.Maybe.Just{just: x} end) + #=> %Algae.Maybe.Just{just: [1, 2, 3]} + + traverse(%Algae.Maybe.Just{just: 4}, fn x -> [x, x * 10] end) + #=> [ + # %Algae.Maybe.Just{just: 4}, + # %Algae.Maybe.Just{just: 40} + # ] + + traverse([1, 2, 3], fn x -> + if is_even(x) do + %Algae.Maybe.Just{just: x} + else + %Algae.Maybe.Nothing{} + end + end) + #=> %Algae.Maybe.Nothing{} + + """ + @spec traverse(Traversable.t(), Traversable.link()) :: Traversable.t() + def traverse(data, link) + end + + properties do + def naturality(data) do + a = generate(data) + + f = fn x -> [x] end + g = &Quark.id/1 + + a + |> Traversable.traverse(f) + |> g.() + |> equal?(Traversable.traverse(a, fn x -> x |> f.() |> g.() end)) + end + + def identity(data) do + a = generate(data) + + a + |> Traversable.traverse(fn x -> {x} end) + |> equal?({a}) + end + + def composition(_data) do + # traverse (Compose . fmap g . f) = Compose . fmap (traverse g) . traverse f + + # MAINTAINER COMMENT + # ================== + # + # I cannot get this working. After throwing 1.5 days at this *one* prop, + # I'm walking away for now. If someone has an implementation that works, + # it would be greatly appreciated. + # + # I've implemented %Endo{} and %Compose{}, but Compose's Foldable refused + # to work because there is no Foldable instance for functions (AFAIK). + # + # I believe that the Hakell version works because of the lazy + # recursive call to foldMap in `Foldable f, Foldable g => Compose (f g)`, + # which in Elixir is trying to get dispatched on the partial function. + # + # It's very possible that there's a simple solution that I'm just not seeing + # after staring at the problem for too long + # + # PRs gladly accepted here: https://github.com/expede/witchcraft/compare + # + # ~ Brooklyn Zelenka, @expede + + true + end + end + + # traverse with args flipped + @doc """ + `traverse/2` with arguments reversed. + + ## Examples + + iex> fn x -> {x, x * 2, x * 10} end |> across([1, 2, 3]) + {6, 12, [10, 20, 30]} + + iex> fn x -> [x] end |> across({1, 2, 3}) + [{1, 2, 3}] + + iex> fn x -> [x, x * 5, x * 10] end |> across({1, 2, 3}) + [ + {1, 2, 3}, + {1, 2, 15}, + {1, 2, 30} + ] + + iex> fn x -> [x, x * 5, x * 10] end |> across([1, 2, 3]) + [ + # + [1, 2, 3], [1, 2, 15], [1, 2, 30], + [1, 10, 3], [1, 10, 15], [1, 10, 30], + [1, 20, 3], [1, 20, 15], [1, 20, 30], + # + [5, 2, 3], [5, 2, 15], [5, 2, 30], + [5, 10, 3], [5, 10, 15], [5, 10, 30], + [5, 20, 3], [5, 20, 15], [5, 20, 30], + # + [10, 2, 3], [10, 2, 15], [10, 2, 30], + [10, 10, 3], [10, 10, 15], [10, 10, 30], + [10, 20, 3], [10, 20, 15], [10, 20, 30] + ] + + """ + @spec across(Traversable.link(), Traversable.t()) :: Traversable.t() + def across(link, traversable), do: traverse(traversable, link) + + @doc """ + `traverse` actions over data, but ignore the results. + + ## Examples + + iex> [1, 2, 3] + ...> |> then_traverse(fn x -> [x, x * 5, x * 10] end) + [ + # + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + # + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + # + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{} + ] + + """ + @spec then_traverse(Traversable.t(), Traversable.link()) :: Applicative.t() + def then_traverse(traversable, link) do + right_fold(traversable, of(traversable, %Unit{}), fn(step, acc) -> + step + |> link.() + |> then(acc) + end) + end + + @doc """ + The same as `then_traverse`, but with the arguments flipped. + + ## Examples + + iex> fn x -> [x, x * 5, x * 10] end + ...> |> then_across([1, 2, 3]) + [ + # + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + # + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + # + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{}, + %Witchcraft.Unit{}, %Witchcraft.Unit{}, %Witchcraft.Unit{} + ] + + """ + @spec then_traverse(Traversable.link(), Traversable.t()) :: Applicative.t() + def then_across(link, traversable), do: then_traverse(traversable, link) + + @doc """ + Run each action/effect in sequence (from left to right), + and accumulate values along the way. + + ## Examples + + iex> sequence([{1, 2, 3}, {4, 5, 6}]) + {5, 7, [3, 6]} + + iex> [ + ...> [1, 2, 3], + ...> [4, 5, 6] + ...> ] + ...> |> sequence() + [ + [1, 4], + [1, 5], + [1, 6], + [2, 4], + [2, 5], + [2, 6], + [3, 4], + [3, 5], + [3, 6] + ] + + """ + @spec sequence(Traversable.t()) :: Foldable.t() + def sequence(traversable), do: traverse(traversable, &Quark.id/1) +end + +definst Witchcraft.Traversable, for: Tuple do + use Witchcraft.Functor + + def traverse(tuple, link) do + last_index = tuple_size(tuple) - 1 + + tuple + |> elem(last_index) + |> link.() + ~> fn last -> put_elem(tuple, last_index, last) end + end +end + +definst Witchcraft.Traversable, for: List do + use Witchcraft.Applicative + use Witchcraft.Foldable + + def traverse([], link), do: of(link.([]), []) + def traverse(list = [head | _], link) do + right_fold(list, of(link.(head), []), fn(x, acc) -> + lift(link.(x), acc, fn(link_head, link_tail) -> + [link_head | link_tail] + end) + end) + end +end diff --git a/lib/witchcraft/unit.ex b/lib/witchcraft/unit.ex new file mode 100644 index 0000000..da2f377 --- /dev/null +++ b/lib/witchcraft/unit.ex @@ -0,0 +1,18 @@ +defmodule Witchcraft.Unit do + @moduledoc """ + The `unit` or `Void` type. A stand in for "no added information here". + + Why not encode unit as `{}`? Many protocols (Witchcraft and others) + convert tuples to lists, and thus will treat unit as `{}` and thus `[]`, + which we don't want. The struct removes this ambiguity. + """ + + alias __MODULE__ + + @type t :: %Unit{} + defstruct [] + + @doc "Helper to summon the singleton `Unit` struct" + @spec new() :: t() + def new, do: %Unit{} +end diff --git a/mix.exs b/mix.exs index 8638ec5..5d6091f 100644 --- a/mix.exs +++ b/mix.exs @@ -7,8 +7,8 @@ defmodule Witchcraft.Mixfile do name: "Witchcraft", description: "Common algebras (monoids, functors, monads, &c)", - version: "1.0.0-alpha-1", - elixir: "~> 1.4", + version: "1.0.0-beta", + elixir: "~> 1.5", package: [ maintainers: ["Brooklyn Zelenka"], @@ -33,12 +33,12 @@ defmodule Witchcraft.Mixfile do {:exceptional, "~> 2.1"}, {:operator, "~> 0.2"}, {:quark, "~> 2.2"}, - {:type_class, "~> 1.0"} + {:type_class, "~> 1.2"} ], docs: [ extras: ["README.md"], - logo: "./brand/Icon/PNG/WC-icon-sml@2x.png", + logo: "./brand/Icon/PNG/WC-icon-sml@2x-circle.png", main: "readme" ] ] diff --git a/mix.lock b/mix.lock index 1c71168..cf99b51 100644 --- a/mix.lock +++ b/mix.lock @@ -1,12 +1,12 @@ -%{"algae": {:hex, :algae, "0.12.1", "79ef7ed521fc6bf3dd2401002f04b06003db487c5c261aa6cc54835e25768a3d", [:mix], [{:quark, "~> 2.2", [hex: :quark, optional: false]}]}, +%{"algae": {:hex, :algae, "0.12.1", "79ef7ed521fc6bf3dd2401002f04b06003db487c5c261aa6cc54835e25768a3d", [:mix], [{:quark, "~> 2.2", [hex: :quark, repo: "hexpm", optional: false]}], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, - "credo": {:hex, :credo, "0.7.3", "9827ab04002186af1aec014a811839a06f72aaae6cd5eed3919b248c8767dbf3", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"}, + "credo": {:hex, :credo, "0.8.4", "4e50acac058cf6292d6066e5b0d03da5e1483702e1ccde39abba385c9f03ead4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"}, "dialyxir": {:hex, :dialyxir, "0.5.0", "5bc543f9c28ecd51b99cc1a685a3c2a1a93216990347f259406a910cf048d1d7", [:mix], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.0", "bf1ce17aea43ab62f6943b97bd6e3dc032ce45d4f787504e3adf738e54b42f3a", [:mix], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.15.1", "d5f9d588fd802152516fccfdb96d6073753f77314fcfee892b15b6724ca0d596", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.2", "f718159d6b65068e8daeef709ccddae5f7fdc770707d82e7d126f584cd925b74", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, "exceptional": {:hex, :exceptional, "2.1.0", "b3bc6c7041a242df9f7e18223649f3d66633827897138cf5f26c46c88b6925ae", [:mix], [], "hexpm"}, "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "operator": {:hex, :operator, "0.2.0", "b613bdb520a20dbc016d921d444fd2ef2212136be9385d6dca448a2a38d0577f", [:mix], [], "hexpm"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, - "quark": {:hex, :quark, "2.2.0", "56c231db44aea238e383472be3f909d4904a013c622631faafe97b001af146f1", [], [], "hexpm"}, - "type_class": {:hex, :type_class, "1.0.0", "116a243e5cbd26797032dc4c113f6770f116ca161402c86970b3ea4bd9ef54da", [:mix], [{:exceptional, "~> 2.1", [hex: :exceptional, repo: "hexpm", optional: false]}], "hexpm"}} + "quark": {:hex, :quark, "2.3.0", "3742ddc14441b4930c7067741879cb5b3e4881f391ea0aed7b138f20d16b30bb", [:mix], [], "hexpm"}, + "type_class": {:hex, :type_class, "1.2.2", "a84cab53b80e7c3c15f5ba9a550f5c68646c2b965d19f711f37473803d1ec2ff", [:mix], [{:exceptional, "~> 2.1", [hex: :exceptional, repo: "hexpm", optional: false]}], "hexpm"}} diff --git a/test/witchcraft_test.exs b/test/witchcraft_test.exs index 575aa5f..f1ac89d 100644 --- a/test/witchcraft_test.exs +++ b/test/witchcraft_test.exs @@ -1,46 +1,43 @@ defmodule WitchcraftTest do - use ExUnit.Case + use ExUnit.Case, async: true - # # Monoid - # # ====== - doctest Witchcraft.Monoid, import: true + ################ + # Data Structs # + ################ - # doctest Witchcraft.Monoid.Protocol.Integer, import: true - # doctest Witchcraft.Monoid.Protocol.Float, import: true - # doctest Witchcraft.Monoid.Protocol.BitString, import: true - # doctest Witchcraft.Monoid.Protocol.List, import: true - # doctest Witchcraft.Monoid.Protocol.Map, import: true + doctest Witchcraft.Unit, import: true - # doctest Witchcraft.Monoid.Operator, import: true - # doctest Witchcraft.Monoid.Property, import: true + ################# + # Error Structs # + ################# - # # Functor - # # ======= - # doctest Witchcraft.Functor, import: true + doctest Witchcraft.Foldable.EmptyError, import: true - # doctest Witchcraft.Functor.Protocol.List, import: true + ################ + # Type Classes # + ################ - # doctest Witchcraft.Functor.Function, import: true - # doctest Witchcraft.Functor.Operator, import: true - # doctest Witchcraft.Functor.Property, import: true + doctest Witchcraft.Semigroupoid, import: true + doctest Witchcraft.Category, import: true + doctest Witchcraft.Arrow, import: true - # # Applicative - # # =========== - # doctest Witchcraft.Applicative, import: true + doctest Witchcraft.Setoid, import: true + doctest Witchcraft.Ord, import: true - # doctest Witchcraft.Applicative.Protocol.List, import: true + doctest Witchcraft.Semigroup, import: true + doctest Witchcraft.Monoid, import: true - # doctest Witchcraft.Applicative.Function, import: true - # doctest Witchcraft.Applicative.Operator, import: true - # doctest Witchcraft.Applicative.Property, import: true + doctest Witchcraft.Foldable, import: true + doctest Witchcraft.Traversable, import: true - # # Monad - # # ===== - # doctest Witchcraft.Monad, import: true + doctest Witchcraft.Functor, import: true + doctest Witchcraft.Bifunctor, import: true - # doctest Witchcraft.Monad.Protocol.List, import: true + doctest Witchcraft.Extend, import: true + doctest Witchcraft.Comonad, import: true - # doctest Witchcraft.Monad.Function, import: true - # doctest Witchcraft.Monad.Operator, import: true - # doctest Witchcraft.Monad.Property, import: true + doctest Witchcraft.Apply, import: true + doctest Witchcraft.Applicative, import: true + doctest Witchcraft.Chain, import: true + doctest Witchcraft.Monad, import: true end