Haskell 将数据类型设置为*->;*那';他不是函子

Haskell 将数据类型设置为*->;*那';他不是函子,haskell,functor,Haskell,Functor,Brent Yorgey's提供了以下练习: 请举例说明一种类型的*->*,它不能作为 函子的实例(不使用未定义的) 请告诉我“不能成为Functor的实例”是什么意思 另外,我想举一个例子,但也许作为一个扰流者,你可以,请,引导我找到答案。一个类型t的*->*可以成为Functor的实例,如果并且只有当它可以实现一个遵守法律的Functor类的实例时。这意味着您必须实现函子类,并且您的fmap必须遵守函子规则: fmap id x == x fmap f (fmap g x) == fmap

Brent Yorgey's提供了以下练习:

请举例说明一种类型的
*->*
,它不能作为
函子的实例(不使用
未定义的

请告诉我“不能成为
Functor
的实例”是什么意思


另外,我想举一个例子,但也许作为一个扰流者,你可以,请,引导我找到答案。

一个类型
t
*->*
可以成为
Functor
的实例,如果并且只有当它可以实现一个遵守法律的
Functor
类的实例时。这意味着您必须实现
函子
类,并且您的
fmap
必须遵守
函子
规则:

fmap id x == x
fmap f (fmap g x) == fmap (f . g) x
所以基本上,要解决这个问题,你必须说出你选择的某种类型,并证明没有合法的
fmap
实现

让我们从一个非示例开始,来确定基调
(>)::*->*->*->*
是函数类型构造函数,如
String->Int::*
等函数类型所示。在Haskell中,您可以部分应用类型构造函数,因此可以使用类似于
(>)r::*->*
的类型。此类型是一个
函子

instance Functor ((->) r) where
    fmap f g = f . g
type Redaer a r = Redaer { runRedaer :: r -> a } 

-- Not gonna work!
instance Functor (Redaer a) where
    fmap f (Redaer g) = ...
直观地说,这里的
Functor
实例允许您将
f::a->b
应用于函数的返回值
g::r->a
“在(可以说)您将
g
应用于某些
x::r
之前”。例如,如果这是返回其参数长度的函数:

length :: [a] -> Int
twiceTheLength :: [a] -> Int
twiceTheLength = fmap (*2) length
…那么这就是返回两倍于其参数长度的函数:

length :: [a] -> Int
twiceTheLength :: [a] -> Int
twiceTheLength = fmap (*2) length
有用的事实:
阅读器
monad只是
(>)
的一个
新类型


是的,我所做的只是将名称向后拼写,更重要的是,翻转类型参数的顺序。我将让您尝试并找出为什么不能将此类型作为
Functor的实例

这是基本概念。考虑类型<代码> a> b>代码>。我想让你们想象的是,这种类型类似于“拥有一个
B
”和“拥有一个
a
”。事实上,如果您偿还了
A
,您将立即收到
B
。函数在某种程度上类似于托管

“拥有”和“欠”的概念可以延伸到其他类型。例如,最简单的容器

newtype Box a = Box a
行为如下:如果您“拥有”一个
框a
,那么您也“拥有”一个
a
。我们考虑类型有“<代码> *> *> /代码>和“拥有”它们的参数为(协变)函子,我们可以将它们实例化为<代码>函子< /COD>

instance Functor Box where fmap f (Box a) = Box (f a)
如果我们考虑谓词类型的类型,比如

会发生什么?
newtype Pred a = Pred (a -> Bool)
在这种情况下,如果我们“拥有”a
Pred a
,我们实际上“欠”a
a
。这是由于
(>)
箭头的左侧有
a
。当
Functor
fmap
通过将函数传递到容器中并将其应用到我们“拥有”内部类型的所有位置来定义时,我们不能对
Pred a
执行相同的操作,因为我们没有“拥有”和
a
s

相反,我们将这样做

class Contravariant f where
  contramap :: (a -> b) -> (f b -> f a)
现在,
contracmap
就像一个“翻转的”
fmap
?它将允许我们将该功能应用于我们在
Pred b
中“拥有”a
b
的位置,以便接收a
Pred a
。我们可以将
contracmap
称为“物物交换”,因为它编码了这样一种思想:如果你知道如何从
a
s获得
b
s,那么你可以将
b
s的债务变成
a
s的债务

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

instance Contravariant Pred where
  contramap f (Pred p) = Pred (\a -> p (f a))
在将其传递到谓词函数之前,我们只需使用
f
运行交易。太好了

现在我们有协变型和逆变型。从技术上讲,它们被称为协变和逆变“函子”。我还将立即声明,几乎所有的逆变函子都不是协变的。因此,这就回答了您的问题:存在一堆不能实例化为
函子的逆变函子
Pred
就是其中之一

不过,也有一些棘手的类型,既有逆变函子,也有协变函子。特别是常数函子:

data Z a = Z -- phantom a!

instance Functor       Z where fmap      _ Z = Z
instance Contravariant Z where contramap _ Z = Z
事实上,你可以从本质上证明,任何同时是
反变的
函子
的东西都有幻像参数

isPhantom :: (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
是不变的,既不能实例化
函子
也不能实例化
逆变

A函子
f
具有
fmap::(A->b)->fa->fb
,满足
fmap x。fmap y=fmap(x.y)
。提出一种类型,其中(1)您不能定义
fmap
,或者(2)您可以定义
fmap
,但不能使其遵循规则。提示:一种常见的函子类型是容器,因此如果您发明了一种新的容器,它可能就是函子。尝试创建一个类型
xa
,这样
xa
就不包含
a
,但可能会对
a
做一些其他的事情。我将通过提醒您Haskell是一种函数语言来补充@DietrichEpp的提示。你有什么想法吗?哈斯克尔IRC我得到了一个提示,即回答-
data Foo a=Foo(a->a)
fmap的类型签名是什么-
Foo(a->a)
看起来像
fb
。你能用
a->b
fooa
变成
foob
吗?等价地