制作数据类型* - > *那不是玩家

时间:2014-11-18 02:27:30

标签: haskell functor

Brent Yorgey的Typeclassopedia给出了以下练习:

  

举一个类型* -> *的例子,它不能成为一种   Functor的实例(不使用undefined)。

请告诉我“无法成为Functor ”的实例。

另外,我很欣赏一个例子,但也许作为一个剧透,以便你可以指导我找到答案。

2 个答案:

答案 0 :(得分:18)

让我们谈谈差异。

这是基本概念。考虑类型A -> B。我想让你想象的是,这种类型类似于"拥有B"还有一个A"事实上,如果您还清了A,则会立即收到B。功能就像托管一样。

"有"的概念和"欠"可以扩展到其他类型。例如,最简单的容器

newtype Box a = Box a

表现得像这样:如果你"有"一个Box a那么你也有"有" a。我们认为类型* -> *和"有"他们的论点是(协变)仿函数,我们可以将它们实例化为Functor

instance Functor Box where fmap f (Box a) = Box (f a)

如果我们考虑一个类型的谓词类型会发生什么,比如

newtype Pred a = Pred (a -> Bool)

在这种情况下,如果我们"有"一个Pred a,我们实际上"欠" a。这是因a位于(->)箭头左侧。通过将函数传递到容器并将其应用于我们所拥有的所有位置来定义fmap Functor的位置。我们的内在类型,我们不能对Pred a做同样的事情,因为我们没有"有"和a s。

相反,我们会这样做

class Contravariant f where
  contramap :: (a -> b) -> (f b -> f a)

现在contramap就像一个&#34;翻转&#34; fmap?它将允许我们将该功能应用于我们拥有的地方&#34; b中的Pred b,以便获得Pred a。我们可以致电contramap&#34;以物易物&#34;因为它编码的想法是,如果你知道如何从b获得a s,那么你可以将b的债务变成a的债务。< / p>

让我们看看它是如何运作的

instance Contravariant Pred where
  contramap f (Pred p) = Pred (\a -> p (f a))

我们只是在将f传递给谓词函数之前使用Functor进行交易。精彩!

所以现在我们有协变和逆变类型。从技术上讲,这些被称为协变和逆变&#34;仿函数&#34;。我还会立刻说明,几乎总是一个逆变函子也不是协变的。因此,这回答了你的问题:存在一堆逆变函子,它们无法实例化为Preddata Z a = Z -- phantom a! instance Functor Z where fmap _ Z = Z instance Contravariant Z where contramap _ Z = Z 就是其中之一。

但是有一些棘手的类型,它们都是逆变和协变的函子。特别是,恒定的仿函数:

Contravariant

事实上,您基本上可以证明FunctorisPhantom :: (Functor f, Contravariant f) => f a -> f b -- coerce?! isPhantom = contramap (const ()) . fmap (const ()) -- not really... 都有幻像参数。

-- from Data.Monoid
newtype Endo a = Endo (a -> a)

另一方面,像

这样的类型会发生什么
Endo a

a我们都欠{并且Endo。这是否意味着我们没有债务?嗯,不,它只是意味着Endo想要协变和逆变并且没有幻像参数。结果:Functor 不变并且既不能Contravariant也不会{{1}}实例化。

答案 1 :(得分:11)

t类型* -> *可以成为Functor的实例,当且仅当有可能实现Functor类的守法实例时为了它。这意味着您必须实施Functor课程,并且fmap必须遵守Functor法律:

fmap id x == x
fmap f (fmap g x) == fmap (f . g) x

所以基本上,要解决这个问题,你必须为自己选择的某种类型命名并证明fmap没有合法的实现。

让我们从 - 示例开始,设置音调。 (->) :: * -> * -> *是函数类型构造函数,如String -> Int :: *等函数类型中所示。在Haskell中,您可以部分应用类型构造函数,因此您可以使用类型(->) r :: * -> *。此类型为Functor

instance Functor ((->) r) where
    fmap f g = f . g

直观地说,此处的Functor实例允许您将f :: a -> b应用于函数g :: r -> a的返回值“之前”(可以这么说)您应用g一些x :: r。例如,如果这是返回其参数长度的函数:

length :: [a] -> Int

...然后这是返回其参数长度两倍的函数:

twiceTheLength :: [a] -> Int
twiceTheLength = fmap (*2) length

有用的事实:Reader monad只是newtype的{​​{1}}:

(->)

现在我们已经完成了非示例,这里的类型不能成为newtype Reader r a = Reader { runReader :: r -> a } instance Functor (Reader r) where fmap f (Reader g) = Reader (f . g) instance Applicative (Reader r) where pure a = Reader (const a) Reader f <*> Reader a = Reader $ \r -> f r (a r) instance Monad (Reader r) where return = pure Reader f >>= g = Reader $ \r -> runReader g (f r) r

Functor

是的,我所做的就是向后拼写名称,更重要的是,翻转类型参数的顺序。我会让你试着弄清楚为什么这种类型不能成为type Redaer a r = Redaer { runRedaer :: r -> a } -- Not gonna work! instance Functor (Redaer a) where fmap f (Redaer g) = ... 的实例。