简体   繁体   中英

How to extract an element from an HList with a specific (parameterized) type

I'm chaining transformations, and I'd like to accumulate the result of each transformation so that it can potentially be used in any subsequent step, and also so that all the results are available at the end (mostly for debugging purposes). There are several steps and from time to time I need to add a new step or change the inputs for a step.

HList seems to offer a convenient way to collect the results in a flexible but still type-safe way. But I'd rather not complicate the actual steps by making them deal with the HList and the accompanying business.

Here's a simplified version of the combinator I'd like to write, which isn't working. The idea is that given an HList containing an A, and the index of A, and a function from A -> B, mapNth will extract the A, run the function, and cons the result onto the list. The resulting extended list captures the type of the new result, so several of these mapNth-ified steps can be composed to produce a list containing the result from each step:

def mapNth[L <: HList, A, B]
    (l: L, index: Nat, f: A => B)
    (implicit at: shapeless.ops.hlist.At[L, index.N]):
    B :: L =
  f(l(index)) :: l

Incidentally, I'll also need map2Nth taking two indices and f: (A, B) => C , but I believe the issues are the same.

However, mapNth does not compile, saying l(index) has type at.Out , but f 's argument should be A . That's correct, of course, so what I suppose I need is a way to provide evidence that at.Out is in fact A (or, at.Out <: A ).

Is there a way to express that constraint? I believe it will have to take the form of an implicit, because of course the constraint can only be checked when mapNth is applied to a particular list and function.

You're exactly right about needing evidence that at.Out is A , and you can provide that evidence by including the value of the type member in at 's type:

def mapNth[L <: HList, A, B]
    (l: L, index: Nat, f: A => B)
    (implicit at: shapeless.ops.hlist.At[L, index.N] { type Out = A }):
    B :: L =
  f(l(index)) :: l

The companion objects for type classes like At in Shapeless also define an Aux type that includes the output type as a final type parameter.

def mapNth[L <: HList, A, B]
    (l: L, index: Nat, f: A => B)
    (implicit at: shapeless.ops.hlist.At.Aux[L, index.N, A]):
    B :: L =
  f(l(index)) :: l

This is pretty much equivalent but more idiomatic (and it looks a little nicer).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM