简体   繁体   English

Haskell 隐式参数和多态递归

[英]Haskell Implicit parameters and polymorphic recursion

I have a question on " Implicit parameters and polymorphic recursion " chapter of GHC User Guide.我对 GHC 用户指南的“ 隐式参数和多态递归”一章有疑问。

The code is代码是

len1 :: [a] -> Int
len1 xs = let ?acc = 0 in len_acc1 xs

len_acc1 [] = ?acc
len_acc1 (_:xs) = let ?acc = ?acc + (1::Int) in len_acc1 xs

------------

len2 :: [a] -> Int
len2 xs = let ?acc = 0 in len_acc2 xs

len_acc2 :: (?acc :: Int) => [a] -> Int
len_acc2 [] = ?acc
len_acc2 (_:xs) = let ?acc = ?acc + (1::Int) in len_acc2 xs

The chapter says章节说

In the former case, len_acc1 is monomorphic in its own right-hand side, so the implicit parameter?acc is not passed to the recursive call.在前一种情况下,len_acc1 在它自己的右手边是单态的,所以隐式参数?acc 没有传递给递归调用。 In the latter case, because len_acc2 has a type signature, the recursive call is made to the polymorphic version, which takes?acc as an implicit parameter.在后一种情况下,由于 len_acc2 具有类型签名,因此对多态版本进行递归调用,该版本将?acc 作为隐式参数。

The questions are问题是

  • Does "monomorphic in its own right-hand side" mean in this case the type len_acc1:: (?acc:: Int) => [a] -> p ?在这种情况下,“在其自己的右侧单态”是否意味着类型len_acc1:: (?acc:: Int) => [a] -> p Why does ghci say len_acc1:: (?acc::Int) => [a] -> Int ?为什么 ghci 说len_acc1:: (?acc::Int) => [a] -> Int

  • Why the first function is monomorphic and the second is not?为什么第一个 function 是单态的,而第二个不是? If my understanding had been correct, it would be vice versa.如果我的理解是正确的,反之亦然。

  • Or may be it means that the type is len_acc1:: [a] -> Int , but each case refers to ?acc implicit value, so I believe the type should mention (?acc:: Int) constraint.或者可能意味着类型是len_acc1:: [a] -> Int ,但是每种情况都引用?acc隐式值,所以我认为类型应该提到(?acc:: Int)约束。

  • How that monomorphism implies that the implicit parameter is not passed to the function?这种单态性如何暗示隐式参数没有传递给 function?

In Haskell, we often refer to type signatures with type variables as polymorphic, like the polymorphic function id whose type signature here includes the type variable a :在 Haskell 中,我们经常将带有类型变量的类型签名称为多态,例如多态 function id ,其类型签名在这里包含类型变量a

id :: a -> a

However, this signature is polymorphic not because it includes the type variable a but because that variable is quantified .但是,这个签名是多态的,不是因为它包含类型变量a而是因为该变量是quantified In Haskell, type signatures are implicitly universally quantified, so the above is equivalent to:在 Haskell 中,类型签名被隐式地普遍量化,所以上面等价于:

id :: forall a. a -> a

which is actually accepted syntax if you turn on the ExplicitForAll extension.如果您打开ExplicitForAll扩展,这实际上是可接受的语法。 It's this quantification that makes the type polymorphic.正是这种量化使类型具有多态性。

When Haskell is typing bindings without a type signature, it uses the Hindley-Milner typing algorithm to assign types.当 Haskell 键入没有类型签名的绑定时,它使用 Hindley-Milner 键入算法来分配类型。 This algorithm works by assigning monomorphic type signatures to sets of mutually-dependent bindings and then refining them by determining the relationships between them.该算法通过将单态类型签名分配给相互依赖的绑定集,然后通过确定它们之间的关系来细化它们。 These signatures are allowed to contain type variables (,).这些签名允许包含类型变量 (,)。 but they are still considered monomorphic because the variables aren't quantified, That is, the variables are imagined to stand for specific types, we just haven't figured them out yet.但是它们仍然被认为是单态的,因为变量没有被量化,也就是说,变量被想象为代表特定的类型,我们只是还没有弄清楚它们。 because we're in the middle of type-checking.因为我们正在进行类型检查。

Once type checking completes, and the "final" monomorphic types have been assigned, there's a separate generalization step.一旦类型检查完成,并且分配了“最终”单态类型,就会有一个单独的泛化步骤。 All the type variables that remain in the monomorphic types, that haven't been determined to be fixed types like Int and Bool , are generalized by universal quantification.保留在单态类型中的所有类型变量,尚未确定为IntBool等固定类型的类型变量,均通过全称量化进行泛化。 That step determines the final, polymorphic types of the bindings.该步骤确定绑定的最终多态类型。 Constraints are part of the quantification process and so are only applied to type signatures at the generalization step when we move from monomorphic to polymorphic types.约束是量化过程的一部分,因此当我们从单态类型转移到多态类型时,仅在泛化步骤中应用于类型签名。

