St.
St.

Reputation: 533

Merging HList elements

I have a simple service definition

trait Service[-Req, +Rep] extends (Req => Future[Rep]) {
    def apply(request: Req): Future[Rep]
}

and a method how to chain services:

implicit class ServiceOps1[Req, RepIn](service: Service[Req, RepIn]) {
    def -->[RepOut](next: Service[RepIn, RepOut]): Service[Req, RepOut] =
        (req: Req) => service(req) flatMap next
}

I would like to put all my service (in assumption that they could be composed) into HList and then build from HList a composition of service.

Here is my Resolver

  trait Resolver[L <: HList, In] {
    type Out
    def apply(l: L): Service[In, Out]
  }

  object Resolver {
    def apply[L <: HList, In](implicit resolver: Resolver[L, In]): Aux[L, In, resolver.Out] = resolver

    type Aux[L <: HList, In, Out0] = Resolver[L, In] { type Out = Out0 }

    implicit def hsingleResolver[I, O, S <: Service[I, O]]: Aux[S :: HNil, I, O] =
      new Resolver[S :: HNil, I] {
        type Out = O
        def apply(l : S :: HNil): Service[I, Out] = l.head
      }

    implicit def hlistResolver[I, O, S <: Service[I, O], T <: HList](implicit res : Resolver[T, O]): Aux[S :: T, I, res.Out] =
      new Resolver[S :: T, I] {
        type Out = res.Out

        def apply(l: S :: T): Service[I, res.Out] = l.head --> res(l.tail)
      }
  }

I have a service

object S extends Service[Int, String] {
    def apply(request: Int): Future[String] =  Future successful request.toString
}

When I try to resolve the simple chain

implicitly[Resolver[S.type :: HNil, Int]].apply(S :: HNil)

I got an implicit not found error.

Upvotes: 1

Views: 156

Answers (2)

Jasper-M
Jasper-M

Reputation: 15086

The problem lies in the type signature of your implicits: implicit def hsingleResolver[I, O, S <: Service[I, O]]: Aux[S :: HNil, I, O]. Here because of S <: Service[I, O] you expect O to be inferred based on the type of S, but unfortunately that's not how it works. The S <: Service[I, O] clause in the type parameter list is not taken into consideration for inferring the type arguments. What happens when you invoke implicitly[Resolver[S.type :: HNil, Int]] is that the compiler sees that S = S.type, I = Int and O is unknown so O = Nothing. Then afterwards it goes on to check that S <: Service[Int,Nothing] which is false and implicit search fails.

So to fix this you have to make the fact that S <: Service[I, O] part of the implicit search/type inference process. For instance in one of these ways:

implicit def hsingleResolver[I, O, S](implicit ev: S <:< Service[I,O]): Aux[S :: HNil, I, O] // option 1
implicit def hsingleResolver[I, O, S]: Aux[(S with Service[I,O]) :: HNil, I, O] // option 2

As a side note: wouldn't it make more sense to define Resolver in the following way?

trait Resolver[L <: HList] {
  type In
  type Out
  def apply(l: L): Service[In, Out]
}

object Resolver {
  def apply[L <: HList](implicit resolver: Resolver[L]): Aux[L, resolver.In, resolver.Out] = resolver

  type Aux[L <: HList, In0, Out0] = Resolver[L] { type In = In0; type Out = Out0 }

  ...
}

Because In is also dependent on L, just like Out.

Upvotes: 1

Michael Pollmeier
Michael Pollmeier

Reputation: 1380

Not sure why this was down voted, maybe you could have made a sample repo available. Anyway, here's a partial answer, maybe this get's you back on track.

1) enable debug options for implicits in your build.sbt: scalacOptions += "-Xlog-implicits"

2) define a resolver for HNil: implicit def hNilResolver[I]: Aux[HNil, I, HNil] = ???

3) following the debug output, fix the remainder :)

Upvotes: 1

Related Questions