【问题标题】:How convert from trait with type member to case class with type parameter an viceversa如何从具有类型成员的特征转换为具有类型参数的案例类,反之亦然
【发布时间】:2017-11-03 16:26:45
【问题描述】:

我不知道如何解决 scala 中的问题。也许有人可以帮助我!

我有一个带有一些类型参数的案例类(Operation),这个类可以由对参数类型一无所知的方法返回(例如来自string/json/xml的解析器) .

所以我需要一种方法以某种方式从ShadowedOperation 转换为Operation,因为需要从一些数据中解析ShadowedOperation 并从中提取类型化版本(Operation)。

我已经编写了一个应该表达问题的代码,它被简化并尝试做一些不同的事情,但是如果可以解决这个问题,我也可以解决真正的需求。

shapeless 可能有解决方案,但我需要找到没有它的解决方案。

object box {

  trait Transform[A, B] {
    def apply(in: A): B
  }
  object Transform {
    def instance[A, B](f: A => B): Transform[A, B] = new Transform[A, B] {
      override def apply(in: A): B = f(in)
    }
  }

  implicit class TransformOps[T](w: T) {
    def transform(implicit t: Transform[T, String]) = t(w)
  }

  trait ShadowedOperation {
    type I
    type O
    def param: String
    def otherParam: Int
    def in: I
    def out: O
  }

  object ShadowedOperation {
    // How can i do this in a generic, typed and wonderful way ???
    implicit def operationToString: Transform[ShadowedOperation, String] = ???
  }

  case class Operation[I0, O0](
    param: String,
    otherParam: Int,
    in: I0,
    out: O0
  ) extends ShadowedOperation {type I = I0; type O = O0}

  object Operation {
    implicit def operationToString[I, O](
      implicit
      iToString: Transform[I, String],
      oToString: Transform[O, String]
    ): Transform[Operation[I, O], String] =
      Transform.instance(in => s"${in.otherParam} - ${in.param} - ${iToString(in.in)} - ${oToString(in.out)}")
  }

  def fakeParseFromString(in: String): List[ShadowedOperation] = {
    // this simulate a parsing (or read from db) from string to extract the case class
    List(Operation("param", 0, "in!", "out!"), Operation("param", 0, "in!", 100))
  }

}


object Main extends App {

  import box._

  implicit val intToString: Transform[Int, String] = Transform.instance(_.toString)
  implicit val stringToString: Transform[String, String] = Transform.instance(_.toString)

  val op = Operation("param", 0, "in!", "out!")
  val shadowedOperationList = fakeParseFromString("imagine that this string contain a json")
  val opString = op.transform
  val shadowedOpString = shadowedOperationList.map(_.transform)
  println(opString)
  println(shadowedOpString)
}

提前感谢所有可以提供帮助的人!

【问题讨论】:

    标签: scala implicit-conversion typeclass


    【解决方案1】:

    我做了几处改动:

    • Transform[-A, +B]添加了协变/逆变

    • 引入类型ShadowedOperation.Aux[I0, O0]

    • 使用Aux-type 修复fakeParseFromString 的返回类型

    • operationToString从案例类的伴生对象提升到特征的伴生对象并进行相应的更改

    • 导入的实例:import op._

    整个代码:

      object box {
    
        trait Transform[-A, +B] {
          def apply(in: A): B
        }
        object Transform {
          def instance[A, B](f: A => B): Transform[A, B] = new Transform[A, B] {
            override def apply(in: A): B = f(in)
          }
        }
    
        implicit class TransformOps[T](w: T) {
          def transform(implicit t: Transform[T, String]) = t(w)
        }
    
        trait ShadowedOperation {
          type I
          type O
          def param: String
          def otherParam: Int
          def in: I
          def out: O
    
          implicit def operationToString(
                                          implicit
                                          iToString: Transform[I, String],
                                          oToString: Transform[O, String]
                                        ): Transform[ShadowedOperation.Aux[I, O], String] =
            Transform.instance(in => s"${in.otherParam} - ${in.param} - ${iToString(in.in)} - ${oToString(in.out)}")
        }
    
        object ShadowedOperation {
          type Aux[I0, O0] = ShadowedOperation { type I = I0; type O = O0 }
        }
    
        case class Operation[I0, O0](
                                      param: String,
                                      otherParam: Int,
                                      in: I0,
                                      out: O0
                                    ) extends ShadowedOperation {type I = I0; type O = O0}
    
    
        def fakeParseFromString[I, O](in: Operation[I, O]): ShadowedOperation.Aux[I, O] = in
    
      }
    
    
    
    
      def main(args: Array[String]): Unit = {
        import box._
    
        implicit val intToString: Transform[Int, String] = Transform.instance(_.toString)
        implicit val stringToString: Transform[String, String] = Transform.instance(_.toString)
    
        val op = Operation("param", 0, "in!", "out!")
        val shadowedOperation = fakeParseFromString(op)
        import op._
        val opString = op.transform
        val shadowedOpString = shadowedOperation.transform
        println(opString)//0 - param - in! - out!
        println(shadowedOpString)//0 - param - in! - out!
      }
    

    所以这里不需要shapeless


    当你只写ShadowedOperation 而不是ShadowedOperation.Aux[???, ???] 时,你会丢失一些关于类型的信息。您必须找到一种方法来恢复有关IO 的这些信息(一些强制转换、显式指定类型、定义更多隐式等)。否则隐式将不起作用。

    例如,在您更新的示例中,您可以编写

    def fakeParseFromString(in: String): List[ShadowedOperation.Aux[String, Any]] = 
      List(Operation("param", 0, "in!","out!"), Operation("param", 0, "in!", 100))
    
    implicit val anyToString: Transform[Any, String] = Transform.instance(_.toString)
    val shadowedOpString = shadowedOperationList.map(_.transform)
    println(shadowedOpString) 
    // List(Operation(param,0,in!,out!), Operation(param,0,in!,100))
    

    【讨论】:

    • 感谢您的回复!问题是真正的fakeParseFromString 不能从函数参数推断类型(IO)。我已经更改了示例以更好地解释问题
    猜你喜欢
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多