In Haskell I find it difficult to completely grasp the purpose of a kind system, and what it truly adds to the language.
I understand having kinds adds safety.
For example consider fmap :: (a -> b) -> f a -> f b
vs a monokinded version of it fmap2 :: (a -> b) -> p -> q
.
My understanding is that, thanks to the kind system, I can specify beforehand with higher detail what the shape of the data should be. It is better, as the type checker can check more, and stricter requirements will make it less likely that users will mess up with types. This will reduce the likelihood of programming errors. I believe this is the main motivation for kinds but I might be wrong.
Now I think fmap2 can have the same implementation as fmap, since its types are unconstrained. Is it true?
Could one just replace all multi kinded types in the base/ghc library by mono kinded ones, and would it still compile correctly?
To clarify a little bit I mean that for a class like:
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
I might replace it by something like
class Functor a b p q where
fmap :: (a -> b) -> p -> q
(<$) :: a -> q -> p
This way for a Functor
instance like
instance Functor [] where fmap = map
I might replace it by
instance Functor a b p q where fmap = map
Remark: This wont work as is because i also need to modify map
and go down the dependency chain. Will think more about this later..
I'm trying to figure out if kinds add more than just safety? Can I do something with multi kinded types that I cannot do with mono kinded ones?
Remark: Here I forgot to mention i'm usually using some language extensions, typically to allow more flexibility in writing classes. When doing vanilla haskell kinds can be really meaningful thing to use. But when I start using type families, and a few of other extensions it becomes much less clear that I need kinds at all.
I have in mind that there is some monotraversable library which reimplements many standard library functions using single kinded types, and type families to generalize signatures. That's why my intuition is that multi kinded variables might not really add that much expressive power after all, provided one uses type families. However a drawback of doing this is that you lose type safety. My question is do you really only lose just that, or do you really lose something else.
fmap
is actually longer than what you say in your question:fmap :: Functor f => (a -> b) -> f a -> f b
. How would you state the constraint analogous toFunctor f
for your proposed alternative type? – Daniel Wagner