有没有办法扩展类型声明?

时间:2015-06-24 07:29:07

标签: scala 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尾部,它被extendBy掉落操作

2 个答案:

答案 0 :(得分:5)

情况并不像lmm的答案所暗示的那么糟糕,部分原因是因为Shapeless提供了一个ExtendBy类型的类,它打包了ExtendLeftByExtendRightBy。因此,如果你真的想要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(_))

即使您确实需要直接使用ExtendLeftByExtendRightBy,您也可以使用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(_))

这适用于任何两个副产品,而不仅仅是F12F34

答案 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]))
正如我所说,繁琐,但它应该工作。 (ExtendRightExtendLeftextendRightByextendLeftBy方法使用的类型 - 任何类似依赖类型的函数都可能具有类似的&#34;帮助&#34;类型)