就在我认为我理解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)
}
答案 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
用法。如果没有它,您只需要为您的内容(TypeA
,TypeB
)提供替代品,您可以将其添加到sealed
特征中,并将其隐含发送给Parser
特征。为他们的超类型生成{{1}}。
当然,这不是唯一的解决方案,它只是我认为最接近您尝试的内容的会面点用最常用的东西来做。