Skip to content

Instantly share code, notes, and snippets.

@arturaz
Created December 17, 2018 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arturaz/493d57840d8dcd7e3f6394db001f967c to your computer and use it in GitHub Desktop.
Save arturaz/493d57840d8dcd7e3f6394db001f967c to your computer and use it in GitHub Desktop.
Playing around with shapeless
package app
import shapeless.ops.hlist.{ConstMapper, Intersection, Union, ZipWith}
import shapeless.{::, HList, HNil, Poly2}
case class Params()
sealed trait EvaluatableGeneratorFilter[+Result] {
def eval(p: Params): Result
}
case class FilteredValue[A, Filters <: HList, Interim <: HList, Outputs <: HList](
filters: Filters
)(val f: Outputs => A)(
implicit
constMapper: ConstMapper.Aux[Params, Filters, Interim],
zipWith: ZipWith.Aux[Filters, Interim, FilteredValue.evalPoly.type, Outputs]
) { self =>
def eval(p: Params): A = {
val mapped = filters.zipWith(filters.mapConst(p))(FilteredValue.evalPoly)
f(mapped)
}
def map[B](
// Needs to have different name than mapper, otherwise implicit resolution fails...
resultMapper: A => B
): FilteredValue[B, Filters, Interim, Outputs] =
FilteredValue[B, Filters, Interim, Outputs](filters) { a =>
resultMapper(f(a))
}
// This builder is required, because otherwise compiler can't infer type of OutputsU.
case class AndBuilder[
B,
Filters2 <: HList, Interim2 <: HList, Outputs2 <: HList,
FiltersU <: HList, InterimU <: HList, OutputsU <: HList
](fv: FilteredValue[B, Filters2, Interim2, Outputs2])(implicit
union: Union.Aux[Filters, Filters2, FiltersU],
constMapper: ConstMapper.Aux[Params, FiltersU, InterimU],
zipWith: ZipWith.Aux[FiltersU, InterimU, FilteredValue.evalPoly.type, OutputsU],
intersect1: Intersection.Aux[OutputsU, Outputs, Outputs],
intersect2: Intersection.Aux[OutputsU, Outputs2, Outputs2]
) {
def build[Result](f: (A, B, OutputsU) => Result) =
FilteredValue[Result, FiltersU, InterimU, OutputsU](filters.union(fv.filters)) { outputsU =>
val outputs1 = outputsU.intersect(intersect1)
val outputs2 = outputsU.intersect(intersect2)
val a = self.f(outputs1)
val b = fv.f(outputs2)
f(a, b, outputsU)
}
}
def and[
B,
Filters2 <: HList, Interim2 <: HList, Outputs2 <: HList,
FiltersU <: HList, InterimU <: HList, OutputsU <: HList
](
fv: FilteredValue[B, Filters2, Interim2, Outputs2]
)(implicit
union: Union.Aux[Filters, Filters2, FiltersU],
constMapper: ConstMapper.Aux[Params, FiltersU, InterimU],
zipWith: ZipWith.Aux[FiltersU, InterimU, FilteredValue.evalPoly.type, OutputsU],
intersect1: Intersection.Aux[OutputsU, Outputs, Outputs],
intersect2: Intersection.Aux[OutputsU, Outputs2, Outputs2]
): AndBuilder[
B,
Filters2, Interim2, Outputs2,
FiltersU, InterimU, OutputsU
] = AndBuilder(fv)
}
object FilteredValue {
object evalPoly extends Poly2 {
implicit def `case`[T]: Case.Aux[EvaluatableGeneratorFilter[T], Params, T] = at(_.eval(_))
}
// This builder is required, because otherwise compiler can't infer type of Outputs.
case class Builder[
Filters <: HList, Interim <: HList, Outputs <: HList
](filters: Filters)(implicit
constMapper: ConstMapper.Aux[Params, Filters, Interim],
zipWith: ZipWith.Aux[Filters, Interim, FilteredValue.evalPoly.type, Outputs]
) {
def build[A](f: Outputs => A) = FilteredValue[A, Filters, Interim, Outputs](filters)(f)
}
def constant[A](a: A): FilteredValue[A, HNil, HNil, HNil] = FilteredValue(HNil: HNil) { _ => a }
}
object Test extends App {
val test =
new EvaluatableGeneratorFilter[Int] { def eval(p: Params) = 3 } ::
new EvaluatableGeneratorFilter[String] { def eval(p: Params) = "3" } :: HNil
val t1 = 3 :: "3" :: HNil
val t2 = "3" :: 3 :: Symbol("3") :: Vector(3) :: HNil
// test.zipWith(test.mapConst(Params()))(FilteredValue.evalPoly)
val fv2 = FilteredValue.Builder(test).build { case a :: b :: HNil => b * a }
val fv3 = FilteredValue.Builder(test).build { case a :: b :: HNil => a * b.length }
val x = fv2.and(fv3).build { case (a, b, l) => (a, b) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment