gpt4 book ai didi

scala - 两个看似相同的语义 : one binds implicitly, 另一个没有

转载 作者:行者123 更新时间:2023-12-01 09:06:56 24 4
gpt4 key购买 nike

你好:我最近一直在学习 Scala(我的相关背景主要是 C++ 模板),我遇到了一些我目前对 Scala 不了解的东西,这让我发疯了。 :(

(另外,这是我在 StackOverflow 上的第一篇文章,我注意到大多数非常棒的 Scala 人似乎都在闲逛,所以如果我对机制做了一些非常愚蠢的事情,我真的很抱歉。)

我的具体困惑与隐式参数绑定(bind)有关:我提出了一个特定情况,即隐式参数拒绝绑定(bind),但具有看似相同语义的函数却可以。

现在,它当然可能是编译器错误,但鉴于我刚刚开始使用 Scala,我已经遇到某种严重错误的可能性非常小,我期待有人解释我的问题做错了。 ;P

我已经浏览了代码并对其进行了相当多的修改,以便提出一个不起作用的示例。不幸的是,这个例子仍然相当复杂,因为这个问题似乎只出现在泛化中。 :(

1) 没有按我预期的方式工作的简化代码

import HList.::

trait HApplyOps {
implicit def runNil
(input :HNil)
(context :Object)
:HNil
= {
HNil()
}

implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(context :Object)
(implicit run :Input=>Object=>Output)
:Int::Output
= {
HCons(0, run(input.tail)(context))
}

def runAny[Input <:HList, Output <:HList]
(input :Input)
(context :Object)
(implicit run :Input=>Object=>Output)
:Output
= {
run(input)(context)
}
}

sealed trait HList

final case class HCons[Head, Tail <:HList]
(head :Head, tail :Tail)
extends HList
{
def ::[Value](value :Value) = HCons(value, this)
}

final case class HNil()
extends HList
{
def ::[Value](value :Value) = HCons(value, this)
}

object HList extends HApplyOps {
type ::[Head, Tail <:HList] = HCons[Head, Tail]
}

class Test {
def main(args :Array[String]) {
HList.runAny( HNil())(null) // yay! ;P
HList.runAny(0::HNil())(null) // fail :(
}
}

这段代码用 Scala 2.9.0.1 编译,返回以下错误:

broken1.scala:53: error: No implicit view available from HCons[Int,HNil] => (java.lang.Object) => Output.
HList.runAny(0::HNil())(null)

在这种情况下,我的期望是 runAll 将绑定(bind)到 runAny 的隐式 run 参数。

现在,如果我修改 runAll 使其不是直接获取它的两个参数,而是返回一个函数,该函数又接受这两个参数(我想尝试的技巧,因为我看到它在别人的代码中),它可以工作:

2) 修改后的代码具有相同的运行时行为并实际运行

    implicit def runAll[Input <:HList, Output <:HList]
(implicit run :Input=>Object=>Output)
:Int::Input=>Object=>Int::Output
= {
input =>
context =>
HCons(0, run(input.tail)(context))
}

本质上,我的问题是:为什么会这样? ;( 我希望这两个函数具有相同的整体类型签名:

1: [Input <:HList, Output <:HList] (Int::Input)(Object):Int::Output
2: [Input <:Hlist, Output <:HList] :Int::Input=>Object=>Int::Output

如果它有助于理解问题,其他一些改变也“起作用”(尽管这些改变了函数的语义,因此不是可用的解决方案):

3) 通过将 Output 替换为 HNil

,仅对第二级硬编码 runAll
    implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(context :Object)
(implicit run :Input=>Object=>HNil)
:Int::HNil
= {
HCons(0, run(input.tail)(context))
}

4) 从隐式函数中移除上下文参数

trait HApplyOps {
implicit def runNil
(input :HNil)
:HNil
= {
HNil()
}

implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(implicit run :Input=>Output)
:Int::Output
= {
HCons(0, run(input.tail))
}

def runAny[Input <:HList, Output <:HList]
(input :Input)
(context :Object)
(implicit run :Input=>Output)
:Output
= {
run(input)
}
}

任何人对此可能有的解释将不胜感激。 :(

(目前,我最好的猜测是隐式参数相对于其他参数的顺序是我遗漏的关键因素,但我对此感到困惑:runAny最后也有一个隐式参数,所以明显的“implicit def不能很好地与尾随implicit一起工作”对我来说没有意义。)

最佳答案

当您像这样声明 隐式 def 时:

implicit def makeStr(i: Int): String = i.toString

然后编译器可以根据这个定义自动为您创建一个隐式 Function 对象,并将其插入到需要隐式 Int => String 类型的位置。这就是您的 HList.runAny(HNil())(null) 行中发生的情况。

但是当你定义 implicit defs 自己接受隐式参数(如你的 runAll 方法)时,它不再起作用,因为编译器无法创建Function 对象,其 apply 方法需要一个隐式 - 更不用说保证调用站点可以使用这种隐式了。

解决方案是定义类似这样的东西而不是 runAll:

implicit def runAllFct[Input <: HList, Output <: HList]
(implicit run: Input => Object => Output):
Int :: Input => Object => Int :: Output =
{ input: Int :: Input =>
context: Object =>
HCons(0, run(input.tail)(context))
}

这个定义更明确一点,因为编译器现在不需要尝试从你的 def 创建一个 Function 对象,而是直接调用你的def来获取需要的函数对象。并且,在调用它时,会自动插入所需的隐式参数,它可以立即解析。

在我看来,每当您期望这种类型的隐式函数时,您都应该提供一个确实返回 Function 对象的 implicit def。 (其他用户可能不同意……任何人?)编译器能够围绕 implicit def 创建 Function 包装器的事实主要是为了支持隐式转换更自然的语法,例如使用 view bounds连同简单的 implicit defs,比如我的第一个 IntString 的转换。

关于scala - 两个看似相同的语义 : one binds implicitly, 另一个没有,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6173815/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com