Wednesday, May 26, 2021

[ruimxwha] return value type annotation

i dislike Haskell's syntax for type signatures because it requires writing the variable name twice (violating the adage Don't Repeat Yourself), and requires counting to figure out which type corresponds to which argument:

myfunction :: Typea -> Typeb -> Typec -> Typed -> Typee;
myfunction x y z = ...;

one can attach type annotations to the arguments (with the ScopedTypeVariables LANGUAGE pragma), but that Repeats Yourself even more:

myfunction :: Typea -> Typeb -> Typec -> Typed -> Typee;
myfunction (x :: Typea) (y :: Typeb) (z :: Typec) = ...;

it would be nice if there were a way to annotate the return type, then it would cover all the information in the type signature:

myfunction (x :: Typea) (y :: Typeb) (z :: Typec) (RETURNS :: Typed -> Typee) = ...;

function declarations in C keep type information closely attached to variables, including return type, albeit in C's confusing syntax for types.

here is a somewhat ugly way to accomplish this without needing new syntax (other than ScopedTypeVariables):

myfunction (x :: Typea) (y :: Typeb) (z :: Typec) = returns :: Typed -> Typee where returns = ...;

you'll need -Wno-missing-signatures to quell ghc's "Top-level binding with no type signature" warning.  of course, doing that deprives you of that warning when you actually don't have enough type annotation on a top-level binding, perhaps accidentally introducing more polymorphism than you intended.

because they are not top-level bindings, you can do this in let and where blocks with impunity.

if you do this nested, you'll get a warning (when -Wall) "The binding for 'returns' shadows the existing binding".  -Wno-name-shadowing quells the warning.  or, you could use different names for each returns variables, but that opens the possibility of using the wrong one with a typo.

type annotations on function arguments are not perfect.  for example, this compiles:

f (x :: [a]) (y :: [b]) = x++y;

if you need typeclass contexts or forall, this will also not work: use traditional type signatures for fancy type stuff.  but we can imagine a language extension that does allow fancy type features to work with type annotations on function arguments:

f (FANCYTYPESTUFF forall a m . (Context m)) (x :: m a) (y :: m a) = ...;

you'll also want traditional type signatures to attach documentation to function arguments in Haddock.

1 comment :

Unknown said...

See https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0228-function-result-sigs.rst and https://gitlab.haskell.org/ghc/ghc/-/issues/18203