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:
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
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')
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With