事情来自于我的一个突发奇想,想要实现一个广义的fmap'函数,实现处理嵌套Functor的效果,即实现
fmap' (+1) [1,2] = [2,3]fmap' (+1) [[1,2],[2,3]] = [[2,3],[3,4]]这样的效果。
attempt 1. 首先我们需要确定fmap'的类型。此处我觉得需要一个“嵌套函子”的类型。不过考虑到“两个函子结合之后依然是函子”,调研到Data.Functor.Compose这个库,这个库主要建立在“两个函子结合之后依然是函子”结论的基础上,核心代码如下:
newtype Compose f g a = Compose { getCompose :: f (g a) }
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose x) = Compose (fmap (fmap f) x)
a <$ (Compose x) = Compose (fmap (a <$) x)
这使我意识到并不需要定义新的“嵌套函子”类型类。
attempt 2. 直接定义一个type Compose f g a = f (g a),然后按照上面的方式定义类型类实例即可。借助TypeOperators可以写出下面的代码:
{-# LANGUAGE TypeOperators #-}
type (f :+: g) a = f (g a)
但是因为type不允许部分应用,所以是不能写出一个Compose f g(在这里是f :+: g)作为instance的,即使新定义一个类型类也不行。
这里主要的问题是,:+:的kind是(:+:) :: (* -> *) -> (* -> *) -> * -> *,是需要接收3个参数的operator。如果我们有能力对kind也进行curry化就好了。
update1. 在这个过程中留意到Data.Functor.Bind库,里面定义了没有pure的Applicative(Apply)和没有return的Monad(Bind),后者相当于只实现了join,很有意思的一个Functor,和只实现了 point的Pointed类型类(在Data.Pointed里面)对映成趣。张凇的书里提到了这件事,不过我更推荐阅读这里。
update2. Functor combination的思路也很吸引我!感觉函数式编程的内容实在太广泛了,却看不到一条路可以通向这些内容