Tom*_*ula 4 types scala shapeless
我有这个代码,我想改进:
sealed abstract class A
case class B() extends A
case class C() extends A
case class D() extends A
case class Foo[+T <: A](a: T)
/** Puts instances that match Foo(B()) in the first list and everything else,
* i.e. Foo(C()) and Foo(D()), in the second list. */
def partition(foos: List[Foo[_ <: A]]): (List[Foo[B]], List[Foo[_ <: A]]) = {
// ...
}
Run Code Online (Sandbox Code Playgroud)
我想在以下方面对此进行改进:
partition返回类型,以便它表明Foo[B]第二个列表中没有吗?Foo类型参数T(即更改Foo为case class Foo(a: A))并仍然partition使用相同的类型保证声明吗?(显然,它必须返回不同的东西(List[Foo], List[Foo]).)PS:让我知道"无形"标签是否与此问题无关.
这个问题有点棘手,因为Scala混合了代数数据类型(如你的A)和子类型.在与ADT的大多数语言,B,C,并D不会在类型都-航空公司仅仅是"建设者"(在一定意义上,它类似于但不一样的OOP构造函数).
Foo[B]在这些语言(如Haskell或OCaml)中讨论a 是没有意义的,但在Scala中你可以,因为Scala将ADT实现为扩展基本特征或类的case类(和类对象).但这并不意味着你应该四处谈论Foo[B],一般而言,如果你想用FP术语思考并使用类型系统来获得优势,那么最好不要这样做.
回答您的具体问题:
Either[Foo[C], Foo[D]]元素的列表)或类似Shapeless Coproduct(带有Foo[C] :+: Foo[D] :+: CNil元素的列表)来表示"类型的事物列表A,但不是B",但这两种方法都是相当重的机器,可能不是首先是最好的主意.Foo类型进行参数化A,但是如果你想在类型级别表示" Foo包含a B",那么你将需要保持当前的方法.为了解决您的附言:如果你想概括了ADT的,无形的,绝对是适用的,看我的博客文章在这里有关分区的构造,例如.但是,如果你只是这样做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)
}
Run Code Online (Sandbox Code Playgroud)
这不是理想的 - 如果我们真的想要一个List[Foo[B]],那么有一个List[Foo[~B]]代表残羹剩饭很好- 但它并不太糟糕.
| 归档时间: |
|
| 查看次数: |
402 次 |
| 最近记录: |