【问题标题】:Shapeless: Mapping an natural transformation over a KListShapeless:在 KList 上映射自然变换
【发布时间】:2021-02-13 18:27:11
【问题描述】:

有没有办法将自然变换(例如 Option ~> Either[String, *])映射到 KList(例如 HListUnaryTCConstraint)上?这对 KList 来说似乎是很自然的事情。

具体来说,我正在尝试执行以下操作:

object myNaturalTransformation extends (Option ~> Either[String, *]) {
  def apply[T](a: Option[T]): Either[String, T] = a.toRight("oh noe!")
}

def doStuff[KList <: HList: *->*[Option]#λ](klist: KList) = {
  klist.map(myNaturalTransformation)
}

我知道缺少的部分是执行.map 所需的Mapper,而Shapeless 无法从myNaturalTransformations 案例和UnaryTCConstraint 中生成一个。是否有可能通过其他方式获得?或者是否有另一种方法来映射我忽略的 KList(除了将 Mapper 传递给 doStuff 函数)?

我能够编写我自己的UnaryTCConstraint 版本,其中包含一个

def mapper[G[_], HF <: ~>[TC, G]](hf: HF): Mapper[hf.type, L]

为给定的自然变换显式生成映射器。但是我很好奇是否可以通过 Shapeless 的 UnaryTCConstraint 实现来做到这一点。

【问题讨论】:

    标签: scala typeclass implicit shapeless context-bound


    【解决方案1】:

    UnaryTCConstraint (*-&gt;*) 不用于映射,它是一个约束(常见于HLists、Coproducts、案例类和密封特征)。对于映射,有 NatTRelMappedComappedMapper 等类型类(HLists 和 Coproducts 分开)。

    尝试约束和类型类

    def doStuff[KList <: HList: *->*[Option]#λ, L <: HList](klist: KList)(implicit 
      natTRel: NatTRel[KList, Option, L, Either[String, *]]
    ): L = natTRel.map(myNaturalTransformation, klist)
    

    或者直接输入类

    def doStuff[KList <: HList, L <: HList](klist: KList)(implicit 
      natTRel: NatTRel[KList, Option, L, Either[String, *]]
    ): L = natTRel.map(myNaturalTransformation, klist)
    

    或通过PartiallyApplied模式隐藏类型参数L

    def doStuff[KList <: HList] = new PartiallyAppliedDoStuff[KList]
    
    class PartiallyAppliedDoStuff[KList <: HList] {
      def apply[L <: HList](klist: KList)(implicit 
        natTRel: NatTRel[KList, Option, L, Either[String, *]]
      ): L = natTRel.map(myNaturalTransformation, klist)
    }
    

    或通过存在隐藏类型参数L(但返回类型不精确)

    def doStuff[KList <: HList](klist: KList)(implicit 
      natTRel: NatTRel[KList, Option, _, Either[String, *]]
    ) = natTRel.map(myNaturalTransformation, klist)
    

    或使用扩展方法

    implicit class NatTRelOps[KList <: HList](val klist: KList) extends AnyVal {
      def map[F[_], G[_], L <: HList](f: F ~> G)(implicit 
        natTRel: NatTRel[KList, F, L, G]
      ): L = natTRel.map(f, klist)
    } 
    
    def doStuff[KList <: HList, L <: HList](klist: KList)(implicit 
      natTRel: NatTRel[KList, Option, L, Either[String, *]]
    ): L = klist.map(myNaturalTransformation)
    

    测试:

    doStuff(Option(1) :: Option("a") :: HNil)           // compiles
    //doStuff(Option(1) :: Option("a") :: true :: HNil) // doesn't compile
    

    【讨论】:

    • 是否有任何资源可以了解无形约束?如果 UnaryTCConstraint 不是用于映射,那么它的目的是什么?此外,我只能在 Shapeless 2.3.3 中找到 HLists 的约束,而不是你提到的 Coproducts、案例类和密封特征。
    • @mrArkwright 好吧,Shapeless 文档并不完美。有bookwikiexamplestests。例如测试constraints
    • @mrArkwright 如果您查看UnaryTCConstraint sources,很明显它是一个类型类,对于类型A,要么存在实例UnaryTCConstraint[A],要么没有实例。 *-&gt;* 用于上下文绑定。类型类中没有类型成员或方法。所以没有办法将A 类型转换为B 或将值a: A 转换为b: B。并为HNilCNilH :: TH :+: TGeneric 定义了实例。
    • @mrArkwright 它的目的是约束,而不是映射。 IE。验证方法或类型类的类型参数L 的形式为TC[A] :: TC[B] :: ... :: HNilTC[A] :+: TC[B] :+: ... :+: CNilSomeCaseClass(tca: TC[A], tcb: TC[B], ...)
    • 直到 2.3.3 UnaryTCConstraint 似乎仅限于 HList。从 2.4.0-M1 开始,它也适用于 CoproductGeneric。这解释了我对此的困惑。
    猜你喜欢
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多