Witchcraft
Monads and other dark magic for Elixir
Install / Use
/learn @witchcrafters/WitchcraftREADME

Witchcraft is a library providing common algebraic and categorical abstractions to Elixir.
Monoids, functors, monads, arrows, categories, and other dark magic right at your fingertips.
README
Table of Contents
- Quick Start
- Library Family
- Values
- Type Class Hierarchy
- Writing Class Instances
- Operators
- Haskell Translation Table
- Prior Art and Further Reading
- Credits
Quick Start
def deps do
[{:witchcraft, "~> 1.0"}]
end
# ...
use Witchcraft
Library Family
Quark TypeClass
↘ ↙
Witchcraft
↓
Algae
| Name | Description |
|--------------------------------------------------:|---------------------------------------------------------------|
| Quark | Standard combinators (id, compose, &c) |
| TypeClass | Used internally to generate type classes |
| Algae | Algebraic data types that implement Witchcraft type classes |
Values
Beginner Friendliness
You shouldn't have to learn another language just to understand powerful abstractions! By enabling people to use a language that they already know, and is already in the same ballpark in terms of values (emphasis on immutability, &c), we can teach and learn faster.
As much as possible, keep things friendly and well explained. Concrete examples are available via doctests.
Consistency & Ethos
Elixir does a lot of things differently from certain other functional languages.
The idea of a data "subject" being piped though functions is conceptually different from
pure composition of functions that are later applied. Witchcraft honours the
Elixir/Elm/OCaml way, and operators point in the direction that data travels.
Some functions in the Elixir standard library have been expanded to work with more
types while keeping the basic idea the same. For example, <> has been expanded
to work on any monoid
(such as integers, lists, bitstrings, and so on).
All operators have named equivalents, and auto-currying variants of higher order functions
are left at separate names so you can performance tune as needed (currying is helpful for
more abstract code). With a few exceptions (we're looking at you, Applicative),
pipe-ordering is maintained.
Pragmatism
Convincing a company to use a language like Haskell or PureScript can be challenging. Elixir is gaining a huge amount of interest. Many people have been able to introduce these concepts into companies using Scala, so we should be able to do the same here.
All functions are compatible with regular Elixir code, and no types are enforced aside from what is used in protocol dispatch. Any struct can be made into a Witchcraft class instance (given that it conforms to the properties).
Type Class Hierarchy
Semigroupoid Semigroup Setoid Foldable Functor -----------┐
↓ ↓ ↓ ↓ ↙ ↓ ↘ |
Category Monoid Ord Traversable Apply Bifunctor |
↓ ↙ ↘ ↓
Arrow Applicative Chain Extend
↘ ↙ ↓
Monad Comonad
Having a clean slate, we have been able to use a clean set of type classes. This is largely
taken from the Fantasy Land Specification
and Edward Kmett's semigroupoids package.
As usual, all Applicatives are Functors, and all Monads are Applicatives.
This grants us the ability to reuse functions in their child classes.
For example, of can be used for both pure and return, lift/* can handle
both liftA* and liftM*, and so on.
Import Chains
It is very common to want to import a class and all of its dependencies.
You can do this with use. For example, you can import the entire library with:
use Witchcraft
Or import a module plus all others that it depends on:
use Witchcraft.Applicative
Any options that you pass to use will be propagated all the way down the chain:
use Witchcraft.Applicative, 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:
use Witchcraft.Applicative, override_kernel: false
# Or even
use Witchcraft, override_kernel: false
Writing Class Instances
How to make your custom struct compatible with Witchcraft:
- Read the
TypeClassREADME - Implement the
TypeClassdata generator protocol for your struct - Use
definst("define instance") instead ofdefimpl:
definst Witchcraft.Functor, for: Algae.Id do
def map(%{id: data}, fun), do: %Algae.Id{id: fun.(data)}
end
All classes have properties that your instance must conform to at compile time.
mix will alert you to any failing properties by name, and will refuse to compile
without them. Sometimes it is not possible to write an instance that will pass the check,
and you can either write a custom generator
for that instance, or force
the instance. If you must resort to forcing the instance, please write a test
of the property for some specific case to be reasonably sure that it will be compatible
with the rest of the library.
More reference instances are available in Algae.
Operators
| Family | Function | Operator |
|-------------:|:-----------------|:---------|
| Setoid | equivalent? | == |
| | nonequivalent? | != |
| Ord | greater_than? | > |
| | lesser_than? | < |
| Semigroup | append | <> |
| Functor | lift | ~> |
| | convey | ~>> |
| | chain | >>> |
| | over | <~ |
| | ap | <<~ |
| | reverse_chain | <<< |
| Semigroupoid | compose | <\|> |
| | pipe_compose | <~> |
| Arrow | product | ^^^ |
| | fanout | &&& |
Haskell Translation Table
| Haskell Prelude | Witchcraft |
|----------------:|:-------------------|
| flip ($) | \|>/2 (Kernel) |
| . | <\|>/2 |
| <<< | <\|>/2 |
| >>> | <~>/2 |
| <> | <>/2 |
| <$> | <~/2 |
| flip (<$>) | ~>/2 |
| fmap | lift/2 |
| liftA | lift/2 |
| liftA2 | lift/3 |
| liftA3 | lift/4 |
| liftM | lift/2 |
| liftM2 | lift/3 |
| liftM3 | lift/4 |
| ap | ap/2 |
| <*> | <<~/2 |
| <**> | ~>>/2 |
| *> | then/2 |
| <* | following/2 |
| pure | of/2 |
| return | of/2 |
| >> | then/2 |
| >>= | >>>/2 |
| =<< | <<</2 |
| *** | ^^^/2 |
| &&& | &&&/2 |
Prior Art and Further Reading
This library draws heavy inspiration from mathematics, other languages, and other Elixir libraries. We would be ashamed not to mention them here. There is much, much more out there, but these are our highlights and inspirations.
The Monad library predates Witchcraft
by several years. This library proved that it is entirely possible
to bring do-notation to Elixir. It takes a very different approach:
it is very up-front that it has a very loose definition of what it means for
something to be a "monad", and relies on behaviours rather than ad-hoc polymorphism.
The Fantasy Land Spec is a spec for projects such as this one, but targ
Related Skills
node-connect
338.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
338.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.4kCommit, push, and open a PR
