-
K 是一种将 2 个参数转换为结构 K a 的数据类型(仅保留第一个参数)
这不太对。 K a b 是使用两个参数形成的数据类型,但说它“将它们变成”任何东西并不正确。相反,它只是向世界表明现在存在一种新类型:K a b。 “所以呢?”你可能会问。好吧,数据类型的后半部分定义了如何生成这种类型的新值。那部分说:“您可以使用此函数创建K a b 类型的新值,我将调用K,其类型为a -> K a b。” 真的认识到 type K 和 constructor K 之间存在区别。
所以,并不是K“只保留第一个参数”——而是构造函数K(它是一个函数)碰巧不接受b类型的任何参数。
-
Flip 是一种将 3 个参数转换为一个结构的数据类型
就像上面一样,这不太对。 Flip 声明指出可以有 Flip f a b 类型的值,唯一的方法是使用 f b a -> Flip f a b 类型的构造函数 Flip。
如果您想知道我是如何为构造函数 K 和 Flip 提供类型签名的,它实际上并不神秘,您可以通过在其中键入 :t K 或 :t Flip 来仔细检查GHCI。这些类型完全基于数据类型声明的右侧进行分配。另外,请注意类型名称和构造函数不必相同。例如,考虑这种数据类型:
data Foo a = Bar Int a | Foo String | Baz a a
这声明了一个带有三个构造函数的类型Foo a:
Bar :: Int -> a -> Foo a
Foo :: String -> Foo a
Baz :: a -> a -> Foo a
基本上,构造函数名称后面的每个类型都是构造函数的参数。
- 因为在
fmap :: (a-> b) -> f a -> f b中,f a有一种*,要写Flip的Functor实例,我们把它写在Flip的最后一个类型上。又名f 和a 在某种程度上是“常量”,我们为b 类型编写函子。
这基本上是对的!你也可以说f 有一种* -> *。由于Flip 具有类型(* -> *) -> * -> * -> *,因此您需要为其提供两个类型参数(第一个类型* -> * 和第二个类型*)以使其成为正确的类型。前两个参数在实例中成为固定的(某种意义上的“常量”)。
我会这样写:...我知道那是完全错误的,但我不知道为什么。
您的实例完全错误的原因是您将 type 与 constructor 混淆了。将(Flip x y z) 放在您所做的模式位置是没有意义的,因为构造函数 Flip 只接受一个参数——记住,它的类型是Flip :: f b a -> Flip f a b!所以你想写这样的东西:
instance Functor (Flip f a) where
fmap f (Flip fxa) = ...
现在,... 填写什么?你有一个值fxa :: f x a,你有一个函数f :: x -> y,你需要产生一个f y a类型的值。老实说,我不知道该怎么做。毕竟,f x a 的值 是 什么?我们不知道f是什么?!
另外,我们为什么要将K 带入Flip 的Functor 实例?有人可以彻底解释提出这个解决方案的过程以及为什么它是正确的吗?
我们刚刚在上面看到,我们不能为任意f 编写Functor 实例,但我们可以为特定 f 编写它。事实证明,K 就是这样一个特别有效的f。让我们试着让它工作:
instance Functor (Flip K a) where
fmap f (Flip kxa) = ...
当f 是任意的时,我们被困在这里,但现在我们知道kxa :: K x a。请记住,生成K x a 类型值的唯一方法是使用构造函数 K。因此,这个值kxa必须是使用该构造函数生成的,所以我们可以将其拆分为:kxa ⩳ K x' where x' :: x。让我们继续把它放到我们的模式中:
fmap f (Flip (K x')) = ...
现在我们可以取得进展了!我们需要产生一个Flip K a y 类型的值。唔。产生Flip 类型值的唯一方法是使用Flip 构造函数,所以让我们从它开始:
fmap f (Flip (K x')) = Flip ...
Flip K a y 类型的 Flip 构造函数采用 K y a 类型的值。产生其中之一的唯一方法是使用 K 构造函数,所以让我们添加:
fmap f (Flip (K x')) = Flip (K ...)
K y a 类型的K 构造函数接受y 类型的值,因此我们需要在此处提供y 类型的值。我们有一个值x' :: x 和一个函数f :: x -> y。将第一个插入第二个为我们提供了我们需要的值:
fmap f (Flip (K x')) = Flip (K (f x'))
只需将x' 重命名为b,您就拥有了老师提供的代码。