gpt4 book ai didi

scala - shapeless HList 到 TupleN,其中元组形状不需要与 HList 形状完全匹配

转载 作者:行者123 更新时间:2023-12-03 23:57:30 25 4
gpt4 key购买 nike

我想创建相当于:

def toTupleN[A1, ..., AN, L <: HList](l: L): TupleN[A1, ..., AN]

代码使用 toTupleN只有当只有一个 N 时才应该编译 l 中的值组合可以从中创建元组。其他任何事情都应该产生编译时错误。应考虑可用的隐式转换。注意 l的大小没有限制或其中值的排序。

例子:

val l = 23 :: (1, "wibble") :: (2, "wobble") :: "foo" :: HNil
// l: shapeless.::[Int,shapeless.::[(Int, String),shapeless.::[(Int, String),shapeless.::[String,shapeless.HNil]]]] = 23 :: (1,wibble) :: (2,wobble) :: foo :: HNil

val t2: (String, Int) = toTuple2(l)
// t2: (String, Int) = (foo,23)

val nope: (String, String) = toTuple2(l)
// Compiler error because no combination of l's values can create nope

val nein: ((Int, String)) = toTuple2(l)
// Another compiler error because there is more than one way l's values can create nein

这个问题来自 answer到以下 question .这个问题中更通用的机制可用于创建数据结构和使用 FunctionN#tupled 调用任何标准函数(其参数是不同类型的)。 .

更新:

使用子类型定义所需行为的一些示例:

trait A
trait B extends A
trait C extends A

val a: A
val b: B
val c: C

toTuple2[(A, Int)](5 :: b :: HNil) // (b, 5): subtypes match supertypes when there is no exact match
toTuple2[(A, Int)](5 :: b :: a :: HNil) // (a, 5): only one exact match is available
toTuple2[(A, Int)](5 :: a :: a :: HNil) // compile error: more than one exact match is available
toTuple2[(A, Int)](5 :: b :: c :: HNil) // compile error: more than one inexact match is available

最佳答案

我无法按照您想要的方式进行目标类型推断,但作为补偿,我已经通过 shapeless 的 Generic 推广到任意产品类型,

import shapeless._, ops.hlist._, test._

object Demo {
trait UniqueSelect[L <: HList, M <: HList] {
def apply(l: L): M
}

object UniqueSelect {
implicit def hnil[L <: HList]: UniqueSelect[L, HNil] =
new UniqueSelect[L, HNil] {
def apply(l: L): HNil = HNil
}

implicit def hcons[L <: HList, H, T <: HList, S <: HList]
(implicit
pt: Partition.Aux[L, H, H :: HNil, S],
ust: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(l)
h :: ust(s)
}
}
}

def toProductUniquely[P <: Product] = new ToProductUniquely[P]
class ToProductUniquely[P <: Product] {
def apply[L <: HList, M <: HList](l: L)
(implicit gen: Generic.Aux[P, M], up: UniqueSelect[L, M]): P =
gen.from(up(l))
}

val l = 23 :: (1, "wibble") :: (2, "wobble") :: "foo" :: HNil

val t2 = toProductUniquely[(String, Int)](l)
typed[(String, Int)](t2)
assert(t2 == ("foo", 23))

illTyped("""
toProductUniquely[(String, String)](l)
""")

illTyped("""
toProductUniquely[Tuple1[(Int, String)]](l)
""")
}

更新 1

如果我们说我们有类型 A,那么添加对被请求类型的子类型满足的选择的支持是相当简单的。和 B <: A然后选择 A来自 A :: B :: HNil是不明确的,因为两个元素都符合 A .这可以通过添加 SubtypeUnifier 来完成。给 hcons 之前定义中的证人,
import shapeless._, ops.hlist._, test._

object Demo extends App {
trait UniqueSelect[L <: HList, M <: HList] {
def apply(l: L): M
}

object UniqueSelect {
implicit def hnil[L <: HList]: UniqueSelect[L, HNil] =
new UniqueSelect[L, HNil] {
def apply(l: L): HNil = HNil
}

implicit def hcons[L <: HList, M <: HList, H, T <: HList, S <: HList]
(implicit
su: SubtypeUnifier.Aux[L, H, M],
pt: Partition.Aux[M, H, H :: HNil, S],
upt: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(su(l))
h :: upt(s)
}
}
}

def toProductUniquely[P <: Product] = new ToProductUniquely[P]
class ToProductUniquely[P <: Product] {
def apply[L <: HList, M <: HList](l: L)
(implicit gen: Generic.Aux[P, M], up: UniqueSelect[L, M]): P =
gen.from(up(l))
}

class A
class B extends A
class C

val ac = new A :: new C :: HNil
val bc = new B :: new C :: HNil
val abc = new A :: new B :: new C :: HNil

// Exact match
val tac = toProductUniquely[(A, C)](ac)
typed[(A, C)](tac)

// Subtype
val tbc = toProductUniquely[(A, C)](bc)
typed[(A, C)](tbc)

// Exact match again
val tabc = toProductUniquely[(B, C)](abc)
typed[(B, C)](tabc)

// Ambiguous due to both elements conforming to A
illTyped("""
toProductUniquely[(A, C)](abc)
""")
}

更新 2

我们还可以适应统一语义,该语义优先选择精确匹配,然后回退到您更新的问题中所述的唯一子类型。我们通过组合上述两个解决方案中的实例来做到这一点:第一个正常优先级的完全匹配实例和低优先级的子类型匹配实例,
import shapeless._, ops.hlist._, test._

object Demo extends App {
trait UniqueSelect[L <: HList, M <: HList] {
def apply(l: L): M
}

object UniqueSelect extends UniqueSelect0 {
implicit def hnil[L <: HList]: UniqueSelect[L, HNil] =
new UniqueSelect[L, HNil] {
def apply(l: L): HNil = HNil
}

implicit def hconsExact[L <: HList, H, T <: HList, S <: HList]
(implicit
pt: Partition.Aux[L, H, H :: HNil, S],
upt: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(l)
h :: upt(s)
}
}
}

trait UniqueSelect0 {
implicit def hconsSubtype[L <: HList, M <: HList, H, T <: HList, S <: HList]
(implicit
su: SubtypeUnifier.Aux[L, H, M],
pt: Partition.Aux[M, H, H :: HNil, S],
upt: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(su(l))
h :: upt(s)
}
}
}

def toProductUniquely[P <: Product] = new ToProductUniquely[P]
class ToProductUniquely[P <: Product] {
def apply[L <: HList, M <: HList](l: L)
(implicit gen: Generic.Aux[P, M], up: UniqueSelect[L, M]): P = gen.from(up(l))
}

trait A
trait B extends A
trait C extends A

val a: A = new A {}
val b: B = new B {}
val c: C = new C {}

// (b, 5): subtypes match supertypes when there is no exact match
toProductUniquely[(A, Int)](5 :: b :: HNil)

// (a, 5): only one exact match is available
toProductUniquely[(A, Int)](5 :: b :: a :: HNil)

// compile error: more than one exact match is available
illTyped("""
toProductUniquely[(A, Int)](5 :: a :: a :: HNil)
""")

// compile error: more than one inexact match is available
illTyped("""
toProductUniquely[(A, Int)](5 :: b :: c :: HNil)
""")
}

关于scala - shapeless HList 到 TupleN,其中元组形状不需要与 HList 形状完全匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34861074/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com