简洁版本。Scala 中的大多数泛型集合都有一个map
实际上,该方法将返回相同类型的集合。 (List[A].map(f:A=>B)
返回一个List[B]
例如。)Scala 集合库就是为了实现这一目标而明确设计的。如果我想编写对任何此类集合具有多态性的代码该怎么办?能 ”Traversable
谁的映射的行为类似于函子的“是否”被表示为类型?
长版。我遇到的情况是,用一个抽象来表示某些对象的集合会很有用。C当前类型,这样如果这些对象被转换为某种D所需的类型,那么集合可以使用这些对象来构造某些类型的对象R结果类型。通过使用函数类型,我几乎可以实现我想要的一切
(C => D) => R
但这种方法的一个缺陷是自然的过度懒惰(在我的应用程序中)map
方法,类似于
def map[C2](f: C=>C2): (C2=>D)=>R = (g => this(f andThen g))
这会延迟应用f
到类型的对象C
直到R
正在计算中。我宁愿申请f
立即地。
因此,例如,我可能会实现类似的东西
class Foo[+C,-D,+R](cs: List[C], finalize: List[D]=>R) {
def apply(f: C=>D): R = finalize(cs.map(f))
def map[C2](f: C=>C2): Foo[C2,D,R] = Foo(cs.map(f), finalize)
}
到目前为止,一切都很好。但现在我想,没什么特别的List
这里;任何实现某种类型的构造函数map
函数就可以了。唯一的一点就是这个功能finalize
可能依赖于集合的结构。例如,也许列表的第一个元素被特殊对待,所以如果List.map
返回一些更通用的集合类型,可能是一个非常抽象的集合,甚至没有“第一个元素”的概念,然后finalize
可能会失败。同样,如果它期望列表具有一定的长度,但我过滤列表或其他内容。
如果我以其自然的通用性编写代码,就不会出现这种问题,例如
class Foo[+C,-D,+R,F[x] <: Traversable[x]](cs: F[C], finalize: F[D]=>R) {
...
}
因为这样我就不会不小心做任何奇怪的事情了F
(除非我在运行时检查它的类型或其他什么,在这种情况下我应得的)。
唯一剩下的问题是cs.map(f)
具有静态类型Traversable[D]
, not F[D]
,尽管我们当然希望它实际上是类型F[D]
通常情况下,Scala 集合库的明确设计就是为了确保这一点。
所以我的问题是,这个要求可以吗F
用类型来表达?
本质上,我想要 Haskell 代码的 Scala 版本
data Foo f b r a = Foo (f a) (f b -> r)
instance (Functor f) => Functor (Foo f b r) where
g `fmap` (Foo fa fbr) = Foo (g `fmap` fa) fbr
dothething :: (Functor f) => Foo f b r a -> (a -> b) -> r
dothething foo g = fbr fb where Foo fb fbr = g `fmap` foo
具有或多或少相同的保证并且没有懒惰。