我试图使用无形的'用于错误类型聚合的Coproduct。以下是尝试隔离我目前的问题:
import shapeless._
case object F1
case object F2
type F12 = F1.type :+: F2.type :+: CNil
case object F3
case object F4
type F34 = F3.type :+: F4.type :+: CNil
type F1234 = F1.type :+: F2.type :+: F3.type :+: F4.type :+: CNil
def custom(f: Either[F12, F34]): F1234 = // how can I declare the resulting type?
f.fold(_.extendRightBy[F34], _.extendLeftBy[F12])
object F1234Handler extends Poly1 {
implicit def caseF1 = at[F1.type](_ => "got F1")
implicit def caseF2 = at[F2.type](_ => "got F2")
implicit def caseF3 = at[F3.type](_ => "got F3")
implicit def caseF4 = at[F4.type](_ => "got F4")
}
custom(Left(Coproduct[F12](F2))).fold(F1234Handler) // got F2
如何在自定义折叠中声明结果类型而不必重复自己?理想情况下,我不想以我的方式声明F1234,我想通过仅仅创建两个现有类型声明F12和F34的联合来声明它。这样,每当我向这些声明中的任何一个添加另一个失败类型时,我都不需要更新F1234声明。我可以声明类型F1234 = F1.type:+:F2.type:+:F34但我不能声明类型F1234 = F12:+:F34,因为F12的CNil尾部,它被extendBy掉落操作
答案 0 :(得分:5)
情况并不像lmm的答案所暗示的那么糟糕,部分原因是因为Shapeless提供了一个ExtendBy
类型的类,它打包了ExtendLeftBy
和ExtendRightBy
。因此,如果你真的想要custom
的返回类型,你没有自己计算并手写,那么你可以使用ExtendBy
:
import shapeless._, ops.coproduct.ExtendBy
case object F1
case object F2
type F12 = F1.type :+: F2.type :+: CNil
case object F3
case object F4
type F34 = F3.type :+: F4.type :+: CNil
def custom(f: Either[F12, F34])(implicit ext: ExtendBy[F12, F34]): ext.Out =
f.fold(ext.right(_), ext.left(_))
即使您确实需要直接使用ExtendLeftBy
和ExtendRightBy
,您也可以使用Aux
类型和单个共享来说服编译器它们具有相同的输出类型类型参数。所以不是这个(lmm代码的完整工作版本):
import ops.coproduct.{ ExtendLeftBy, ExtendRightBy }
def custom[ERO, ELO](f: Either[F12, F34])(implicit
el: ExtendRightBy[F12, F34] { type Out = ELO },
er: ExtendLeftBy[F12, F34] { type Out = ERO },
w: ELO =:= ERO
): ERO = f.fold(l => w(el(l)), er(_))
你会写这个:
def custom[Out <: Coproduct](f: Either[F12, F34])(implicit
extL: ExtendRightBy.Aux[F12, F34, Out],
extR: ExtendLeftBy.Aux[F12, F34, Out]
): Out = f.fold(extL(_), extR(_))
在大多数情况下,如果您静态地知道输入类型,那么您只需自己写出返回类型并完全跳过隐式参数业务。只有在使用泛型类型时才需要隐式证据,如下所示:
def custom[A <: Coproduct, B <: Coproduct](f: Either[A, B])(implicit
ext: ExtendBy[A, B]
): ext.Out = f.fold(ext.right(_), ext.left(_))
这适用于任何两个副产品,而不仅仅是F12
和F34
。
答案 1 :(得分:3)
Scala依赖类型总是有点奇怪和麻烦。类型级函数被编码为implicits,结果为类型成员,但就scala编译器而言,它只是一个类型成员。所以你必须做这样的事情:
def custom(f: Either[F12, F34])(implicit er: ExtendRight[F12, F34]): er.Out
= f.fold(_.extendRightBy[F34], _.extendLeftBy[F12])
不幸的是,由于编译器无法判断extendLeftBy[F12]
的输出是同一类型,因此无法工作。我们知道这两种类型总是相同的,但是编译器没有,所以我们必须要求见证(实际上总是存在)。类似的东西:
def custom(f: Either[F12, F34])(implicit er: ExtendRight[F12, F34],
el: ExtendLeft[F34, F12])(implicit w: er.Out =:= el.Out): er.Out
= f.fold(_.extendRightBy[F34], w(_.extendLeftBy[F12]))
不幸的是,即使这样做也不起作用,因为我们的类型参数不允许依赖于同一列表中的类型参数,我们只能有一个隐式列表。所以我们必须提升&#34;相反,这些类型是类型参数:
def custom[ERO, ELO](f: Either[F12, F34])(
implicit er: ExtendRight[F12, F34]{type Out = ERO},
el: ExtendLeft[F34, F12]{type Out = ELO}, w: ELO =:= ERO): ELO
= f.fold(_.extendRightBy[F34], w(_.extendLeftBy[F12]))
正如我所说,繁琐,但它应该工作。 (ExtendRight
和ExtendLeft
是extendRightBy
和extendLeftBy
方法使用的类型 - 任何类似依赖类型的函数都可能具有类似的&#34;帮助&#34;类型)