在Scala中,我如何才能避免铸造函数参数?

问题描述 投票:0回答:2

我想具有各种组件的“口味”,每一个处理一个不同的“线”的形式(例如字符串,字节数组等)。下面的实施例。在read()函数的内部结构并不重要。

请注意,在使用我需要强制转换参数"Heavy"thing.WIRE工作。由于这是我的最高级别的API,我不希望用户必须强制转换。他们选择当他们打电话FantasticThing.apply的味道(或接受默认)。从那以后,我宁愿不再需要强制转换。

我怎样才能避免施放并斯卡拉意识到read()说法是基于StringFlavor被选择的String?

trait Flavor {
  type WIRE
  def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
}

trait Maker {
  def make(): Flavor
}

object StringFlavor extends Maker {
  def make(): Flavor { type WIRE = String } = StringFlavor()
}

case class StringFlavor() extends Flavor {
  type WIRE = String
  def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
    println(tt.tpe)
    if(tt.tpe =:= typeOf[Int]) {
      5.asInstanceOf[T]
    } else
      throw new Exception("Boom")
  }
}

object FantasticThing {
  def apply[WIRE](maker: Maker = StringFlavor): Flavor = maker.make()
}

object RunMe extends App {
  val thing: Flavor = FantasticThing(StringMaker)
  println(thing.read[Int]("Heavy".asInstanceOf[thing.WIRE])) // <-- How can I avoid this cast?
}
  • 基于路易斯·马吉尔注编辑:我不是那种真正加入到FantasticThing.apply()或我会失去拔插。我希望用户能够方便地选择他们想要的味道。我已经重构了一下有工厂模式,这确实增加了你提出的类型信息,以证明这一点,但仍不幸离开我需要投顶层。

如果我提供了一堆口味的,那么用户应该能够做这样的事情:

val foo = FantasticThing(ByteArrayFlavor)
scala
2个回答
1
投票

您可以WIRE类型参数,并通过类型成员或者您Maker型传播它。即:

import scala.reflect.runtime.universe._

trait Flavor[WIRE] {
  def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
}

trait Maker {
  type O
  def make(): Flavor[O]
}

object StringMaker extends Maker {
  type O = String
  def make(): Flavor[O] = StringFlavor()
}

case class StringFlavor() extends Flavor[String] {
  def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
    if(tt.tpe =:= typeOf[Int]) {
      5.asInstanceOf[T]
    } else
      throw new Exception("Boom")
  }
}

object FantasticThing {
  def apply(): Flavor[String] = StringMaker.make()
  def apply(maker: Maker): Flavor[maker.O]  = maker.make() // Path dependent type.
}

object RunMe extends App {
  val thing: Flavor[String] = FantasticThing(StringMaker)
  thing.read[Int]("Heavy") // res0: Int = 5
}

编辑:增加了无参数适用()这个anwser。如果用机的默认值(例如StringMaker)你会得到一个编译错误,因为参数“重”,现在应该是类型Maker.O。添加不带参数的应用解决了这个问题,同时提供给调用者相同的体验。


0
投票

我冒昧地修改代码的意图来说明如何(我是这么理解的),您的问题可以使用类型类和类型的参数来解决,而不是类型成员。

import scala.reflect.runtime.universe.{TypeTag, typeOf}

implicit class Json(val underlying: String) extends AnyVal
implicit class Csv(val underlying: String) extends AnyVal

trait Flavor[W] {
  def read[T](wire: W)(implicit tt: TypeTag[T]): T
}

trait Maker[W] {
  def make(): Flavor[W]
}

object Maker {
  implicit val StringFlavorMaker: Maker[String] = new Maker[String] {
    override def make(): Flavor[String] = StringFlavor
  }

  implicit val JsonFlavorMaker: Maker[Json] = new Maker[Json] {
    override def make(): Flavor[Json] = JsonFlavor
  }

  implicit val CsvFlavorMaker: Maker[Csv] = new Maker[Csv] {
    override def make(): Flavor[Csv] = CsvFlavor
  }
}

case object StringFlavor extends Flavor[String] {
  override final def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
    if(tt.tpe =:= typeOf[Int])
      0.asInstanceOf[T]
    else
      throw new Exception("Boom 1")
  }
}

case object JsonFlavor extends Flavor[Json] {
  override final def read[T](wire: Json)(implicit tt: TypeTag[T]): T = {
    if(tt.tpe =:= typeOf[Int])
      3.asInstanceOf[T]
    else
      throw new Exception("Boom 2")
  }
}

case object CsvFlavor extends Flavor[Csv] {
  override final def read[T](wire: Csv)(implicit tt: TypeTag[T]): T = {
    if(tt.tpe =:= typeOf[Int])
      5.asInstanceOf[T]
    else
      throw new Exception("Boom 3")
  }
} 

object FantasticThing {
  def apply[W](implicit maker: Maker[W]): Flavor[W] = maker.make()
}

然后,您可以创建和用户的任何味道(因为有范围的隐含制造商)这种方式。

val stringFlavor = FantasticThing[String]
// stringFlavor: Flavor[String] = StringFlavor

stringFlavor.read[Int]("Heavy")
// res0: Int = 0

val jsonFlavor = FantasticThing[Json]
// jsonFlavor: Flavor[Json] = JsonFlavor

jsonFlavor.read[Int]("{'heavy':'true'}")
// res1: Int = 3

val csvFlavor = FantasticThing[Csv]
// csvFlavor: Flavor[Csv] = CsvFlavor

csvFlavor.read[Int]("Hea,vy")
// res2: Int = 0

在一般情况下,最好是留过类型的成员,因为它们更复杂,用于诸如路径依赖的类型更先进的东西。 让我知道在评论,如果您有任何疑问。


免责声明:我是坏与类型成员(仍在学习他们),这可能促使我用不同的选择。 - 在任何情况下,我希望你可以申请类似你真正的问题东西..

© www.soinside.com 2019 - 2024. All rights reserved.