简体   繁体   中英

Function that retrieves element from HList (while preserving its type)

I have this type which will be generated via shapeless:

type hlistt = STUDENT.type :: AUTO_LOANS.type :: HNil

Basically I have a bunch of case objects extending a trait so I managed to create a method which gives me instances of all case objects as an HList

Then using import shapeless.ops.hlist.Last and init I wrote a method to retrieve one of the nodes in the HList if the value is equal to the string "student":

def getLast(hl:hlistt) = {
  val last0=Last[hlistt]
  val la=last0(hl)

  if (la.value == "student") la
  else init(hl)
}

The issue is that if I call this method I will not get the correct node type from the HList.

getLast(STUDENT :: AUTO_LOANS :: HNil)

The method works and returns the node but the type is off:

Product with Serializable = STUDENT :: HNil

Do I need some Witness/Aux implicits to return the correct type?

I am not quite sure what you want to do. Given:

type hlistt = STUDENT.type :: AUTO_LOANS.type :: HNil

Last[hlistt] will resolve to AUTO_LOANS.type (your true if branch) while init will resolve to STUDENT :: HNil (your false if branch)

the LUB (least upper bound) of these types will be Product with Serializable so that's why you see that.

If you want to check a runtime property of a member of the hlist you'll have to thread the appropriate typebounds and result types through by deriving them with the appropriate machinery. In this case that is already given by shapeless.

https://scalafiddle.io/sf/fdtn3cz/0

Is this what you wanted?

EDIT: I also just read

I have this type which will be generated dynamically:

what do you exactly mean by "dynamically"? because unless you can specify some compile time properties, shapeless might not be the solution you're looking for.

la is of type AUTO_LOANS.type , init(hl) is of type STUDENT.type :: HNil , so

if (la.value == "student") la
else init(hl)

is of type Any (or Product with Serializable ).

If you would like to return values of different types from different branches you need a Poly .

import shapeless.{Poly1, Witness}

object myPoly extends Poly1 {
  implicit def studentCase: Case.Aux[Witness.`"student"`.T, STUDENT.type] = 
    at(_ => STUDENT)
  implicit def autoLoansCase: Case.Aux[Witness.`"auto-loans"`.T, AUTO_LOANS.type] = 
    at(_ => AUTO_LOANS)
}

import shapeless.syntax.singleton._
println(
  myPoly("student".narrow)
) // STUDENT

println(
  myPoly("auto-loans".narrow)
) // AUTO_LOANS

// println(
//   myPoly("abc".narrow)
// )  // doesn't compile

This approach works if the string is known at compile time.

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