4

我有一个新类型来代表 Hughes 的列表(即列表构造):

newtype Hughes a = Hughes {unHughes :: [a] -> [a]}

有一些功能可以处理它:

mkHughes :: [a] -> Hughes a
mkHughes = Hughes . (++)

runHughes :: Hughes a -> [a]
runHughes h = unHughes h []

Monoid实例与上述功能一样简单:

instance Monoid (Hughes a) where
    mempty = Hughes id
    mappend (Hughes f) (Hughes g) = Hughes (f . g)

...但是当我到达FunctorandApplicative实例时,麻烦就出现了。到目前为止,这是我想出的:

instance Functor Hughes where
    fmap f (Hughes h) = Hughes $ unsafeCoerce $ fmap f . h

instance Applicative Hughes where
    pure = Hughes . (:)
    (<*>) (Hughes f) (Hughes v) = Hughes $ unsafeCoerce $
                                  (<*>) <$> f <*> unsafeCoerce v

我的问题是我不喜欢使用unsafeCoerce. 有没有办法在Functor不使用实例的情况下设计实例,还是不可避免?

此外,我将如何实现Monad此数据类型的实例?

编辑:与DList包中不同,我想保持性能改进,即不评估[a] -> [a]但映射它。

4

1 回答 1

7

没有办法实现Functor Hughes实例。

让我们检查一下 的定义Hughes

data Hughes a = Hughes ([a] -> [a])
                         ^

我们可以看到参数a在标记的位置出现逆变。只有当最后一个Functor类型参数的所有外观都是协变时,实例才能存在。

基本上你被要求定义一个函数fmap :: (a -> b) -> ([a] -> [a]) -> [b] -> [b]。问题是你不能用 做任何事情[b],没有接受[b]或的函数b。你可以忽略它,但函子定律fmap id = id将不成立。

至于 DList,Functor实例是有效的,因为实现不导出实际的数据构造函数,只导出智能构造函数。因此,您不能构造任意列表处理函数的 dlist 值。使用提供的智能构造函数,您只能构造与 同构的 dlist [a],这就是函子定律成立的原因。

如果您以某种方式将构造函数破解(带入)到作用域中,您会发现函子定律实际上并不适用于任意函数,例如fmap id x /= xifx是类似DList (map negate).

于 2015-07-12T03:55:42.557 回答