Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type synonyms unwrapping in functions?

Tags:

haskell

Reading book by Simon Marlow "Parallel and Concurrent Programming in Haskell" I encountered a thing I am not sure about why it works the way GHC sees it. Namely:

evalPair :: Strategy a -> Strategy b -> Strategy (a,b)
evalPair sa sb (a,b) = do
  a' <- sa a
  b' <- sb b
  return (a',b')

Has two input arguments (the way I see it Strategy a and Strategy b) on type level and one output - Strategy (a,b). But line under there are three arguments: sa, sb and (a,b). It is confusing.

But since Strategy is type synonym:

type Strategy a = a -> Eval a

I thought that maybe if I unroll Strategies to (a -> Eval a) it will be clearer. So:

evalPair :: (a -> Eval a) -> (b -> Eval b) -> (a,b) -> Eval (a,b)

and (brackets added on the end)

evalPair :: (a -> Eval a) -> (b -> Eval b) -> ((a,b) -> Eval (a,b))

both compile.

Then I wrote it this way and thanks to Stack tooltips found that what is returned from function evalPair (went back to Strategy on purpose to make it more confusing) is not Strategy (a,b) but Eval (a,b).

evalPair :: Strategy a -> Strategy b -> Strategy (a,b)
evalPair sa sb (a,b) = 
let res = do
          a' <- sa a
          b' <- sb b
          return (a', b')
in res

So clearly compiler unwrapped last argument from its type synonym, but only the last one - since we do not need to provide values for Strategy a and Strategy b

Here are my questions:

  1. Where can I get more information on this behaviour of the compiler? Why the function returns Eval a even though it says that it returns Strategy a

  2. If unwrapping happens, then why don't I need (and can't in fact) provide values for Strategy a and Strategy b like so:

    evalPair :: Strategy a -> Strategy b -> Strategy (a,b)
    evalPair a sa a sb (a,b) = do
      a' <- sa a
      b' <- sb b
      return (a',b')
    
like image 247
GrayCat Avatar asked Jan 27 '23 09:01

GrayCat


1 Answers

Given the type synonym

type Strategy a = a -> Eval a

And the type

Strategy a -> Strategy b -> Strategy (a,b)

We can "desugar" the type by replacing each use of the synonym with its definition:

(a -> Eval a) -> (b -> Eval b) -> ((a,b) -> Eval (a,b))

Note that the parens are necessary here to clarify what's going on. The function evalPair still takes two arguments. Its two arguments are two functions. This may be clearer if I visually align the types with their corresponding arguments like so:

evalPair :: (a -> Eval a) -> (b -> Eval b) -> (a,b) -> Eval (a,b)
evalPair    sa               sb               (a,b)  = ...

Therefore the type of sa is a -> Eval a, and the type of sb is b -> Eval b.

Note that the Haskell Report states:

Type synonyms are a convenient, but strictly syntactic, mechanism to make type signatures more readable. A synonym and its definition are completely interchangeable

Therefore, the compiler may freely "wrap" or "unwrap" type synonyms, as they are "completely interchangeable".

You can read about Type Synonyms in the Haskell Report, section 4.2.2: https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-730004.2.2

like image 148
Dan Burton Avatar answered Feb 04 '23 01:02

Dan Burton