instance
of that class
. a -> a -> a
) While we may expect them to behave in some common ways, the exact meaning of that depends on the specific type. Why not write functions that could work on any of these types with respect to this combining behaviour?Semigroup
. Don't let the math-y name scare you! It's actually very straightforward for a "combinable" thing.=>
.x
, then you can also do y
!" It lets you extend abstract interfaces with more functions. This usually makes for a smaller number of types that have the new interface, because they need the old type class, plus an instance for the new one, which is not always even possible!<>
is a no op. We'll call this mempty
, because of how it works on lists ([] ++ xs = xs) and because the name id
was already taken.mempty
and <>
automatically available. Let's look at a few of these:Functor
tree, which includes the infamour Monad
class. These also have special rules that you need to obey when writing instances for, but you'll mostly be using someone else's so don't worry about them too much for now.fmap
's operator variants <gt;
and <&>
floating around$
(application) but working on something that's been put into a container (practically any sum or product type), hence the visual pun of <gt;
. Same goes for &
(pipe) and <&>
.Applicative
s add a lot of power to Functors
by defining two functions:pure
(also known as the somewhat confusingly named return
) takes a plain value and wraps it in a container.<*>
does function application when both functions and arguments are wrapped<*>
is often referred to as the Tie Fighter operator 🤣 It's also called ap
, short for "applicative apply".Maybe
:>>=
is pronounced "bind". This is because when you use it to link into the next function, it "binds" to the first argument, like a supercharged pipe. Further, because of how scoping rules work, argument name bindings last until the end of the chain:>>=
is special compared to <gt;
and <*>
. It doesn't run out of arguments, and can be chained forever. >>=
and nesting to keep it straight. Haskell has "do notation" to clean this up, make it feel like an linear set of instructions, and also pun on a lot of imperative programming.pure
with return
, which is the same function but renamed for extra imperative punning.return
does not exit out of the function chain. It is the same function as pure
and only wraps values in the correct container.let
that lets you skip the <-
for simple values.class
declaration, or in the same module as a datatype definition. But what happens when you need to add an instance from a library to a datatype that's also from a library?Fission.Internal.Orphanage
. Importing this module doesn't have any actual exports, but you do need to signal to the compiler that it's a dependency for the module that you're writing. Importing it normally will lead to the linter complaining about an unused import. You can get around this warning by explicitely showiung that are no imports: