简体   繁体   English

找不到参数x的隐式值

[英]Could not find implicit value for parameter x

Just when I thought I understood the basics of Scala's type system... :/ 就在我以为我了解了Scala的类型系统的基础...:/

I'm trying to implement a class that reads the contents of a file and outputs a set of records. 我正在尝试实现一个类,该类读取文件的内容并输出一组记录。 A record might be a single line, but it could also be a block of bytes, or anything. 一条记录可能是一行,但也可能是一个字节块或其他内容。 So what I'm after is a structure that allows the type of Reader to imply the type of the Record, which in turn will imply the correct Parser to use. 因此,我所追求的是一种结构,该结构允许Reader类型隐含Record的类型,而后者又隐隐暗示使用正确的解析器。

This structure works as long as MainApp.records(f) only returns one type of Reader. 只要MainApp.records(f)仅返回一种类型的Reader,此结构就起作用。 As soon as it can return more, I get this error: 一旦它可以返回更多,我得到这个错误:

could not find implicit value for parameter parser 找不到参数解析器的隐式值

I think the problem lies with the typed trait definitions at the top, but I cannot figure out how to fix the issue... 我认为问题在于顶部的类型化特征定义,但我不知道如何解决此问题...

// 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)
}

First off you must import the implicit object in order to use them: 首先,您必须导入隐式对象才能使用它们:

import TypeA._
import TypeB._

That's not enough though. 但这还不够。 It seems like you're trying to apply implicits dynamically. 似乎您正在尝试动态应用隐式。 That's not possible; 那是不可能的。 they have to be found compile time. 必须找到它们的编译时间。

If you import the objects as above and change the records so that the compiler finds the correct generic it will run fine: 如果您按上述方式导入对象并更改记录,以便编译器找到正确的泛型,它将正常运行:

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

But then it may not be what you were looking for ;) 但这可能不是您想要的;)

1) Using type with implicit inside as type parameter - doesn't bind this implicit to the host type, to do this change objects to the traits and mix them instead of generalizing (type-parametrizing): 1)使用内部具有隐式类型的类型作为类型参数-不会将此隐式绑定到主机类型,而是将对象更改为特征并混合它们,而不是泛化(类型参数化):

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

2) The parser should be in scope of function that calls parse . 2) parser应在调用parse的函数范围内。 So you may try smthg like that: 因此,您可以像这样尝试smthg:

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

PlanB) Simpler alternative is to define type-parameter specific implicit methods inside the AppMain (or some trait mixed in), but it will work only if TypeA/TypeB is known on compile time, so records method can return concrete type: 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) 

The problem is that the return type of your records method is basically FileReader[_] (since it can return either FileReader[TypeA] or FileReader[TypeB] ), and you don't provide an implicit argument of type Parser[Any] . 问题是records方法的返回类型基本上是FileReader[_] (因为它可以返回FileReader[TypeA]FileReader[TypeB] ),并且您没有提供Parser[Any]类型的隐式参数。 If you remove the if -expression the return type is inferred to FileReader[TypeA] , which works fine. 如果删除if -expression,则将返回类型推断为FileReader[TypeA] ,效果很好。 I'm not sure what you're trying to do, but obviously the compiler can't select implicit argument based upon a type that is only known at runtime. 我不确定您要做什么,但是显然编译器无法基于仅在运行时才知道的类型选择隐式参数。

Here is, I think, the full set of modifications you need to do to get where I think you want to go. 我认为,这是您需要进行的全套修改才能达到我认为的目标

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)
}

The gist of it is: all of this would be completely classsical if only your parse instance did not have to pattern-match on a generic type but dealt directly with an Alternative instead. 它的要点是:如果仅您的parse实例不必对泛型类型进行模式匹配,而是直接与Alternative处理,那么所有这些将完全是经典的。

It's this limitation (inherited from the JVM) that scala can't properly pattern-match on an object of a parametric type that requires the reflection & typeOf usage. 正是这种限制(从JVM继承)使得scala无法在需要反射和typeOf用法的参数类型对象上正确进行模式匹配。 Without it, you would just have type alternatives for your content ( TypeA , TypeB ), which you would add to a sealed trait, and which you would dispatch on, in an implicit that produces a Parser for their supertype. 没有它,您将只为内容( TypeATypeB )使用类型替代方法,将其添加到sealed特征中并派发给它,在隐式中为其生成超类而生成Parser

Of course this isn't the only solution, it's just what I think is the meeting point of what's closest to what you're trying to do, with what's most idiomatic. 当然,这不是唯一的解决方案,这只是我认为最接近您要尝试做的事情的交汇点,并且是最惯用的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM