bob*_*bob 3 haskell lenses haskell-lens
给定一般Getter类型
type Getter s a = forall f. (Contravariant f, Functor f) => (a -> f a) -> s -> f s
Run Code Online (Sandbox Code Playgroud)
我将如何为一对元组实现一个吸气剂?我想上面的类型代表了部分应用的功能,而缺少的部分正是让我感到困惑的地方。
除此之外,我不了解逆约束。可能是为了使镜头类型更像,但功能还不够吗?
在type中Getter s a,type s表示以某种方式“包含” type值的“对象”,agetter可以以某种方式“提取”该值。
如果要为一对实现一个吸气剂,则您s = (x, y)和的a取值为x或y,具体取决于要提取的元素。为了清楚起见,我们要提取第一个元素。然后a = x。
好的,因此您的函数应如下所示:
firstElementGetter :: Getter (x, y) x
Run Code Online (Sandbox Code Playgroud)
现在,如果扩展的定义Getter,我们将得到:
firstElementGetter :: (blah-blah) => (x -> f x) -> (x, y) -> f (x, y)
firstElementGetter h (x, y) = ...
Run Code Online (Sandbox Code Playgroud)
这意味着您的函数有两个参数:(1)h可以“包装” x函子的函数f,和(2)元组(x, y);它需要返回(x, y)包装在函子中的元组f。让我们看看是否可以做到这一点。
首先,我们有一个h采用type参数的函数x。方便地,我们也有x这种类型的。让我们来应用它:h x。结果为type f x。我们怎样才能把它变成f (x, y)?
好吧,a的本质Functor是您可以映射它。那么我们可以映射f x得到什么功能f (x, y)呢?这样的功能显然需要一个类型x -> (x, y)-瞧!我们拥有构造此功能的所有部分!我们可以利用现有的y并将其重组为元组:\xx -> (xx, y)。
现在,我们拥有履行吸气剂合同的一切:
firstElementGetter :: (blah-blah) => (x -> f x) -> (x, y) -> f (x, y)
firstElementGetter h (x, y) = fmap (\xx -> (xx, y)) (h x)
Run Code Online (Sandbox Code Playgroud)
从根本上讲,这就是所有光学器件的工作方式-它们是吸气剂,导线,棱镜还是其他。然后,消费者可以通过选择合适的函子f和合适的包装功能,使他们做不同的事情h。
例如,消费者可以通过选择此函子,使用您的吸气剂从元组中“提取”第一个元素:
data Const a b = Const a
instance Functor (Const a) where
fmap f (Const a) = Const a
Run Code Online (Sandbox Code Playgroud)
注意它是如何完全忽略类型的b。实际上并没有“包装”它的值,fmap实现也没有涉及它。您可能会说这是“伪”函子。我们将利用它来发挥我们的优势!
对于功能,h我们将选择Const。它适合该类型,因为Const :: x -> Const x foo对于任何foo碰巧与兼容x -> Const x x的类型,它与x -> f x时所需的类型匹配f = Const x。我知道,这有点令人难以置信,但请多多包涵。
现在,如果if h = Const,我们的getter将尽职地调用h x,它将返回Const x,getter将fmap结束,但是由于我们的定义fmap忽略其第一个参数,因此get 的结果fmap将仍然是相同的Const x,然后getter将返回。现在,我们要做的就是拆开包装,我们完成了!
getFirst :: (x, y) -> x
getFirst pair =
let (Const x) = firstElementGetter Const pair
in x
Run Code Online (Sandbox Code Playgroud)
该Contravariant部分有点聪明的类型级黑客。看到的,当一个函子f不仅Functor,而且Contravariant,它具有以具有的形状Const上述类型-即,它不能“包装”其类型参数内的值。
Functor可以认为是“产生”(或“包含”)值Contravariant的事物,而可以认为是“消耗”值的事物。如果必须同时使用“包装器”类型,则实现它的唯一方法是仅假装“使用”或“产生”值,而在后台忽略它们。我知道这不是一个很明确的解释,但我无法做得更好。您会发现,只需尝试实现类似的类型。
因此Getter,给了这个怪异的约束,就像确保它唯一可以做的就是“获取”值,而不是“设置”或“转换”该值一样。
s -> a。(a -> x) -> s -> x-连续传递样式,但等效于前一个。(a -> Const x a) -> s -> Const x s-我只是将两者都a替换Const x foo为different foo,但它仍然与上一个相同。尽管第一个(最简单的)定义可以实际获取,但最后一个定义的优点是具有与其他镜片兼容的特征,因此可以在镜片组合物中使用这种吸气剂。