[英]Type synonyms unwrapping in functions?
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. Simon Marlow的阅读书“ Haskell中的并行和并发编程 ”我遇到了一个我不确定它为什么会像GHC看到的那样工作的东西。 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)
. 在类型级别和一个输出上有两个输入参数(我将其
Strategy a
和Strategy b
) - Strategy (a,b)
。 But line under there are three arguments: sa
, sb
and (a,b)
. 但是下面的行有三个参数:
sa
, sb
和(a,b)
。 It is confusing. 这令人困惑。
But since Strategy
is type synonym: 但由于
Strategy
是类型同义词:
type Strategy a = a -> Eval a
I thought that maybe if I unroll Strategies to (a -> Eval a) it will be clearer. 我想也许如果我将策略展开(a - > Eval a)它会更清楚。 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)
. 然后我用这种方式编写它并且感谢Stack工具提示发现从函数evalPair返回的内容(故意回到策略以使其更加混乱)不是
Strategy (a,b)
而是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
很明显,编译器从其类型同义词中解开了最后一个参数,但只是最后一个 - 因为我们不需要为
Strategy a
和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
为什么函数返回
Eval a
即使它表示它返回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: 如果解缠发生,那么为什么我不需要(实际上不能)为
Strategy a
和Strategy b
提供这样的值:
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: 我们可以通过将同义词的每次使用替换为其定义来“desugar”类型:
(a -> Eval a) -> (b -> Eval b) -> ((a,b) -> Eval (a,b))
Note that the parens are necessary here to clarify what's going on. 请注意,这里必须使用parens来澄清发生了什么。 The function
evalPair
still takes two arguments. 函数
evalPair
仍然有两个参数。 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
. 因此
sa
的类型是a -> Eval a
, sb
的类型是b -> Eval b
。
Note that the Haskell Report states: 请注意,Haskell报告指出:
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 您可以在Haskell报告的4.2.2节中阅读有关类型同义词的内容: https : //www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-730004.2.2
You are misunderstanding how Haskell functions work. 您误解了Haskell函数的工作原理。
All Haskell functions have only one argument . 所有Haskell函数只有一个参数 。 If a function
f
takes a value of type A
, and returns something of type B
, we write f :: A -> B
. 如果函数
f
采用类型A
的值,并返回类型B
东西,我们写f :: A -> B
However, of course, we can write functions with multiple arguments, but in a clever way, called currying . 但是,当然,我们可以用多个参数编写函数,但是以一种聪明的方式,称为currying 。 If we want a function
g
that takes an A
and a B
, and produces a C
, we actually write a function that takes an A
, and then returns a function that takes a B
and returns a C
. 如果我们想要一个带有
A
和B
的函数g
,并产生一个C
,我们实际上写一个带A
的函数,然后返回一个带B
的函数并返回一个C
We write this as g :: A -> (B -> C)
. 我们把它写成
g :: A -> (B -> C)
。 However, since this is what we usually want to do with functions, the Haskell compiler will read the signature g :: A -> B -> C
as the same thing. 但是,由于这是我们通常想要的函数,Haskell编译器将读取签名
g :: A -> B -> C
作为相同的东西。
This means that when we write A -> B -> C -> ... -> Z
, we really mean A -> (B -> (C -> (... -> Z...)))
. 这意味着当我们写
A -> B -> C -> ... -> Z
,我们的意思是A -> (B -> (C -> (... -> Z...)))
。
However, if we write g :: (A -> B) -> C
, this is not the same thing! 但是,如果我们写
g :: (A -> B) -> C
, 这不是一回事! Brackets matter! 括号很重要!
g
is now a function that takes a function and produces a C
. g
现在是一个函数,它接受一个函数并产生一个C
Now, let's inspect this specific example: 现在,让我们检查一下这个具体的例子:
evalPair :: (a -> Eval a) -> (b -> Eval b) -> (a,b) -> Eval (a,b)
I read this as: 我读到这个:
The function
evalPair
takes the following:evalPair
函数采用以下方法:
- Something of type
a -> Eval a
类型
a -> Eval a
- Something of type
b -> Eval b
类型为
b -> Eval b
东西b -> Eval b
- Something of type
(a,b)
某种类型
(a,b)
And produces something of type
Eval (a,b)
.并产生
Eval (a,b)
类型的东西。
So the declaration would naturally look something like 所以声明自然会像
evalPair first_arg second_arg third_arg = -- ...
Writing evalPair a sa a sb (a,b) = -- ...
is nonsensical, since that takes 5 arguments, and thus evalPair
would have a type of the form a -> b -> c -> (d, e) -> f
, which is completely different to what we expect. 写
evalPair a sa a sb (a,b) = -- ...
是荒谬的,因为它需要5个参数,因此evalPair
将有一个类型的形式a -> b -> c -> (d, e) -> f
,这与我们的期望完全不同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.