Higher order polymorphism requires a strict order of arguments?

Reading LYAH , I came across this piece of code:

newtype Writer wa = Writer { runWriter :: (a, w) } instance (Monoid w) => Monad (Writer w) where return x = Writer (x, mempty) (Writer (x,v)) >>= f = let (Writer (y, v')) = fx in Writer (y, v `mappend` v') 

When trying to understand that this is a feature of Writer w in the first line, I found that this is not a complete type, but a kind of type constructor with 1 argument, for example Maybe for Maybe String

It looks great, but what if the initial type, if Writer' defined with variable arguments of type type:

 newtype Writer' aw = Writer' { runWriter :: (a, w) } 

Is it possible to implement a Monad instance now? Something like this, but what really can be compiled:

 instance (Monoid w) => Monad (\* -> Writer' * monoid) where 

The idea \* -> Writer' * monoid is the same as Writer w : There is no type constructor with an argument of the same type - this time the first.

+7
syntax haskell
source share
2 answers

This is not possible in Haskell, you will need a type-level lambda function that does not exist.

There are type synonyms that can be used to define reorders of type variables:

 type Writer'' aw = Writer' aw 

but you cannot specify instance instances for partially used type synonyms (even with the TypeSynonymInstances extension).

I wrote a dissertation on how lambdas of type level can be added to the GHC: https://xnyhps.nl/~thijs/share/paper.pdf , which will be used in an -class type without sacrificing type inferences.

+9
source share

What you see here is a limited selection of Haskell designs. It is perfectly clear, conceptually speaking, that your Writer' type is a functor if you "leave" your first parameter. And the syntax of a programming language could be invented to allow such declarations.

The Haskell community did not do this because what they have is relatively simple and works reasonably well. This does not mean that alternative projects are impossible, but for the adoption of such a design will have to:

  • Itโ€™s no harder to put into practice what we already have;
  • Offer functionality or benefits that a switch would cost.

This generalizes many other ways the Haskell communities use it; often the choice to present something as a difference of types is tied to some artifact of language design. Many monad transformers are good examples, like MaybeT :

 newtype MaybeT ma = MaybeT { runMaybeT :: m (Maybe a) } instance Functor m => Functor (MaybeT m) where ... instance Applicative m => Applicative (MaybeT m) where ... instance Monad m => Monad (MaybeT m) where ... instance MonadTrans MaybeT where ... 

Since this is a newtype , this means that MaybeT IO String is isomorphic to IO (Maybe String) ; you can think of two types as two โ€œperspectivesโ€ in one set of values:

  • IO (Maybe String) is an IO action that creates values โ€‹โ€‹of type Maybe String ;
  • MaybeT IO String is a MaybeT IO action that creates String values.

The difference between the perspectives is that they imply different implementations of Monad operations. In Haskell, then it is also associated with the following essential technical facts:

  • One String has the last type parameter ("value"), and the other Maybe String :
  • IO and MaybeT IO have different instances for the Monad class.

But, perhaps, there is a language design where it can be said that the type IO (Maybe a) can have a specific monad for it and different from the monad for the more general type IO a . This language will have some complexity to make this distinction sequentially (for example, rules to determine which instance of Monad is the default for IO (Maybe String) and rules to allow the programmer to override the default selection). And I would modestly declare that the final result will be no less complicated than what we have. TL; DR: Fur.

+5
source share

All Articles