约束左右类型的签名

时间:2015-09-16 14:05:32

标签: scala

给出SuccessFailure的以下AST:

sealed trait Success
case object FooGood extends Success
case object BarGood extends Success

sealed trait Failure
case object FooBad extends Failure
case object BarBad extends Failure

方法签名:

def go[A <: Failure, B <: Success](x: Int): Either[A, B] = ???

但是,我希望将LeftRight类型限制为特定于FooBar

但是下面的代码编译(违背我的意愿):

scala> go[FooBad.type, BarGood.type](5)
scala.NotImplementedError: an implementation is missing

如何在编译时实现此约束?

3 个答案:

答案 0 :(得分:4)

这是一个依赖于类型类的解决方案。值得注意的是,它不需要为每个(一对)AST节点手动定义新的类型实例。 它确实涉及为每对引入一个共同的超类型(虽然你在技术上没有将它用作基类,但它仅用作标记类型)。 / p>

sealed trait Success[T]
abstract sealed class Foo
abstract sealed class Bar
case object FooGood extends Foo with Success[Foo]
case object BarGood extends Bar with Success[Bar]
sealed trait Failure[T]
case object FooBad extends Foo with Failure[Foo]
case object BarBad extends Bar with Failure[Bar]

@annotation.implicitNotFound("Expecting reciprocal Failure and Success alternatives, but got ${A} and ${B}")
trait IsGoodAndBadFacet[A,B]
implicit def isGoodAndBadFacet[T,A,B](implicit e1: A <:< Failure[T], e2: B<:<Success[T]): IsGoodAndBadFacet[A,B] = null

def go[A, B](x: Int)(implicit e: IsGoodAndBadFacet[A,B]): Either[A, B] = ???

Repl测试:

scala> go[FooBad.type, BarGood.type](5)
<console>:17: error: Expecting reciprocal Failure and Success alternatives, but got FooBad.type and BarGood.type
              go[FooBad.type, BarGood.type](5)
                                           ^

scala> go[FooBad.type, FooGood.type](5)
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at .go(<console>:11)
  ... 33 elided

scala> go[BarBad.type, BarGood.type](5)
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at .go(<console>:11)
  ... 33 elided

答案 1 :(得分:2)

你遇到的问题是编译器不知道WITH SET [OP] AS NONEMPTY([Z_OP].[LEVEL01].MEMBERS, [MEASURES].[EssentialMeasureInCube]) SET [NV] AS NONEMPTY([Z_NV].[LEVEL01].MEMBERS, [MEASURES].[EssentialMeasureInCube]) SET [DT] AS NONEMPTY([Z_DT].[LEVEL01].MEMBERS, [MEASURES].[EssentialMeasureInCube]) SELECT NON EMPTY { } ON 0, NON EMPTY [OP] * [NV] * [DT] ON 1 FROM [Z_TEST/Z_TEST_REQ]; FooGood有某种关联,所以你需要以某种方式提示它。

这是我想出的,虽然我承认它不是很优雅:

FooBad

正如您所看到的,提示是通过创建trait Grouping[B, G] object FooHelper { implicit object fooGrouping Grouping[FooBad.type, FooGood.type] } object BarHelper { implicit object barGrouping Grouping[BarBad.type, BarGood.type] } def go[A <: Failure, B <: Success](x: Int)(implicit ev: Grouping[A, B]): Either[A, B] = ??? import FooHelper._ import BarHelper._ // the following two type check go[FooBad.type, FooGood.type](5) go[BarBad.type, BarGood.type](5) // while these two do not go[FooBad.type, BarGood.type](5) go[BarBad.type, FooGood.type](5) 并将正确的分组放入隐式范围来实现的。这种方法的问题在于用户可能会创建自己的分组,这可能无效。

答案 2 :(得分:0)

通过引入类型参数,可以将输出类型限制为Success / Failure对:

sealed trait Success[T]
sealed trait Failure[T]

sealed trait Foo
sealed trait Bar

case object FooGood extends Success[Foo]
case object BarGood extends Success[Bar]

case object FooBad extends Failure[Foo]
case object BarBad extends Failure[Bar]

def go[T, A <: Failure[T], B <: Success[T]](x: Int): Either[A, B] = ???

但是!只有当你创建一个提供类型T的具体实现时才有意义,因为其他方式创建这样的签名毫无意义。

我可以看到任何实用程序作为接口的一部分,它约束go的结果,如下所示:

trait Paired[T] {                  
  type A <: Failure[T]            
  type B <: Success[T]            
  def go(x: Int): Either[A, B]    
}

如果你想明确地提供类型(但你将如何为此创建实现?) - 提供隐式分组的答案是非常合理的。