由于 Scala 的混合方式,这个问题有点棘手代数数据类型(像你的A
)和子类型。在大多数带有 ADT 的语言中,B
, C
, and D
根本不是类型——它们只是“构造函数”(某种意义上类似于 OOP 构造函数,但不一样)。
谈论一个没有意义Foo[B]
在这些语言(如 Haskell 或 OCaml)中,但在 Scala 中可以,因为 Scala 将 ADT 实现为扩展基本特征或类的案例类(和类对象)。那并不意味着你should到处谈论Foo[B]
不过,一般来说,如果您想用 FP 术语进行思考并使用类型系统来发挥自己的优势,那么最好不要这样做。
回答您的具体问题:
- 不,实际上不是以任何方便的方式。您可以使用标记的联合(带有
Either[Foo[C], Foo[D]]
元素)或类似 Shapeless 的东西Coproduct
(一个列表Foo[C] :+: Foo[D] :+: CNil
elements) 来表示“一系列事物类型A
, 但不是B
”,但这两种方法都是相当笨重的机械,对于一开始可能不是最好的想法的东西来说。
- 我建议不要参数化
Foo
在子类型上A
,但是如果你想能够代表“aFoo
其中包含一个B
“在类型级别,您将需要保持当前的方法。
解决你的后记:如果你想概括 ADT,Shapeless 绝对适用 - 请参阅我的博客文章here例如,关于按构造函数进行分区。如果你这样做只是为了A
不过,Shapeless 可能不会给你带来太多好处。
作为脚注,如果我确实需要一个分区操作来分割类型的元素Foo[B]
,我可能会这样写:
def partition(foos: List[Foo[A]]): (List[Foo[B]], List[Foo[A]]) =
foos.foldRight((List.empty[Foo[B]], List.empty[Foo[A]])) {
case (Foo(B()), (bs, others)) => (Foo(B()) :: bs, others)
case (other, (bs, others)) => (bs, other :: others)
}
这并不理想——如果我们真的想要一个List[Foo[B]]
,如果有一个就好了List[Foo[~B]]
代表剩菜——但这还不错。