两个看似相同的字符串表现不同

时间:2016-09-16 22:04:51

标签: scala

请查看以下编译良好的代码:

trait ParserError
trait Parser[+A]{
  def run[A](input:A):Either[ParserError, A]
}

object Parser{
  case object ParserErrorImpl extends ParserError

  def string(s:String):Parser[String] = new Parser[String]{
    def run[String](input:String) = {
      if(input == s) Right(input) else Left(ParserErrorImpl)
    }
  }
}

object Runner{
  import Parser._
  def main(args:Array[String]):Unit={
    println(string("aaa").run("aaa"))
  }
}

当我将Right(input)更改为Right(s)时,会出现编译错误:

Error:(13, 58) type mismatch;
 found   : s.type (with underlying type String)
 required: String
    def run[String](input:String) = if(input == s) Right(s) else Left(ParserErrorImpl)
                                                         ^

你能解释为什么会发生这种情况,因为我不完全清楚这一点吗?

3 个答案:

答案 0 :(得分:3)

您正在通过(重新)在run方法中定义类型参数A来隐藏它。使特征Parser按类型A参数化,并将该类型用作run方法的参数类型。

这是固定版本:

trait ParserError
trait Parser[A]{
  def run(input:A):Either[ParserError, A] // <-- not run[A] !!!
}

object Parser{
  case object ParserErrorImpl extends ParserError

  def string(s:String):Parser[String] = new Parser[String]{
    def run(input:String) = {
      if(input == s) Right(input) else Left(ParserErrorImpl)
    }
  }
}

原因是您无法将已实现方法的类型设置为String。你想这样做,但实际发生的是&#34; String&#34;只是方法类型的另一个标识符;它也可能是&#34; Strings&#34;或&#34; Stringozoid&#34;。请参阅@Tzach Zohar的回答,以获得更丰富的解释。

另外,我删除了协方差以使事情更简单,但是你可能想要保留它,所以你需要做一个好的旧的下界技巧。请查看@Leif Ericson的答案以获得完整的解决方案。

答案 1 :(得分:3)

当覆盖类型参数化方法时,不能使用String将类型“设置”为def methodName[String] - 无论你“传递”什么,因为类型参数实际上不是解释为参数 value ,而是作为新参数名称 。在这种情况下,在:

def string(s:String):Parser[String] = new Parser[String]{
  def run[String](input:String) = { 
    if(input == s) Right(input) else Left(ParserErrorImpl)
  }
}

当您撰写def run[String]时,您正在创建类型参数名为“字符串”,而不是使用{{1}的重写方法}。现在,scala.Predef.String保证具有该类型(无论它是什么,可以是Int!),但input不是。

要看到这一点 - 尝试在原始编译版本中使用不同的名称而不是“String” - 并看到它仍然可以编译:

s

要解决此问题 - 在这种情况下,您可以删除方法的类型参数并使用@slouc建议的类的参数 - 您将它们命名为def string(s:String):Parser[String] = new Parser[String]{ def run[SomeUnknownType](input:SomeUnknownType) = { if(input == s) Right(input) else Left(ParserErrorImpl) } } ,但这是两个不同的参数。

答案 2 :(得分:2)

你实际上可以保持协方差,同时仍然编译代码:

trait ParserError

trait Parser[+A] {
  def run[B >: A](input: B): Either[ParserError, B]
}

object Parser {

  case object ParserErrorImpl extends ParserError

  def string(s: String): Parser[String] = new Parser[String] {
    def run[B >: String](input: B) = {
      if (input == s) Right(s) else Left(ParserErrorImpl)
    }
  }
}

object Runner {

  import Parser._

  def main(args: Array[String]): Unit = {
    println(string("aaa").run("aaa"))
  }
}