The documentation is referring to the fact that, when inferring len_acc1 's type, the algorithm assigns it a monomorphic type, ultimately the final monomorphic type len_acc1:: [a] -> Int with a free (ie, unquantified) type variable a .该文档指的是这样一个事实,即在推断len_acc1的类型时,该算法为其分配了一个单态类型,最终是最终的单态类型len_acc1:: [a] -> Int和一个自由(即未量化)类型变量a While the type checker will note that an (?acc:: Int) constraint is demanded, it doesn't make this part of the inferred type of len_acc1 .虽然类型检查器会注意到需要(?acc:: Int)约束,但它不会使这部分成为len_acc1的推断类型。 In particular, the type assigned to the recursive call of len_acc1 is the monomorphic type [a] -> Int with no constraint.特别是,分配给len_acc1递归调用的类型是单态类型[a] -> Int ,没有约束。 It's only after this final monomorphic type for len_acc1 has been determined, is it generalized by quantifying over a and adding the constraints, to yield the final top-level signature.只有在确定了len_acc1的最终单态类型之后,才通过量化a并添加约束对其进行泛化,以产生最终的顶级签名。

In contrast, when a top-level polymorphic signature is provided:相反,当提供顶级多态签名时:

len_acc2 :: forall a. (?acc :: Int) => [a] -> Int

the binding len_acc2 is polymorphic (together with its associated constraint) everywhere it's used, in particular in the recursive call.绑定len_acc2在任何使用它的地方都是多态的(连同其相关的约束),特别是在递归调用中。

The result is that len_acc1 is recursively called monomorphically, without a constraint dictionary supplying a (new) ?acc parameter value, while len_acc2 is called polymorphically, with a constraint dictionary for the new ?acc value.结果是len_acc1态递归调用,没有提供(新) ?acc参数值的约束字典,而len_acc2被多态调用,具有新?acc值的约束字典。 This situation is, I believe, unique to implicit parameters.我相信,这种情况是隐式参数所独有的。 You wouldn't otherwise encounter a situation where a recursive call could be typed both monomorphically and polymorphically in such a way that the code would behave differently under the two typings.否则,您不会遇到递归调用可以单态和多态键入的情况,这样代码在两种类型下的行为会有所不同。

You'd be more likely to run into a situation like the following, where an "obvious" type signature is required because no monomorphic type can be inferred:您更有可能遇到如下情况,其中需要“明显”的类型签名,因为无法推断出单态类型:

data Binary a = Leaf a | Pair (Binary (a,a)) deriving (Show)

-- following type signature is required... 
-- toList :: Binary a -> [a]
toList (Leaf x) = [x]
toList (Pair b) = concatMap (\(x,y) -> [x,y]) (toList b)

main = print $ toList (Pair (Pair (Leaf ((1,2),(3,4)))))

I can only answer the question partially.我只能部分回答这个问题。

Why does ghci say len_acc1:: (?acc::Int) => [a] -> Int ?为什么 ghci 说len_acc1:: (?acc::Int) => [a] -> Int

It's Int because the first clause returns ?acc and it can be inferred to be Int from ?acc + (1::Int) in the second clause.这是Int因为第一个子句返回?acc并且可以从第二个子句中的?acc + (1::Int)推断为Int

"in... right-hand side" “在……右手边”

means the part after = , which has type Int as above, which is monomorphic.表示=之后的部分,其类型为Int如上所述,它是单态的。 len_acc1:: (?acc:: Int) => [a] -> p would be poly -morphic. len_acc1:: (?acc:: Int) => [a] -> p将是态的。

Or may be it means that the type is len_acc1:: [a] -> Int, but each case refers to?acc implicit value, so I believe the type should mention (?acc:: Int) constraint.或者可能意味着类型是 len_acc1:: [a] -> Int,但是每个 case 都引用了?acc 隐式值,所以我认为类型应该提到 (?acc:: Int) 约束。

This is what it seems to be saying to me;这似乎是对我说的; that at least temporarily it's treated as having that type.至少暂时将其视为具有该类型。 But then I don't understand why it compiles at all.但是我不明白为什么它会编译。

I've tried to read the paper where implicit parameters were defined, but didn't find the answer.我试图阅读定义隐式参数的论文,但没有找到答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM