I'm using Shapeless to accumulate materialized values in Akka as an HList and convert that to a case class.
(You don't have to know Akka much for this question, but the default approach accumulates materialized values as recursively nested 2-tuples, which isn't much fun, so Shapeless HLists seemed a more sensible approach -- and works pretty well. But I don't know how to properly re-use that approach. Here, I'll simplify the kinds of values Akka produces.)
For example, let's say we've got two materialized types, "A" and "B":
case class Result(b: B, a: A)
createA
.mapMaterialized((a: A) => a :: HNil)
.viaMat(flowCreatingB)((list1, b: B) => b :: list1)
.mapMaterialized(list2 => Generic[Result].from(list2))
// list1 = A :: HNil
// list2 = B :: A :: HNil
... and that produces Result just fine. But it requires that your case class be written backwards -- first value last, etc -- which is kind of dorky and hard to follow.
So the sensible thing is to reverse the list before converting to the case class, like this:
case class Result(a: A, b: B)
// ...
.mapMaterialized(list2 => Generic[Result].from(list2.reverse))
Now we can think about Result properties in the same order they were built. Yay.
But how to simplify and reuse this line of code?
The problem is that implicits don't work on multiple type parameters. For example:
def toCaseClass[A, R <: HList](implicit g: Generic.Aux[A, R], r: Reverse.Aux[L, R]): R => A =
l => g.from(l.reverse)
I'd need to specify both A (Result, above) and the HList being built:
.mapMaterialized(toCaseClass[Result, B :: A :: HNil])
Obviously, that invocation is going to be absurd with long lists (and Akka tends to build up really ugly-looking materialized types, not merely "A" and "B"). It'd be nicer to write something like:
.mapMaterialized(toCaseClass[Result])
I've tried to solve this using implicits, like this:
implicit class GraphOps[Mat <: HList](g: RunnableGraph[Mat]) {
implicit def createConverter[A, RL <: HList](implicit
r: Reverse.Aux[Mat, RL],
gen: Generic.Aux[A, RL]): Lazy[Mat => A] =
Lazy { l =>
val x: RL = l.reverse
val y: A = gen.from(x)
gen.from(l.reverse)
}
def toCaseClass[A](implicit convert: Lazy[Mat => A]): RunnableGraph[A] = {
g.mapMaterializedValue(convert.value)
}
But the compiler complains "No implicit view available".
The deeper problem is that I don't quite understand how to properly infer...
// R = Reversed order (e.g. B :: A :: NHNil)
// T = Type to create (e.g. Result(a, b))
// H = HList of T (e.g. A :: B :: HNil)
gen: Generic.Aux[T, H] // Generic[T] { type Repr = H }
rev: Reverse.Aux[R, H] // Reverse[R] { type Out = H }
This is sort of backwards from how Shapeless likes to infer things; I can't quite chain the abstract type members properly.
Profound thanks if you have insight here.
My bad: the example above, of course, requires Akka to compile. A simpler way of putting it is this (with thanks to Dymtro):
import shapeless._
import shapeless.ops.hlist.Reverse
case class Result(one: String, two: Int)
val results = 2 :: "one" :: HNil
println(Generic[Result].from(results.reverse))
// this works: prints "Result(one,2)"
case class Converter[A, B](value: A => B)
implicit class Ops[L <: HList](list: L) {
implicit def createConverter[A, RL <: HList](implicit
r: Reverse.Aux[L, RL],
gen: Generic.Aux[A, RL]): Converter[L, A] =
Converter(l => gen.from(l.reverse))
def toClass[A](implicit converter: Converter[L, A]): A =
converter.value(list)
}
println(results.toClass[Result])
// error: could not find implicit value for parameter converter:
// Converter[Int :: String :: shapeless.HNil,Result]
Dymtro's final example, below...
implicit class GraphOps[Mat <: HList, R <: HList](g: RunnableGraph[Mat]) {
def toCaseClass[A](implicit
r: Reverse.Aux[Mat, R],
gen: Generic.Aux[A, R]
): RunnableGraph[A] = g.mapMaterializedValue(l => gen.from(l.reverse))
}
... does seem to do what I'd been hoping for. Thank you very much Dmytro!
(Note: I had been somewhat misled in analyzing it earlier: it seems IntelliJ's presentation compiler incorrectly insists it won't compile (missing implicits). Moral: Don't trust IJ's presentation compiler.)
If I understood correctly you wish that in
def toCaseClass[A, R <: HList, L <: HList](implicit
g: Generic.Aux[A, R],
r: Reverse.Aux[L, R]
): L => A = l => g.from(l.reverse)
you could specify only A and then R, L be inferred.
You can do this with PartiallyApplied pattern
import shapeless.ops.hlist.Reverse
import shapeless.{Generic, HList, HNil}
def toCaseClass[A] = new {
def apply[R <: HList, L <: HList]()(implicit
g: Generic.Aux[A, R],
r0: Reverse.Aux[R, L],
r: Reverse.Aux[L, R]
): L => A = l => g.from(l.reverse)
}
class A
class B
val a = new A
val b = new B
case class Result(a: A, b: B)
toCaseClass[Result]().apply(b :: a :: HNil)
(without implicit r0 type parameter L can't be inferred upon call of .apply() because L becomes known only upon call .apply().apply(...))
or better
def toCaseClass[A] = new {
def apply[R <: HList, L <: HList](l: L)(implicit
g: Generic.Aux[A, R],
r: Reverse.Aux[L, R]
): A = g.from(l.reverse)
}
toCaseClass[Result](b :: a :: HNil)
(here we don't need r0 because L becomes known already upon call .apply(...)).
If you want you can replace anonymous class with named one
def toCaseClass[A] = new PartiallyApplied[A]
class PartiallyApplied[A] {
def apply...
}
Alternatively you can define a type class (although this is a little more wordy)
trait ToCaseClass[A] {
type L
def toCaseClass(l: L): A
}
object ToCaseClass {
type Aux[A, L0] = ToCaseClass[A] { type L = L0 }
def instance[A, L0](f: L0 => A): Aux[A, L0] = new ToCaseClass[A] {
type L = L0
override def toCaseClass(l: L0): A = f(l)
}
implicit def mkToCaseClass[A, R <: HList, L <: HList](implicit
g: Generic.Aux[A, R],
r0: Reverse.Aux[R, L],
r: Reverse.Aux[L, R]
): Aux[A, L] = instance(l => g.from(l.reverse))
}
def toCaseClass[A](implicit tcc: ToCaseClass[A]): tcc.L => A = tcc.toCaseClass
toCaseClass[Result].apply(b :: a :: HNil)
Hiding several implicits with a type class: How to wrap a method having implicits with another method in Scala?
You could find an answer to your question in Type Astronaut:
https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:ops:migration (6.3 Case study: case class migrations)
Notice that IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2a] takes a single type parameter.
Your code with GraphOps doesn't work for several reasons.
Firstly, shapeless.Lazy is not just a wrapper. It's a macro-based type class to handle "diverging implicit expansion" (in Scala 2.13 there are by-name => implicits for that, although they are not equivalent to Lazy). You should use Lazy when you understand why you need it.
Secondly, you seem to define some implicit conversion (implicit view, Mat => A) but resolution of implicit conversions is trickier than resolution of other implicits (1 2 3 4 5).
Thirdly, you seem to assume that when you define
implicit def foo: Foo = ???
def useImplicitFoo(implicit foo1: Foo) = ???
foo1 is foo. But generally this is not true. foo is defined in current scope and foo1 will be resolved in the scope of useImplicitFoo call site:
Setting abstract type based on typeclass
When doing implicit resolution with type parameters, why does val placement matter? (difference between implicit x: X and implicitly[X])
So implicit createConverter is just not in scope when you call toCaseClass.
Fixed version of your code is
trait RunnableGraph[Mat]{
def mapMaterializedValue[A](a: Mat => A): RunnableGraph[A]
}
case class Wrapper[A, B](value: A => B)
implicit class GraphOps[Mat <: HList](g: RunnableGraph[Mat]) {
val ops = this
implicit def createConverter[A, RL <: HList](implicit
r: Reverse.Aux[Mat, RL],
gen: Generic.Aux[A, RL],
): Wrapper[Mat, A] =
Wrapper { l =>
val x: RL = l.reverse
val y: A = gen.from(x)
gen.from(l.reverse)
}
def toCaseClass[A](implicit convert: Wrapper[Mat, A]): RunnableGraph[A] = {
g.mapMaterializedValue(convert.value)
}
}
val g: RunnableGraph[B :: A :: HNil] = ???
val ops = g.ops
import ops._
g.toCaseClass[Result]
Try
import akka.stream.scaladsl.RunnableGraph
import shapeless.{::, Generic, HList, HNil}
import shapeless.ops.hlist.Reverse
implicit class GraphOps[Mat <: HList, R <: HList](g: RunnableGraph[Mat]) {
def toCaseClass[A](implicit
r: Reverse.Aux[Mat, R],
gen: Generic.Aux[A, R]
): RunnableGraph[A] = g.mapMaterializedValue(l => gen.from(l.reverse))
}
case class Result(one: String, two: Int)
val g: RunnableGraph[Int :: String :: HNil] = ???
g.toCaseClass[Result]
Related
I want to create something that works like this
implicit class HListOps[AHList<:HList](value:AHList){
def fold[R](folder: /*What here?*/)={
}
}
so that it works like this
("HeY"::42::HNil).fold{string=>int=> string+int.toString} // returns "HeY42"
I don't think it is directly possible using some type classes in Shapeless, but it is possible to do a similar thing for a function of type (T0, T1, ...) => R:
implicit class HListOps[L <: HList](value: L) {
def fold[R, F](folder: F)(implicit ftp: FnToProduct.Aux[F, L => R]): R = {
ftp(folder).apply(value)
}
}
(1 :: "a" :: HNil).fold((x: Int, y: String) => y + x)
Unfortunately, you still have to specify parameters for the function type explicitly. It could be theoretically possible to define the extension class to this:
implicit class HListOps2[L <: HList, F, R](value: L)(implicit ftp: FnToProduct.Aux[F, L => R]) {
def fold(folder: F): R = ftp(folder).apply(value)
}
This, however, would require you to know the result type "in advance", which is quite unergonomic (and won't really work with the definition above, but it is possible to make it work with a bit more code).
You can overcome the last problem by requiring the function to accept a tuple instead:
implicit class HListOps3[L <: HList, T](value: L)(implicit tup: Tupler.Aux[L, T]) {
def fold[R](folder: T => R): R = folder(tup(value))
}
(1 :: "a" :: HNil).fold { case (x, y) => y + x }
This way, you won't need to specify argument types for the function, but rather the function itself should accept a tuple, which is also a bit of ergonomic hit because you will have to use the partial function syntax to unpack arguments from the tuple argument.
Well, after checking how this problem looks very similar to actually reversing an HList, I used a very similar approach and got this working:
import java.time.LocalDate
import scala.language.{higherKinds, reflectiveCalls}
import cats.Applicative
import shapeless._
trait HFolder[InL <: HList, Out] {
type Fld
def fold(inL: InL): Function1[Fld, Out]
}
object HFolder {
implicit def nilHFolder[Out]: HFolder[HNil, Out] {type Fld = Out} = new HFolder[HNil, Out] {
type Fld = Out
override def fold(inL: HNil): Function1[Out, Out] = identity
}
implicit def consHFolder[InH, InT <: HList, Out]
(implicit tailHFolder: HFolder[InT, Out]): HFolder[InH :: InT, Out] {type Fld = InH => tailHFolder.Fld} =
new HFolder[InH :: InT, Out] {
override type Fld = InH => tailHFolder.Fld
override def fold(inL: InH :: InT): Function1[InH => tailHFolder.Fld, Out] = {
folder =>
inL match {
case inH :: inT => tailHFolder.fold(inT)(folder(inH))
}
}
}
implicit class HListOps[InL <: HList](inL: InL) {
def fold[Out](implicit hfolder: HFolder[InL, Out]): Function[hfolder.Fld, Out] = {
hfolder.fold(inL)
}
}
//Here compiler can infer correct info (in this case (String=>Int=>LocalDate)=>LocalDate)
val folder= ("Alejo" :: 29 :: HNil).fold[LocalDate]
}
Only small problem is that after calling the method fold I have to invoke it using the word apply without syntactic sugar because otherwise compiler thinks I'm passing the implicit explicitly.
I am trying to gather the fields of a case class that have a particular annotations at compile time using shapeless. I tried to play around the following snippet, but it did not work as expected (output nothing instead of printing "i"). How can I make it work ?
import shapeless._
import shapeless.labelled._
final class searchable() extends scala.annotation.StaticAnnotation
final case class Foo(#searchable i: Int, s: String)
trait Boo[A] {
def print(a: A): Unit
}
sealed trait Boo0 {
implicit def hnil = new Boo[HNil] { def print(hnil: HNil): Unit = () }
implicit def hlist[K <: Symbol, V, RL <: HList](implicit b: Boo[RL]): Boo[FieldType[K, V] :: RL] =
new Boo[FieldType[K, V] :: RL] {
def print(a: FieldType[K, V] :: RL): Unit = {
b.print(a.tail)
}
}
}
sealed trait Boo1 extends Boo0 {
implicit def hlist1[K <: Symbol, V, RL <: HList](implicit annot: Annotation[searchable, K], witness: Witness.Aux[K], b: Boo[RL]): Boo[FieldType[K, V] :: RL] =
new Boo[FieldType[K, V] :: RL] {
def print(a: FieldType[K, V] :: RL): Unit = {
Console.println(witness.value.name)
b.print(a.tail)
}
}
}
object Boo extends Boo1 {
implicit def generics[A, HL <: HList](implicit iso: LabelledGeneric.Aux[A, HL], boo: Boo[HL]): Boo[A] =
new Boo[A] {
def print(a: A): Unit = {
boo.print(iso.to(a))
}
}
}
implicitly[Boo[Foo]].print(Foo(1, "2"))
Looking at the macro of Annotation, it rejects type that is not a product or coproduct straight up
val annTreeOpts =
if (isProduct(tpe)) { ... }
else if (isCoproduct(tpe)) { ... }
else abort(s"$tpe is not case class like or the root of a sealed family of types")
this is quite unfortunate, as collecting type annotations at per field symbol level could be quite useful sometimes.
There is another type class Annotations defined in the same file that can actually collect particular annotations on field into an HList. However problem is the field information is totally lost. There is a clumsy way to hack things together to serve my use case...
// A is our annotation
// B is our result type
// C is our case class with some fields annotated with A
def empty: B = ???
def concat(b1: B, b2: B): B = ???
def func(a: A, nm: String): B = ???
object Collector extends Poly2 {
implicit def some[K <: Symbol](implicit witness: Witness.Aux[K]) =
at[B, (K, Some[A])] { case (b, (_, a)) => concat(b, func(a.get, witness.value.name)) }
implicit def none[K <: Symbol] = at[B, (K, None.type)] { case (b, _) => b }
}
def collect[HL <: HList, RL <: HList, KL <: HList, ZL <: HList](implicit
iso: LabelledGeneric.Aux[C, HL]
, annot: Annotations.Aux[A, C, RL]
, keys: Keys.Aux[HL, KL]
, zip: Zip.Aux[KL :: RL :: HNil, ZL]
, leftFolder: LeftFolder.Aux[ZL, B, Collector.type, B]): B = {
zip(keys() :: annot() :: HNil).foldLeft(empty)(Collector)
}
Suppose I have container-marker
case class TypedString[T](value: String)
and partial function trick
abstract class PartFunc[Actual <: HList] {
val poly: Poly
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[poly.type, L1]): L1 = l
}
Poly for Mapper
object f extends (TypedString ~>> String) {
def apply[T](s : TypedString[T]) = s.value
}
and result method
def func[Actual <: HList] = new PartFunc[Actual] {
val poly = f
}
Example of usage:
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
This code fails at compile time because compiler cannot find Mapped implicit parameter:
could not find implicit value for parameter mapped:
shapeless.ops.hlist.Mapped[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],nottogether.MapperTest.TypedString]{type Out = shapeless.::[nottogether.MapperTest.TypedString[Int],shapeless.::[nottogether.MapperTest.TypedString[String],shapeless.HNil]]}
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
But if we remove Mapper implicit parameter from PartFunc.apply(...) signature all works fine. So I have no idea why and how Mapper influence on Mapped.
The compiler complains about Mapped while the actual issue is with Mapper. I am not sure why, but there seems to be going something wrong with getting a Mapped for the singleton type poly.type when poly is an abstract value or a constructor argument of PartFunc.
A solution would be to make poly a P <: Poly and passing the singleton type along with Actual when we create the PartFunc :
import shapeless._
import ops.hlist.{Mapped, Mapper}
import poly.~>>
abstract class PartFunc[Actual <: HList, P <: Poly] {
val poly: P
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type] { val poly = f }
Or with a regular class :
class PartFunc[Actual <: HList, P <: Poly](poly: P) {
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type](f)
Notice that we now have to write mapper(l), because l map poly will still look for a Mapped[poly.type, L1].
We can now call func :
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
// String :: String :: HNil = 42 :: hello :: HNil
I am sure that someone with some more in depth knowledge of the Scala type system could provide us with a clearer explanation and possibly a better solution for this problem.
My initial code:
sealed trait Adder[L <: HList, U] extends DepFn2[L, Vector[U]]
object Adder {
def apply[L <: HList, U: Ordering](implicit adder: Adder[L, U]): Aux[L, U, adder.Out] = adder
type Aux[L <: HList, U, Out0] = Adder[L, U] { type Out = Out0 }
implicit def found[T <: HList, U: Ordering]: Aux[Vector[U] :: T, U, Vector[U] :: T] =
new Adder[Vector[U] :: T, U] {
type Out = Vector[U] :: T
def apply(l: Vector[U] :: T, collection: Vector[U]): Out = {
(l.head ++ collection).sorted :: l.tail
}
}
implicit def notFound[H, T <: HList, U: Ordering, OutT <: HList](implicit ut: Aux[T, U, OutT]): Aux[H :: T, U, H :: OutT] =
new Adder[H :: T, U] {
type Out = H :: OutT
def apply(l: H :: T, collection: Vector[U]): Out = {
val outT = ut(l.tail, collection)
l.head :: outT
}
}
implicit def empty[U: Ordering]: Aux[HNil, U, Vector[U] :: HNil] =
new Adder[HNil, U] {
type Out = Vector[U] :: HNil
def apply(l: HNil, collection: Vector[U]): Out = collection :: HNil
}
}
I found a bug where things that don't have the context bound
Ordering, the type is passed via notFound instead of found,
which is in hinsight not suprising. I tried to fix the bug by adding
another implicit which should trigger when there is no Ordering:
implicit def foundNoOrdering[T <: HList, U]: Aux[Vector[U] :: T, U, Vector[U] :: T] =
new Adder[Vector[U] :: T, U] {
type Out = Vector[U] :: T
def apply(l: Vector[U] :: T, collection: Vector[U]): Out = {
l.head ++ collection :: l.tail
}
}
However, this results in an ambiguous implicit between the
foundNoOrdering and found. How can I have different code paths
dependent on if there is an Ordering or not?
The standard trick is to reduce the priority by putting the implicit in an ancestor trait
object Adder extends LowPriorityAdderImplicits {
implicit def found...
}
trait LowPriorityAdderImplicits {
implicit def foundNoOrdering....
}
You will find a few of those in the standard library. LowPriorityImplicits seems to be customary in the name.
In the specification:
SLS §7.2 Implicit parameters
If there are several eligible arguments which match the implicit
parameter’s type, a most specific one will be chosen using the rules
of static overloading resolution (§6.26.3)
SLS §6.26.3 : The relevant bit is too long to cite in full, but you have something about
A class or object C is derived from a class or object D if one of the
following holds:
• C is a subclass of D, or
• C is a companion object of a class derived from D, or
• D is a companion object of a class from which C is derived.
and there being derived making it more specific and getting priority in resolution. I believe that one was made just for implicit.
The following gist has the code for an idea I am playing with
package com.test1
import scala.language.implicitConversions
import shapeless._
import FromTraversable._
import Traversables._
import Nat._
import Tuples._
trait ToArity[P, N <: Nat]
object ToArity {
implicit def prod1[P <: Product1[_]] = new ToArity[P, _1] {}
implicit def prod2[P <: Product2[_, _]] = new ToArity[P, _2] {}
// ad nauseum...
}
trait SizedHListAux[A, N <: Nat, T <: HList]
object SizedHListAux {
implicit def base[A, H <: HList] = new SizedHListAux[A, _0, HNil] {}
implicit def induct[A, H <: HList, N <: Nat, P <: Nat](implicit r: PredAux[N,P], k: SizedHListAux[A, P, H]) = new SizedHListAux[A, N, A :: H] {}
}
trait SomeFun {
type Result
def apply(): Result
}
// I want to abstract over A, the contained type in the List
// over P the Product type which is the arg notably its arity
// This means we need to recover arity of the Product type and render it in value space
// and also means that we need to compute the type of the intermediate HList
object SomeFun {
def produce(m: SomeFun): m.Result = m()
implicit def fromF1[T, A, P <: Product, N <: Nat, H <: HList](f1: (P => T, List[A]))(implicit k: ToArity[P, N], toI: ToInt[N], l: SizedHListAux[A, N, H], toHL: FromTraversable[H], tp: TuplerAux[H, P]) =
new SomeFun {
type Result = (T, List[A])
def apply(): Result = {
val (f, as) = f1
val (ts, rest) = (as.take(toI()), as.drop(toI()))
f((toHL(ts).get).tupled) -> rest
}
}
// Debug Arity checker
def printArity[P <: Product, N <: Nat](p: P)(implicit k: ToArity[P, N], toI: ToInt[N]) = println("Arity: " + toI())
}
object Test {
val thedata = List("foo", "bar", "baz", "bob")
val tfn = (x: (String, String)) => println("%s and %s".format(x._1, x._2))
def foo = SomeFun.printArity("a" -> "b")
//def doit = SomeFun.produce((tfn, thedata)) // Adding this line does not compile
}
The idea is that you use a function's argument arity, in this case the arity of a Product type, to drive parsing of an associated List[A]. Kind of like using sticky tape to peel off layers of graphene from graphite, i.e. the type of the functions pull things out of the list. This is just an sketch using a single contained type, but I imagine it could be generalised. The important facet is that the functions themselves are unaware of the List processing.
However...the concept seems to fail when trying to resolve the ToArity[P,N] implicit. On its own ToArity is resolvable as evidenced by printArity().
Can someone shed some light on why this is not resolvable in the context of fromF1? Is it that it can't resolve all of the dependent implicits and then registers the error with the first, i.e. an N cannot be found to satisfy ToArity, ToInt and SizedHListAux?
Update: I just saw your edit, which means you've already addressed the problem noted in the first couple of paragraphs here, but I hope the rest is useful.
The problem is that your SizedHListAux instance isn't being inferred:
scala> implicitly[SizedHListAux[String, _1, String :: HNil]]
<console>:25: error: could not find implicit value for parameter e...
Fortunately this is an easy fix:
object SizedHListAux {
implicit def base[A] = new SizedHListAux[A, _0, HNil] {}
implicit def induct[A, H <: HList, N <: Nat, P <: Nat](implicit
r: PredAux[N, P],
k: SizedHListAux[A, P, H]
) = new SizedHListAux[A, N, A :: H] {}
}
I've just removed the R <: PredAux[N, P] type parameter and typed r appropriately. I've also removed the unused type parameter H on base, even though it wasn't causing problems—it just wasn't doing anything.
That's almost all—now all the instances for fromF1 get inferred:
scala> SomeFun.fromF1((tfn, thedata))
res0: SomeFun{type Result = (Unit, List[String])} = SomeFun$$anon$1#7eacbeb
You're still not going to get a view from the type of (tfn, thedata) to SomeFun, though. Consider the following simplified example:
scala> trait Foo
defined trait Foo
scala> trait Bar[A, B]
defined trait Bar
scala> implicit def toInt[F <: Foo, X](f: F)(implicit ev: Bar[F, X]) = 42
toInt: [F <: Foo, X](f: F)(implicit ev: Bar[F,X])Int
scala> implicit object fooBar extends Bar[Foo, String]
defined module fooBar
scala> toInt(new Foo {})
res0: Int = 42
scala> implicitly[Foo => Int]
<console>:12: error: No implicit view available from Foo => Int.
implicitly[Foo => Int]
So even though we have an implicit method in scope that will transform a Foo into an Int, that X causes problems for the compiler when it tries to find a view from Foo to Int.
In your case I'd avoid this limitation by skipping the SomeFun business and having a method that takes a (P => T, List[A]) and returns a (T, List[A]).
I'll also observe that both ToArity and SizedHListAux seem unnecessary, since you can gather the same evidence with TuplerAux, LengthAux, and LUBConstraint. For example:
import shapeless._
trait SomeFun {
type Result
def apply(): Result
}
implicit def fromF1[T, A, P <: Product, N <: Nat, H <: HList](
f1: (P => T, List[A])
)(implicit
tp: TuplerAux[H, P],
hl: LengthAux[H, N],
toHL: FromTraversable[H],
allA: LUBConstraint[H, A],
toI: ToInt[N]
) = new SomeFun {
type Result = (T, List[A])
def apply(): Result = {
val (f, as) = f1
val (ts, rest) = (as.take(toI()), as.drop(toI()))
f((toHL(ts).get).tupled) -> rest
}
}
And then:
val tfn = (x: (String, String)) => println("%s and %s".format(x._1, x._2))
val thedata = List("foo", "bar", "baz", "bob")
val sf = fromF1((tfn, thedata))
And finally:
scala> sf()
foo and bar
res2: (Unit, List[String]) = ((),List(baz, bob))
No annoying prodN boilerplate necessary.