无法找到参数x的隐含值

时间:2014-11-28 12:28:30

标签: scala types implicit

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

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

只要MainApp.records(f)只返回一种类型的Reader,此结构就会起作用。一旦它可以返回更多,我得到这个错误:

  

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

我认为问题在于顶部的类型特征定义,但我无法弄清楚如何解决问题......

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

4 个答案:

答案 0 :(得分:2)

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

import TypeA._
import TypeB._

但这还不够。看起来你正试图动态地应用implicits。那是不可能的;他们必须在编译时找到。

如果你导入上面的对象并更改记录,以便编译器找到正确的通用,它将运行良好:

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

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

答案 1 :(得分:1)

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

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

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

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

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

records

答案 2 :(得分:1)

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

答案 3 :(得分:0)

我认为,这是我需要做的全套修改,以便达到我想要去的地方

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特征。为他们的超类型生成{{1}}。

当然,这不是唯一的解决方案,它只是我认为最接近您尝试的内容的会面点用最常用的东西来做。