haskpy.types.compose.Compose¶
- Compose(X, Y)[source]¶
Compose two type constructors X and Y into a single type constructor
Kind:
Compose :: (* -> *) -> (* -> *) -> * -> *
That is, it takes two type constructors and one concrete type to create a concrete type. Another way to see it is that it takes two one-argument type constructors and returns a one-argument type constructor:
Compose :: (* -> *) -> (* -> *) -> (* -> *)
That’s how we can see it here.
Two nested Applicative structures are merged into one layer:
Composed :: f1 (f2 a) -> f f1 f2 a
Note that
f
is a type constructor of three arguments as shown by its kind above.Some motivation for the current implementation:
Why not just a class that takes a value/object? Why do we need this function wrapper Compose that takes classes as arguments? Because we need to be able to implement the class method
pure
. Also, this solution would make it possible to decide the parent classes based on the classes of X and Y, so that composed result isn’t, for instance, Foldable unless both X and Y are Foldable.Why the class Composed doesn’t have
__init__
that passes*args
and**kwargs
to the outer class constructor? It would make it slightly more convenient to create values, right? Not really. We want to be able to transform already existing non-composed values to composed values. For that we need to just take a value, not the outer class constructor arguments. Also, this allows us to decompose and compose in turns without problems. Also, it is more explicit for the user that which are the underlying types because they need to write those when creating values.
So, in comparison to Haskell, the function
Compose
corresponds to type-level composing andComposed
corresponds to value-level type converter. We need explicit type-level composing here because we need some class methods such aspure
.