有没有办法扩展类型声明?
Is there a way to extend type declarations?
我正在尝试使用 shapeless 的 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 尾部被丢弃扩展操作。
Scala 依赖类型总是有点奇怪和麻烦。类型级函数被编码为隐含的结果作为类型成员,但就 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]))
不幸的是,即使这样也行不通,因为我们的类型参数不允许依赖于同一列表中的类型参数,而且我们只能有一个隐式列表。所以我们必须 "lift" 这些类型改为类型参数:
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
方法使用的类型 - 任何类似的依赖类型函数可能具有类似的 "helper" 类型)。
情况并不像 lmm 的回答所暗示的那么糟糕,部分原因是 Shapeless 提供了一个 ExtendBy
类型 class 来打包 ExtendLeftBy
和 ExtendRightBy
。所以如果你真的想要 custom
的 return 类型而不是你自己计算并手写出来,你可以使用 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(_))
在大多数情况下,如果您静态地知道输入类型,那么您只需自己写出 return 类型并完全跳过隐式参数业务。只有在使用泛型类型时才需要隐式证据,如下所示:
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
。
我正在尝试使用 shapeless 的 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 尾部被丢弃扩展操作。
Scala 依赖类型总是有点奇怪和麻烦。类型级函数被编码为隐含的结果作为类型成员,但就 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]))
不幸的是,即使这样也行不通,因为我们的类型参数不允许依赖于同一列表中的类型参数,而且我们只能有一个隐式列表。所以我们必须 "lift" 这些类型改为类型参数:
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
方法使用的类型 - 任何类似的依赖类型函数可能具有类似的 "helper" 类型)。
情况并不像 lmm 的回答所暗示的那么糟糕,部分原因是 Shapeless 提供了一个 ExtendBy
类型 class 来打包 ExtendLeftBy
和 ExtendRightBy
。所以如果你真的想要 custom
的 return 类型而不是你自己计算并手写出来,你可以使用 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(_))
在大多数情况下,如果您静态地知道输入类型,那么您只需自己写出 return 类型并完全跳过隐式参数业务。只有在使用泛型类型时才需要隐式证据,如下所示:
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
。