【问题标题】:Could not find implicit value for parameter x找不到参数 x 的隐式值
【发布时间】:2015-01-27 02:46:13
【问题描述】:

就在我以为我理解了 Scala 类型系统的基础知识的时候...:/

我正在尝试实现一个读取文件内容并输出一组记录的类。一条记录可能是一行,但也可能是一个字节块或任何东西。所以我追求的是一种结构,它允许 Reader 的类型暗示 Record 的类型,这反过来又暗示要使用正确的 Parser。

只要MainApp.records(f) 只返回一种类型的阅读器,这个结构就可以工作。只要它可以返回更多,我就会收到此错误:

找不到参数解析器的隐式值

我认为问题在于顶部的类型化特征定义,但我不知道如何解决这个问题......

// Core traits
trait Record[T]
trait Reader[T] extends Iterable[Record[T]]
trait Parser[T] {
  def parse(r: Record[T]): Option[Int]
}

// Concrete implementations
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] { 
  val lines = Source.fromFile(f).getLines()
  def iterator: Iterator[LineRecord[T]] =
    new Iterator[LineRecord[T]] {
      def next() = new LineRecord[T]
      def hasNext = lines.hasNext
    }
}
trait TypeA
object TypeA {
    implicit object TypeAParser extends Parser[TypeA] {
        def parse(r: Record[TypeA]): Option[Int] = ???
    }
}
trait TypeB
object TypeB {
    implicit object TypeBParser extends Parser[TypeB] {
        def parse(r: Record[TypeB]): Option[Int] = ???
    }
}

// The "app"
object MainApp {
  def process(f: File) =
    records(f) foreach { r => parse(r) }

  def records(f: File) = {
    if(true)
      new FileReader[TypeA](f)
    else
      new FileReader[TypeB](f)   
  }

  def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
      parser.parse(r)
}

