具有两个类型参数的多态Scala无形映射

时间:2016-06-24 08:36:46

标签: scala shapeless

给出ProblemParser和Solver的特征:

r.db('content').table("users").merge(function(user){
  return r.object(
    'permissions', user('roles').eqJoin(function(id){
      return id
    }, r.db('content').table('roles'), {index: 'role'}).zip()('permissions')
    .reduce(function(left, right){
      return left.merge(right)
    })
    )      
});

和解析器和解算器的HLists,我正在尝试将所有(类型适当的)求解器应用于成功解析所产生的所有不同问题类型。

我可以看到如何使用〜>获取问题选项的HList。 :

trait ProblemParser[Problem] {
  def parse(description: String): Option[Problem]
}

trait Solver[Problem,Solution] {
  def solve(problem: Problem): Option[Solution]
}

问。鉴于不同问题类型的解析器的HList,我如何将解算器映射到已解析的问题列表? 大概是因为Solver需要两个类型参数,这需要Poly1而不是〜>?

1 个答案:

答案 0 :(得分:1)

你的问题有点简洁,所以我把它分成几个部分来理解:

  1. ProblemParser类型类,可以将描述解析为Problem

    String => Option[Problem]

  2. 使用HList解析ProblemProblemParser个描述列表的方法。

    例如List[String] => Option[ProblemA] :: Option[ProblemB] :: HNil

  3. 类型类Solver,可为Solution提供Problem

    Problem => Option[Solution]

  4. 使用Problem解决步骤2中的Solver

    例如Option[ProblemA] :: Option[ProblemB] :: HNil => Option[SolutionA] :: Option[SolutionB] :: HNil

  5. 我们首先定义两个简单的问题,得到一对整数的和或最大值:

    case class Sum(a: Int, b: Int)
    case class Max(a: Int, b: Int)
    

    现在,我们为两个问题创建了ProblemParser类型类,其中包含两个实例:

    import scala.util.Try
    
    trait ProblemParser[Problem] extends Serializable {
      def parse(description: String): Option[Problem]
    }
    
    object ProblemParser {
      def apply[A](implicit pp: ProblemParser[A]): ProblemParser[A] = pp
    
      def fromFunction[A](f: String => Option[A]): ProblemParser[A] =
        new ProblemParser[A] { def parse(s: String): Option[A] = f(s) }
    
      def intPairParser[A](f: (Int, Int) => A): ProblemParser[A] =
        fromFunction { s => 
          s.split(",") match { 
            case Array(l, r) => 
              for { 
                ll <- Try(l.toInt).toOption
                rr <- Try(r.toInt).toOption
              } yield f(ll, rr)
            case _ => None
          }
        }
    
      implicit val sumParser: ProblemParser[Sum] = intPairParser(Sum.apply)
      implicit val maxParser: ProblemParser[Max] = intPairParser(Max.apply)
    }
    

    HList的{​​{1}}中解析说明与其他问题中的my answer类似:

    Problem

    我们现在可以解析一些描述:

    import shapeless._
    import scala.collection.GenTraversable
    
    trait FromTraversableParsed[L <: HList] extends Serializable {
      type Out <: HList
      def apply(l: GenTraversable[String]): Out
    }
    
    object FromTraversableParsed {
      def apply[L <: HList]
        (implicit from: FromTraversableParsed[L]): Aux[L, from.Out] = from
    
      type Aux[L <: HList, Out0 <: HList] = FromTraversableParsed[L] { type Out = Out0 }
    
      implicit val hnilFromTraversableParsed: Aux[HNil, HNil] = 
        new FromTraversableParsed[HNil] {
          type Out = HNil
          def apply(l: GenTraversable[String]): Out = HNil
        }
    
      implicit def hlistFromTraversableParsed[H, T <: HList, OutT <: HList](implicit 
        ftpT: FromTraversableParsed.Aux[T, OutT],
        parseH: ProblemParser[H]
      ): Aux[H :: T, Option[H] :: OutT] = 
        new FromTraversableParsed[H :: T] {
          type Out = Option[H] :: OutT
          def apply(l: GenTraversable[String]): Out =
            (if(l.isEmpty) None else parseH.parse(l.head)) :: ftpT(l.tail)
        }
    }
    

    转到val parse = FromTraversableParsed[Max :: Sum :: HNil] parse(List("1,2", "1,2")) // Some(Max(1,2)) :: Some(Sum(1,2)) :: HNil 类型类(我将Solver作为依赖类型):

    Solution

    对于这些问题trait Solver[Problem] extends Serializable { type Solution def solve(problem: Problem): Option[Solution] } object Solver { def apply[Problem] (implicit solver: Solver[Problem]): Aux[Problem, solver.Solution] = solver type Aux[Problem, Solution0] = Solver[Problem] { type Solution = Solution0} implicit val solveMax: Aux[Max, Int] = new Solver[Max] { type Solution = Int def solve(max: Max) = Some(math.max(max.a, max.b)) } implicit val solveSum: Aux[Sum, Int] = new Solver[Sum] { type Solution = Int def solve(sum: Sum) = Some(sum.a + sum.b) } } HListProblem个实例,我们应该可以映射我们的Solver并使用正确的HList要解决Solver s:

    Problem

    有了所有这些机制,我们现在可以解析一系列描述并解决已解析的问题:

    object solve extends Poly1 {
      implicit def apply[Problem, Solution](
        implicit solver: Solver.Aux[Problem, Solution]
      ): Case.Aux[Option[Problem], Option[Solution]] =
        at[Option[Problem]](_.flatMap(solver.solve))
    }
    
    import shapeless.ops.hlist.Mapper
    
    def parseAndSolve[Problems <: HList] = 
      new PartiallyAppliedParseAndSolve[Problems]
    
    class PartiallyAppliedParseAndSolve[Problems <: HList] {
      def apply[OP <: HList](descriptions: List[String])(implicit 
        ftp: FromTraversableParsed.Aux[Problems, OP], 
        mapper: Mapper[solve.type, OP]
      ): mapper.Out = mapper(ftp(descriptions))
    }