【问题标题】:Scala Multiple type parameter that are defined laterScala 后面定义的多类型参数
【发布时间】:2017-01-11 20:31:55
【问题描述】:

我正在尝试将 Haskell 的通用库移植到 Scala。但是,我目前对如何解决 Scala 中的通用 Crush 函数并不满意。

我定义了以下处理 Crush 功能的方法。

trait FRep[G[_],F[_]]{
    def frep[A](g1 : G[A]) : G[F[A]]
  } 

trait Crush[B,A]{ 
    def selCrush : Assoc => A => B => B
}

接下来,我想定义crush函数,但在这里我遇到了问题。问题是我需要这个 FRep 特征来表示 Crush 函数,但是 frep 中的 G(Generic) 只允许 1 个参数。我使用 lambda 类型解决了这个问题,但在定义函数时仍然存在一些问题。这是我想要工作的方法:

def crush[B,A,F[_]](asc : Assoc)(f : A => B => B)(z : B)(x : F[A])(implicit rep : FRep[({type AB[A] = Crush[B,A]})#AB,F]): B = {
    def fCrush = new Crush[B,A]{ 
      override def selCrush= _ => f
    }
    return(rep frep(fCrush).selCrush(asc)(x)(z))
}

这显然报错了,因为crush函数中的A参数与隐式rep变量中的A lambda类型不一样,需要相同才能使用Crush函数。这是我收到的错误:

<pastie>:677: error: type mismatch;
 found   : x.type (with underlying type F[A])
 required: A
       return(rep frep(fCrush).selCrush(asc)(x)(z))

所以,我想出的解决方案是将粉碎函数拆分为更多部分,这样我就可以将相同的 A 用于粉碎函数。这是当前编译的解决方案:

class CrushFunction[B,F[_]](asc : Assoc)(z : B)(implicit rep : FRep[({type AB[A] = Crush[B,A]})#AB,F]){
    def crush[A](f : A => B => B)(x : F[A]) : B = {
        val crushVal = new Crush[B,A]{
            override def selCrush:  Assoc => A => B => B = _ => f
        }
        return(rep.frep(crushVal).selCrush(asc)(x)(z))
    }
}

所以,我的问题是:有没有更好的方法来解决这个问题?

【问题讨论】:

    标签: scala generics type-conversion generic-programming


    【解决方案1】:

    实际上,您的第一个解决方案的唯一问题是您遗漏了repfrep 之间的点。我还建议您不要显式使用return,也不要隐藏类型参数的名称(A):

    def crush[B,A,F[_]](asc : Assoc)(f : A => B => B)(z : B)(x : F[A])(implicit rep : FRep[({type AB[X] = Crush[B,X]})#AB,F]): B = {
        def fCrush = new Crush[B,A]{ 
          override def selCrush: Assoc => A => B => B = _ => f
        }
        rep.frep(fCrush).selCrush(asc)(x)(z)
    }
    

    现在您可能会问自己:如果所有错误都是缺少点,为什么会出现此错误消息?好吧,scala 支持中缀表示法。它将a b c d e 解析为a.b(c).d(e)。但是你写了这样的东西:a b(c).d(e)。这使解析器感到困惑。您可以看到将//print 附加到 REPL(我认为是 2.11.8 或更高版本)中的代码并按 TAB 键时会发生什么。

    我为你过滤掉了所有的垃圾。 rep frep(fCrush).selCrush(asc)(x)(z) 被解析为:

    rep.frep[A](fCrush.selCrush(asc)(x)(z))
    

    在该表达式中,x 确实需要为A 类型而不是F[A]

    老实说:对我来说,这似乎有点像 scalac 设法解析您的无点代码的错误,完全忽略了过程中的括号。 让我们称之为不幸的语法怪癖:-)

    【讨论】:

    • -Xlint 用于阴影类型参数。你将如何解析 "hello" +("world").length ?我认为他们想添加一个注释来指示哪些成员(运算符)可以被中缀使用。这是为了提高可读性,但也许 Abide 规则可以警告 alnum 中缀后面没有空格(等)。
    • 你是对的。这样可行!感谢您的精彩解释!
    猜你喜欢
    • 2011-01-04
    • 2023-02-02
    • 2019-06-01
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    • 2012-04-11
    相关资源
    最近更新 更多