为什么没有prism set函数返回Option / Maybe

时间:2017-09-23 20:51:46

标签: scala haskell f# functional-programming lenses

在功能光学中,一个表现良好的棱镜(我相信在scala中称为部分透镜)应该具有类型为'subpart -> 'parent -> 'parent set 函数,其中如果是棱镜& #34;成功"并且在结构上与给定的'parent参数兼容,然后它返回给定的'parent,其中相应的子部分被修改为给定'subpart值。如果棱镜"失败"并且在结构上与'parent参数不兼容,然后返回未经修改的'parent
我想知道为什么棱镜没有返回'parent option(Haskellers的Maybe)来表示set函数的通过/失败性质?程序员不应该从返回类型中判断该集合是否成功"或不?

我知道在功能光学领域已经进行了大量的研究和思考,所以我确信必须有一个我似乎无法找到的明确答案。

(我来自F#背景,所以如果我使用的语法对Haskell或Scala程序员来说有点不透明,我会道歉。)

2 个答案:

答案 0 :(得分:12)

我怀疑有一个确定的答案,所以我会在这里给你两个。

来源

我认为,首先想象的是棱镜(Dan Doel,如果我的模糊回忆是正确的)作为“合作镜头”。而sa的镜头提供

get :: s -> a
set :: (s, a) -> s

sa提供的棱镜

coget :: a -> s
coset :: s -> Either s a

所有箭头都颠倒过来,产品(,)被副产品Either取代。因此,类型和功能类别中的棱镜是双重类别中的镜头。

对于简单的棱镜,s -> Either s a似乎有点奇怪。你为什么要回原价?但lens包还提供类型更改光学器件。所以我们最终得到了

get :: s -> a
set :: (s, b) -> t

coget :: a -> s
coset :: t -> Either s b

突然之间,我们在不匹配的情况下回来的可能实际上有点不同!那是什么意思?这是一个例子:

cogetLeft :: a -> Either a x
cogetLeft = Left

cosetLeft :: Either b x -> Either (Either a x) b
cosetLeft (Left b) = Right b
cosetLeft (Right x) = Left (Right x)

在第二个(不匹配)的情况下,我们返回的是相同的,但其类型已被更改。

很好的层次结构

对于Van Laarhoven(如lens)和profunctor风格的框架,镜头和棱镜也可以代替穿越。要做到这一点,他们需要有类似的形式,这种设计可以实现这一点。 leftaroundabout的答案提供了有关这方面的更多细节。

答案 1 :(得分:10)

回答“为什么” - 镜头等非常严格地从类别理论中得出,所以这实际上非常明确 - 你所描述的行为只是从数学中剔除,它不是任何人所定义的出于任何目的,但从更广泛的想法出发。

好的,这并不令人满意。

不确定是否有其他语言'类型系统足以表达这一点,但原则上在Haskell中,棱镜是遍历的特例。 遍历是一种“访问”某些“容器”中所有“元素”出现的方法。经典的例子是

mapM :: Monad m => (a -> m b) -> [a] -> m [b]

这通常用作

Prelude> mapM print [1..4]
1
2
3
4
[(),(),(),()]

这里的重点是:对动作/副作用进行排序,并将结果收集到一个与我们开始时具有相同结构的容器中。

关于棱镜的特殊之处在于,容器被限制为包含一个或零个元素(而一般遍历可以覆盖任意数量的元素)。但set运营商并不了解这一点,因为它严格来说更为一般。好的一点是,你可以在镜头,棱镜或mapM上使用它,并且总能得到合理的行为。但它“完全插入结构一次或者告诉我它是否失败”的行为。

并不是说这不是一个明智的操作,只是它不是镜头库所谓的“设置”。您可以通过明确匹配和重新构建来实现:

set₁ :: Prism s a -> a -> s -> Maybe s
set₁ p x = case matching p x of
    Left _ -> Nothing
    Right a -> Just $ a ^. re p

更确切地说:棱镜分离案例:一个容器可能包含一个元素,除此之外别无其他任何元素,或者它可能没有元素但可能没有相关的东西。< /子>