简体   繁体   中英

Shapeless HList literal-types erased?

I am trying to filter an HList by a literal type but can't get it working, it just filters all strings:

import shapeless._
import shapeless.ops.hlist._

def filterLabel[Labels <: HList, Label, Out <: HList](labels: Labels, label: Label)(
implicit sel: Filter.Aux[Labels, label.type, Out]): Out = sel(labels)
val a: "a" = "a"
val b: "b" = "b"
val c1: 1 = 1
val h1 = a :: b :: a :: c1 :: HNil
//result: hl: String :: String :: String :: Int :: HNil = "a" :: "b" :: "a" :: 1 :: HNil 
//looks like literal-types are no longer present?
filterLabel(h1, "a")

I tested this with Scala org.typelevel 2.12.4-bin-typelevel-4. Should this be possible? Should HList's maintain literal-types?

The code can be run in Scala with compiler option "-Yliteral-types" for Scala 2.12. Ammonite for example:

curl -s https://raw.githubusercontent.com/typelevel/scala/typelevel-readme/try-typelevel-scala.sh | bash
repl.compiler.settings.YliteralTypes.value = true
import $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._, shapeless.ops.hlist._
//now paste the above code

UPDATE: I now have the following for just Lightbend Scala:

import shapeless._
import shapeless.ops.hlist._
import syntax.singleton._

implicit def stringToF(label: String) = () => label.narrow

def filterLabel[Labels <: HList, T, Label <: String, Out <: HList](labels: Labels, label: () => Label)(
implicit
sel: Filter.Aux[Labels, Label, Out]): Out = sel(labels)

val h1 = "a".narrow :: "b".narrow :: "a".narrow :: "c".narrow :: HNil

filterLabel(h1, "a")
//res92: a :: a :: HNil = "a" :: "a" :: HNil

it also works on a hlist of objects with literal-typed parameters:

import shapeless._
import shapeless.ops.hlist._
import syntax.singleton._

class MyLabel[L <: String](label: L)
object MyLabel {
  def apply(label: String) = new MyLabel(label.narrow)
}

implicit def stringToMyLabel(label: String) = new MyLabel(label.narrow)

def filterLabel[Labels <: HList, T, Label <: String, Out <: HList](labels: Labels, label: MyLabel[Label])(
  implicit
  sel: Filter.Aux[Labels, MyLabel[Label], Out]): Out = sel(labels)

val h1 = MyLabel("a") :: MyLabel("b") :: MyLabel("a") :: MyLabel("c") :: HNil

filterLabel(h1, "a")
//res88: MyLabel[a] :: MyLabel[a] :: HNil = ammonite.$sess.cmd83$MyLabel@55a5d73c :: ammonite.$sess.cmd83$MyLabel@eb78e11 :: HNil

I guess this works with Lightbend Scala:

import shapeless.ops.hlist.Filter
import shapeless.{::, HList, HNil, Witness}
import shapeless.syntax.singleton._

def filterLabel[Labels <: HList, Label, Out <: HList](labels: Labels, label: Label)(
  implicit sel: Filter.Aux[Labels, Label, Out]): Out = sel(labels)

val h1: Witness.`"a"`.T :: Witness.`"b"`.T :: Witness.`"a"`.T :: Witness.`1`.T :: HNil = 
  "a".narrow :: "b".narrow :: "a".narrow :: 1.narrow :: HNil

filterLabel(h1, "a".narrow) // a :: a :: HNil

You can try to replace Witness.`X`.T with just X for Typelevel Scala.

That's the way I do it on Typelevel scala 2.12.4

trait Narrow[T] {
  def value: T
}

object Narrow {
  def apply[T](implicit n: Narrow[T]): n.type = n

  implicit def hnil = new Narrow[HNil] { def value = HNil }

  implicit def cons[T, L <: HList: Narrow](implicit t: ValueOf[T]) = new Narrow[T::L] {
   def value = t.value :: Narrow[L].value
  }
}

Then on REPL

scala> val h1 = Narrow["a"::"b"::"a"::1::HNil].value
h1: "a" :: "b" :: "a" :: 1 :: shapeless.HNil = a :: b :: a :: 1 :: HNil

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