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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With