具有两个类型参数的多态 Scala Shapeless 映射
Polymorphic Scala Shapeless mapping with two type parameters
ProblemParser 和 Solver 的给定特征:
trait ProblemParser[Problem] {
def parse(description: String): Option[Problem]
}
trait Solver[Problem,Solution] {
def solve(problem: Problem): Option[Solution]
}
以及解析器和求解器的 HList,我正在尝试将所有(适合类型的)求解器应用于成功解析产生的所有不同问题类型。
我可以看到如何通过 ~> 获得问题选项的 HList :
object parseFn extends (ProblemParser ~> Option) {
def apply[P](x: ProblemParser[P]): Option[P] = x.parse(input)
}
Q. 给定一个包含不同问题类型的解析器的 HList,我如何将求解器映射到已解析问题的列表上?
大概是因为 Solver 采用两个类型参数,这需要 Poly1 而不是 ~>?
你的问题有点简洁,所以我把它分成几个部分来理解:
ProblemParser
类型 class 可以将描述解析为 Problem
。
String => Option[Problem]
一种使用 ProblemParser
.
将描述列表解析为 Problem
的 HList
的方法
例如List[String] => Option[ProblemA] :: Option[ProblemB] :: HNil
类型 class Solver
可以为 Problem
提供 Solution
。
Problem => Option[Solution]
使用 Solver
.
求解步骤 2 中的 Problem
例如Option[ProblemA] :: Option[ProblemB] :: HNil => Option[SolutionA] :: Option[SolutionB] :: HNil
我们从定义两个简单问题开始,获取一对整数的总和或最大值:
case class Sum(a: Int, b: Int)
case class Max(a: Int, b: Int)
现在,我们为我们的两个问题创建 ProblemParser
类型 class 和两个实例:
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)
}
将描述解析为 Problem
的 HList
类似于另一个问题中的 :
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
类型 class(我将 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)
}
}
有了 HList
个 Problem
和 Solver
个实例来解决这些问题,我们应该能够映射我们的 HList
并使用正确的 Solver
s 解决 Problem
s :
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))
}
有了所有这些机制,我们现在可以解析描述列表并解决已解析的问题:
parseAndSolve[Max :: Sum :: HNil](List("1,2", "1,2"))
// Option[Int] :: Option[Int] :: HNil = Some(2) :: Some(3) :: HNil
ProblemParser 和 Solver 的给定特征:
trait ProblemParser[Problem] {
def parse(description: String): Option[Problem]
}
trait Solver[Problem,Solution] {
def solve(problem: Problem): Option[Solution]
}
以及解析器和求解器的 HList,我正在尝试将所有(适合类型的)求解器应用于成功解析产生的所有不同问题类型。
我可以看到如何通过 ~> 获得问题选项的 HList :
object parseFn extends (ProblemParser ~> Option) {
def apply[P](x: ProblemParser[P]): Option[P] = x.parse(input)
}
Q. 给定一个包含不同问题类型的解析器的 HList,我如何将求解器映射到已解析问题的列表上? 大概是因为 Solver 采用两个类型参数,这需要 Poly1 而不是 ~>?
你的问题有点简洁,所以我把它分成几个部分来理解:
ProblemParser
类型 class 可以将描述解析为Problem
。String => Option[Problem]
一种使用
将描述列表解析为ProblemParser
.Problem
的HList
的方法例如
List[String] => Option[ProblemA] :: Option[ProblemB] :: HNil
类型 class
Solver
可以为Problem
提供Solution
。Problem => Option[Solution]
使用
求解步骤 2 中的Solver
.Problem
例如
Option[ProblemA] :: Option[ProblemB] :: HNil => Option[SolutionA] :: Option[SolutionB] :: HNil
我们从定义两个简单问题开始,获取一对整数的总和或最大值:
case class Sum(a: Int, b: Int)
case class Max(a: Int, b: Int)
现在,我们为我们的两个问题创建 ProblemParser
类型 class 和两个实例:
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)
}
将描述解析为 Problem
的 HList
类似于另一个问题中的
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
类型 class(我将 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)
}
}
有了 HList
个 Problem
和 Solver
个实例来解决这些问题,我们应该能够映射我们的 HList
并使用正确的 Solver
s 解决 Problem
s :
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))
}
有了所有这些机制,我们现在可以解析描述列表并解决已解析的问题:
parseAndSolve[Max :: Sum :: HNil](List("1,2", "1,2"))
// Option[Int] :: Option[Int] :: HNil = Some(2) :: Some(3) :: HNil