【问题标题】:The argument types of an anonymous function must be fully known. (SLS 8.5)匿名函数的参数类型必须是完全已知的。 (SLS 8.5)
【发布时间】:2017-10-27 15:16:39
【问题描述】:

我有一个函数字面量

{case QualifiedType(preds, ty) =>
               t.ty = ty ;
               Some((emptyEqualityConstraintSet,preds)) }

这会导致错误消息

missing parameter type for expanded function The argument types of an anonymous function
must be fully known. (SLS 8.5) Expected type was:
? => Option[(Typer.this.EqualityConstraintSet, Typer.this.TypeRelationSet)]

我查看了SLS 8.5,但没有找到解释。

如果我自己将函数扩展为

{(qt : QualifiedType) =>
  qt match {case QualifiedType(preds, ty) =>
               t.ty = ty ;
               Some((emptyEqualityConstraintSet,preds)) }}

错误消失了。

(a) 为什么这是一个错误?

(b) 我能做些什么来解决它?

我尝试了明显的解决方法,即在模式和 => 之间添加 : QualifiedType,但这是一个语法错误。


我注意到的一件事是上下文有所不同。如果我使用函数文字作为声明为期望 QualifiedType => B 的函数的参数,则没有错误。但是,如果我将它用作期望 A => B 的函数的参数,则会出现错误。我希望这里发生的事情是,由于可以想象该模式可以应用于类型是 QualifiedType 的超类型的对象,编译器不愿意在不保证函数不会应用于的情况下分配明显的类型任何不是 QualifiedType 的东西。我真正想要的是能够写{QualifiedType( preds, ty) => ...} 并让它与 Haskell 的 \QualifiedType(preds,ty) -> ... 含义相同。

【问题讨论】:

  • 接受的答案仅解决问题 (a)。我的回答中回答了问题 (b)。

标签: scala lambda pattern-matching function-literal


【解决方案1】:

{ case X(x) => ... } 是一个偏函数,但编译器仍然不知道你的输入类型是什么,除了它是X 的超类型。通常这不是问题,因为如果您正在编写匿名函数,则类型是从上下文中知道的。但您可以通过以下方式提供类型:

case class Foo(x: Int)

// via annotation
val f: Foo => Int = { case Foo(x) => x }

// use pattern matching
val f = (_: Foo) match { case Foo(x) => x }

// or more normally, write as a method
def f(a: Foo) = a match { case Foo(x) => x }
def f(a: Foo) = a.x

您可能已经注意到,在这里使用函数字面量/模式匹配是毫无意义的。在您的情况下,您似乎只需要一个常规方法:

def whatever(qt: QualifiedType) = {
  t.ty = qt.ty
  Some((emptyEqualityConstraintSet, qt.preds)) 
}

尽管您应该重构以删除该可变状态。

【讨论】:

  • 谢谢。所有本地 def 都重复 Foo,这是我想要避免的。这似乎是多余的,但现在我明白为什么不是了。请参阅我的答案以获取解决方法。
  • 添加类型信息的更多选项(此处:直接添加到 case 子句本身):例如myListMap.map[Int,Int]( { case (x: Int, y: Int) => (x+1, y+2) } )
【解决方案2】:

这是SLS quote,我们其他人:

必须部分定义此类表达式的预期类型。它 对于某些 k > 0,必须是 scala.Functionk[S1, . . . , Sk, R],或者 scala.PartialFunction[S1, R],其中参数类型 S1,. . . , Sk 必须完全确定,但结果类型 R 可能是 未定。

否则,您回答了您的问题。

【讨论】:

  • 就我而言,在 for 循环 (map()) 的上下文中,函数的返回值需要一些说明。已编译:def aMethodRetSeq(root: T): Seq[U] 而未编译:def aMethodRetSeq(root: T): U
【解决方案3】:

这就是为什么我想使用函数字面量并且不喜欢重复输入两次的原因。我试图构建自己的控件构造来分解所有选项匹配代码。如果开销太大,那么控制构造就没有任何帮助。这就是我想做的事情

//This is the control construct definition
def switch[A,B]( x : Option[A], noneFun : =>B, someFun : A=>B) = x match {
    case None => noneFun 
    case Some(y) => someFun(y) }

//And this is an example of using it.
def foobar( qt : Option[QualifiedType]  ) = 
    switch( qt, {reportError("SNAFU");None},
            {case QualifiedType(preds, ty) =>
               Some((emptyEqualityConstraintSet,preds)) } ) 

开关控制结构编译得很好,但使用导致错误,因为 SLS 说我有一个 A,我应该有一个“确定的类型”。那是因为这种函数字面量(带有“case”的那种)是用于部分函数的,其中参数可以是合法的任何东西。我可以用一个 int 来争论我的函数文字,这不会是类型错误,而仅仅是所有模式都失败的问题。所以编译器需要一些“自上而下”的信息来知道我打算为“扩展函数文字”的参数使用什么类型,即在下面为 X 放置什么

{(x : X) => x match {case Some(QualifiedType(preds, ty)) =>
               Some((emptyEqualityConstraintSet,preds)) } }

我想知道为什么编译器不能使用 switch 的类型来查看我不打算使用偏函数,然后将 A 与 QualifiedType 统一起来。但事实并非如此。

无论如何它不会编译。但是用 Any 替换 A 可以消除错误。以下代码实际编译。我失去的是一些类型检查。

//This is the control construct definition
def switch[A,B]( x : Option[A], noneFun : =>B, someFun : A=>B) = x match {
    case None => noneFun 
    case Some(y) => someFun(y) }

//And this is an example of using it.
def foobar( qt : Option[QualifiedType]  ) = 
    switch( qt, {reportError("SNAFU");None},
            {case QualifiedType(preds, ty) =>
               Some((emptyEqualityConstraintSet,preds)) } ) 

我很想知道 (a) 是否可以改进上述 switch 的定义,以及 (b) 是否已经有一个库函数可以满足我的需求。


在 Luigi 的评论之后添加

这是最终代码。是的,我认为这是一个折叠(catamorphism)。

def switch[A,B]( x : Option[A])(noneFun : =>B, someFun : A=>B) = x match {
    case None => noneFun 
    case Some(y) => someFun(y) }

def foobar( qt : Option[QualifiedType]  ) : Option[(EqualityConstraintSet, TypeRelationSet)] =
    switch( qt )({reportError("SNAFU");None},
            {case QualifiedType(preds, ty) =>
               Some((emptyEqualityConstraintSet,preds)) } ) 

感谢路易吉。

【讨论】:

  • 尝试将switch 的参数列表一分为二,以便在您的函数中推断A 的类型。您仍然不需要使用提取器。现有的构造是 option.map(function if some).getOrElse(function if none),但 Scala 2.10 在 Option 上有一个 fold 方法来做同样的事情。
  • 非常感谢。请参阅我对我的答案的编辑中的解决方案。有趣的是,柯里化对打字有如此强烈的影响。
  • 所以最后我只是重新发明了Option.fold。 (我想我当时还在使用 2.09,这就是为什么我没有找到fold 开始的原因。)有些人不喜欢Option.fold,因为它与fold 不一样。元素序列。其他人是因为他们更喜欢相反的论点。在我看来,参数顺序与 Seq.fold 的参数顺序相似,并且在长度为 1 的序列上它应该与 fold 不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-12
  • 2021-06-18
  • 2019-01-24
  • 2020-06-20
  • 1970-01-01
  • 2020-10-31
  • 2023-04-05
相关资源
最近更新 更多