Let's say we have this:
def join[A, B](a: A, b: B)
def join[A, B, C](a: A, b: B, c: C) // etc
Basically a lot of overloaded methods for up to 20 type arguments.
And then the following which is creating a K-list by enforcing an unary type constraint on the HList
such that all inner elements are of type Task[_]
.
class Test(L <: HList : *->*[Task]#λ](val deps : L = HNil)
Is it possible to convert the deps
to a Task[A1, A2, A3]
etc where the A1
, A2
, A3
are the inner types of the HList
?
So for:
val hls = Task(1) :: Task("string") :: Task9(true) :: HNil
Retrieve a:
Task[(Int, String, Boolean)]
And do this for any number of arguments? I can convert a Task[A], Task[B]
to a Task[A, B]
for any number of arguments already, just need to get from HList
to varargs
or similar.
From Scala's perspective those
join
methods don't have anything in common but their name, so it's not really possible to use them in this context without a lot of boilerplate (or a custom macro). Instead it's possible to use thejoin
on Twitter'sFuture
itself repeatedly. You can do this by creating a new type class:This type class will witness that an hlist
L
is made up entirely of futures, and it will give us a way to sequence those futures into aFuture[T]
, whereT
is the tuple made up of the types inside of each of the futures inL
(the*->*
gives us the first part of this, but doesn't support the second, or even any way to do the second part at all conveniently).The companion object does all the interesting work:
We're using induction here—first we describe how to sequence
HNil
(our base case), and then we describe how to sequenceFuture[H] :: T
given that we know how to sequenceT
.Next we'll define a method to make this easy to use:
And then if we've got some examples:
We can sequence them:
You could also require a
FutureSequence
instance in your class's constructor, or on specific methods on your class.