【问题标题】:Underscore in Named Arguments命名参数中的下划线
【发布时间】:2023-06-18 19:57:02
【问题描述】:

谁能解释一下这里到底发生了什么?我现在还没有完全进入它:

val s = Seq(1D,2D,3D,4D)
case class WithUnit(value: Double, unit: String)
s map { WithUnit(_,"cm") } // works
s map { WithUnit(value = _ , unit = "cm") } // error: missing parameter type for expanded function ((x$2) => value = x$2)

我猜编译器无法推断参数类型,因为我写了参数的名称。但为什么不呢?不应该仅仅因为陈述论点的名称而变得更加困难吗?!

谢谢!

【问题讨论】:

    标签: scala scala-placeholder-syntax


    【解决方案1】:

    你写的时候:

     WithUnit(value = _, unit = "cm")
    

    你想要的意思是:

     x => WithUnit(value = x, unit = "cm")
    

    但是如果你仔细看一下错误信息,你会发现编译器并没有这样看,它把它解析为:

     WithUnit(x => value = x, unit = "cm"})
    

    如您所见,_ 的范围比您想要的更严格。

    _ 总是选择最紧凑的非退化范围。范围在解析过程中纯粹由语法确定,与类型无关。

    通过非退化,我的意思是编译器没有认为你的意思是:

    WithUnit(value = x => x, unit = "cm")
    

    最紧密的非退化作用域是指由最里面的函数括号相对于下划线定义的作用域。如果没有这样的规则,当函数调用嵌套时,编译器将无法知道哪个 _ 对应于哪个函数。

    【讨论】:

    • 啊,我明白了!现在我有了更深的理解……最严格的非退化范围……我会记住的!谢谢!
    • 可以说,这是一个错误。 WithUnit(_, "cm") 之所以有效,是因为它被视为部分应用的函数,而不是匿名函数文字。当与命名参数一起使用时,它不会得到同样的处理,我怀疑这只是一个疏忽。
    • @Daniel:我认为您在“部分应用函数”和“匿名函数文字”之间的区别并不存在。
    • 可以说,这是一个错误,因为f(id = e) 不再意味着分配。以前的语法是“赋值或命名arg”,现在只有“命名arg”,表达式为_,OP的直观读法是正确的。
    【解决方案2】:

    试试这个:

    scala> val withUnits = s map { x => WithUnit(value = x, unit = "cm") }
    withUnits: Seq[WithUnit] = List(WithUnit(1.0,cm), WithUnit(2.0,cm), WithUnit(3.0,cm), WithUnit(4.0,cm))
    

    问题在于使用下划线直接定义任意函数。

    详细说明在chapter 8.5. Placeholder syntax in the "Programming in Scala" book

    【讨论】:

    • 感谢您的链接,有时会忘记查看圣经;)