type signature golfing (or not) in Haskell

Viewed 94

Could GHC golf out this extra type annotation or does it bring something real ?

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module SOQuestionInstanceQuantification2 where

class IsomorphismFromTo a b where --  :~:
  isofromto :: forall p. p a -> p b
  to :: a -> b
  --to = (isofromto) @((->) a) id -- Bad without (redundant ?) annotation
  to = (isofromto :: (forall p. p a -> p b)) @((->) a) id -- Good with (redundant ?) annotation

edit : removed superfluous DefaultSignatures

2 Answers

The real signature of isofromto includes the type variables from the class:

isofromto :: forall a b. IsomorphismFromTo a b => forall p. p a -> p b

Therefore, if we want to be explicit on p, we need to write

to = isofromto @a @b @((->) a) id

to pass those argument as well. Alternatively,

to = isofromto @_ @_ @((->) a) id

makes those arguments to be inferred.

In the longer alternative

to = (isofromto :: (forall p. p a -> p b)) @((->) a) id

the signature is not redundant, since it fixes the a and b type arguments.

Note that, in the general case, having to pass these additional arguments is needed since we might want to call, within to @a @b, an implementation of isofromto from another instance e.g. isofromto @(a,b) @(b,a).

Of course, Haskell could have a notation to indicate "call isofromto from the current instance", without additional type arguments, but has not. I guess in regular programming type inference already covers most cases, and when it's not enough, type applications already provide a means to choose the instance we need (including the current one), so I guess there is no strong need for such a special notation.

For this kind of question in the future, you can ask ghci what's going on:

> :set -fprint-explicit-foralls
> :t +v isofromto
isofromto
  :: forall a b (p :: * -> *). IsomorphismFromTo a b => p a -> p b

This makes it very clear that a and b are the first two type arguments. I recommend adding something like this to your ~/.ghci; it's quietly saved my bacon a bunch of times.

:set -fprint-explicit-foralls
:def t Prelude.return Prelude.. (":type +v "Prelude.++)

The second line aliases :t to :type +v, so you don't need to remember the +v when you need it most. In the rare case that you actually want :t without +v, it is still available as :type.

These two pieces together (+v and print-explicit-foralls) will show you which type variables (if any) are available for application, and in which order. Type variables that are not available for type application will be enclosed in curly braces:

> :t +v id id
id id :: forall {a}. a -> a

Missing one or the other of print-explicit-foralls or +v will lie in some situations.

Related