【问题讨论】:

    标签: scala types implicit


    【解决方案1】:

    首先你必须导入隐式对象才能使用它们:

    import TypeA._
    import TypeB._
    

    但这还不够。似乎您正在尝试动态应用隐式。那是不可能的;它们必须在编译时找到。

    如果您按上述方式导入对象并更改记录,以便编译器找到正确的泛型,它将正常运行:

    def records(f: File) = new FileReader[TypeA](f)
    

    但它可能不是你想要的;)

    【讨论】:

    • 据我所见,操作的代码旨在实现基于隐式的编译时多态化 - 可以通过使用类型分类
    • 谢谢,但这不是我想要的。 :) 你是对的;我正在尝试动态返回隐式......也许它回到了绘图板!
    • @dk14 查看records 中的 if 语句。在此之后,直到程序运行才知道类型参数。
    • 类型类不可能,假设标志 true 不是静态常量。顺便说一句,在运行时使用 mix-ins 仍然可以进行动态调度 - 可以通过标志选择混合特征并包含其自己的隐式值。
    • new FileReader(f) with TypeA(用于隐式运行时多态)
    【解决方案2】:

    问题是您的records 方法的返回类型基本上是FileReader[_](因为它可以返回FileReader[TypeA]FileReader[TypeB]),并且您没有提供Parser[Any] 类型的隐式参数.如果您删除if-表达式,则返回类型将被推断为FileReader[TypeA],这可以正常工作。我不确定您要做什么,但显然编译器无法根据仅在运行时知道的类型选择隐式参数。

    【讨论】:

    • 是的,准确地说是输入_ >: TypeA with TypeB <: Object!我基本上是在尝试实现某种适配器模式。 :S
    【解决方案3】:

    1) 使用带有隐式内部的类型作为类型参数 - 不会将此隐式绑定到主机类型,这样做会将对象更改为特征并将它们混合而不是泛化(类型参数化):

      def records(f: File) = {
        if(true)
          new FileReader(f) with TypeA
        else
          new FileReader(f) with TypeB   
      }
    

    2) parser 应该在调用parse 的函数范围内。所以你可以这样尝试:

     def process(f: File) = {
         val reader = records(f);
         import reader._
         reader foreach { r => parse(r) }
      }
    

    PlanB) 更简单的替代方法是在 AppMain 中定义特定于类型参数的隐式方法(或混入一些特征),但只有在编译时已知 TypeA/TypeB 时它才会起作用,因此 records 方法可以返回具体类型:

    implicit class TypeAParser(r: Record[TypeA]) {
        def parse: Option[Int] = ???
    }
    
    implicit class TypeBParser(r: Record[TypeB]) {
        def parse: Option[Int] = ???
    }
    
    def process[T <: TypeAorB](f: File) =
        records[T](f).foreach(_.parse)
    
    def recordsA[T <: TypeAorB](f: File) =  new FileReader[T](f) 
    

    【讨论】:

      【解决方案4】:

      这是,我认为,你需要做的整套修改才能到达我认为你想去的地方。

      import scala.io.Source
      import java.io.File
      import reflect.runtime.universe._
      
      // Core traits
      trait Record[+T]
      trait Reader[+T] extends Iterable[Record[T]]
      trait Parser[-T] {
        def parse(r: Record[T]): Option[Int]
      }
      
      // Concrete implementations [unmodified]
      class LineRecord[T] extends Record[T]
      class FileReader[T](f:File) extends Reader[T] {
        val lines = Source.fromFile(f).getLines()
        def iterator: Iterator[LineRecord[T]] =
          new Iterator[LineRecord[T]] {
            def next() = new LineRecord[T]
            def hasNext = lines.hasNext
          }
      }
      
      sealed trait Alternatives
      case class TypeA() extends Alternatives
      object TypeA {
          implicit object TypeAParser extends Parser[TypeA] {
              def parse(r: Record[TypeA]): Option[Int] = ???
          }
      }
      case class TypeB() extends Alternatives
      object TypeB {
          implicit object TypeBParser extends Parser[TypeB] {
              def parse(r: Record[TypeB]): Option[Int] = ???
          }
      }
      
      class ParseAlternator(parserA: Parser[TypeA], parserB: Parser[TypeB]) extends Parser[Alternatives] {
        def parse(r: Record[Alternatives]): Option[Int] = r match {
          case x: Record[TypeA @unchecked] if typeOf[Alternatives] =:= typeOf[TypeA] => parserA.parse(x)
          case x: Record[TypeB @unchecked] if typeOf[Alternatives] =:= typeOf[TypeB] => parserB.parse(x)
        }
      }
      object ParseAlternator {
        implicit def parseAlternator(implicit parserA: Parser[TypeA], parserB: Parser[TypeB]): Parser[Alternatives] = new ParseAlternator(parserA, parserB)
      }
      
      // The "app"
      object MainApp {
        import ParseAlternator._
        def process(f: File) =
          records(f) foreach { r => parse(r) }
      
        def records(f: File): Reader[Alternatives] = {
          if(true)
            new FileReader[TypeA](f)
          else
            new FileReader[TypeB](f)
        }
      
        def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
            parser.parse(r)
      }
      

      它的要点是:如果您的 parse 实例不必对泛型类型进行模式匹配,而是直接处理 Alternative,那么所有这些都将是完全经典的。

      正是这个限制(继承自 JVM),scala 无法对需要反射和typeOf 使用的参数类型的对象进行正确的模式匹配。没有它,您将只有内容的替代类型(TypeATypeB),您可以将其添加到sealed 特征中,并且您将在其上分派,隐含地产生Parser for他们的超类型。

      当然,这不是唯一的解决方案,它只是我认为最接近你想要做的事情的交汇点,与最惯用的事情。

      【讨论】:

      • 谢谢 - 你得给我一点时间来接受!
      猜你喜欢
      • 2017-02-02
      • 2016-01-17
      • 2011-10-17
      • 2016-03-31
      • 1970-01-01
      • 2016-02-27
      • 2016-02-22
      • 2011-04-15
      • 2015-06-28
      相关资源
      最近更新 更多