trait Obs[A, B] { def value: A } trait ChildObs[A, B] extends Obs[A, B] object Obs { import shapeless._, poly._, ops.hlist.Mapper, ops.function.FnToProduct object values extends Poly1 { implicit def a1[A, C <% Obs[A, _]]: Case.Aux[C, A] = at[C]{ _.value } } def calculate[T, V <: HList, L <: HList, F, Z](obss: T)(f: F)(implicit gen: Generic.Aux[T, L], mapper: Mapper.Aux[values.type, L, V], ftp: FnToProduct.Aux[F, V => Z] ): Z = ftp(f)(mapper.apply(gen.to(obss))) } object ObsExample { import Obs._ val obs1: Obs[Double, Double] = new ChildObs[Double, Double] { val value = 10.0 } val obs2: ChildObs[Option[Int], Int] = new ChildObs[Option[Int], Int] { val value = Some(1) } def func(d: Double, i: Option[Int]) = d + i.getOrElse(0).toDouble println(calculate(obs1, obs2)(func _)) }