Functor in Control.Categorical.Functor具有以下定义:
class (Category r, Category t) => Functor f r t | f r -> t, f t -> r where
fmap :: r a b -> t (f a) (f b)
但是假设我想要一个从普通函数到 Kleisli 的仿函数箭头(这可能是一个愚蠢的例子)。
我想要这样的类型:
fmap :: (Monad m) => (a -> b) -> Kleisli m a b
那么我可以让r = (->)
, t = Kleisli m
得到:
fmap :: (Monad m) => (a -> b) -> Kleisli m (f a) (f b)
但是,f
是什么?!我真的只是想让它消失。我可以使用 Identity通过让 f = Identity
仿函数,然后我得到:
fmap :: (Monad m) => (a -> b) -> Kleisli m (Identity a) (Identity b)
这需要一些困惑的展开。
然后我想到这样定义 Functor:
class (Category r, Category t) => Functor r t where
type family F r t x :: *
fmap :: r a b -> t (F r t a) (F r t b)
这允许我为 Kleisli 定义一个 Functor 实例,如下所示(没有丑陋的身份包装器):
instance (Monad m) => Functor (->) (Kleisli m) where
type F (->) (Kleisli m) a = a
fmap f = Kleisli (return . f)
在此之后,我很确定我处于:
fmap :: (Monad m) => (a -> b) -> Kleisli m a b
这很好。
现在,我可以立即识别一个问题,即对于 Functor
的给定 r
和 t
参数,原始类定义允许 f
有多个选项,而根据我的定义,r
和 t
确定 f
。这是一个严重的问题,就好像我定义说:
fmap :: (a -> b) -> (Maybe a -> Maybe b)
我无法定义:
fmap :: (a -> b) -> ([a] -> [b])
在这两种情况下,r = (->)
和 t = (->)
。所以目前我的 Functor
甚至没有取代原始的 Prelude 版本。
所以我现在有一些问题:
- 我可以调整我的定义,使
r
和t
无法确定f
(与原始版本一样)吗?或者这需要 Injective Type Families (如果是这种情况,我很乐意编译 head 来尝试这个)。 - 我可以进一步调整我的定义,以便
f
和r
确定t
以及f
和t
确定r
? - 执行上述操作后(如果不可能,则不执行)对类型推断有哪些潜在影响?
- 与原始类定义相比,除了增加打字量之外,还有其他不好的地方吗?
- 是否有其他方法仍然允许我定义 Kleisli 仿函数而不用 Identity 包装,同时比我提出的方法“更好”(更有用的结构、更好的类型推断等)。
很抱歉,最后几个问题有点模糊,我知道类型推断与通用性通常是一种权衡,但我只是在这种特殊情况下寻找一些关于它的想法。
(这个问题部分源于 this question 的答案)
最佳答案
你能得到的最接近的是
class (Category r, Category t) => Functor
(f :: *) (r :: *->*->*) (t :: *->*->*) where
type F f x :: *
fmap :: Tagged f ( r a b -> t (F f a) (F f b) )
instance Functor [()] (->) (->) where
type F [()] x = [x]
fmap = Tagged map
instance (Monad m) => Functor (Kleisli m () ()) (->) (Kleisli m) where
type F (Kleisli m () ()) x = x
fmap = Tagged $ \f -> Kleisli $ return . f
关于haskell - 仿函数泛化问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32695316/