Haskell 镜头 - 棱镜组成

Haskell Lens - Prism Composition

我正在尝试在 Haskell 中组合透镜和棱镜,但没有得到我预期的答案。这是我的测试代码:

{-# LANGUAGE OverloadedStrings #-}
import Data.Function  ( ($), (&) )

import Control.Lens.Getter  ( (^.) )
import Control.Lens.Lens    ( Lens', lens )
import Control.Lens.Prism   ( Prism', prism )
import Control.Lens.Setter  ( (.~) )
import Control.Lens.Review  ( AReview, re )
import Control.Lens.TH      ( makeClassy )

(##) :: AReview t s -> s -> t
x ## y = y ^. re x

data Test = T1 Int | T2 String
  deriving Show

_T1 :: Prism' Test Int
_T1 = prism T1 (\x -> case x of T1 i -> Right i; _ -> Left x)

_T2 :: Prism' Test String
_T2 = prism T2 (\x -> case x of T2 t -> Right t; _ -> Left x)

data Combi = Combi { _t :: Test }
  deriving Show

defCombi :: Combi
defCombi = Combi (T1 7)

t :: Lens' Combi Test
t = lens (\(Combi x) -> x) (\( Combi _ ) t' -> Combi t')

test = (defCombi & t .~ (_T2 ## "foo"), defCombi & (t . _T2) .~ "bar")

现在令我惊讶的是,当我 运行 这个时,第二对显示 Combi {_t = T1 7};也就是说, "assignment" 通过 t 。 _T2没有效果。

查看类型,显然相关的细节是将 t 与 _T2 组合将 "Functor" 要求提升为 "Applicative" 要求。

*Main
λ> :t t
t :: Functor f => (Test -> f Test) -> Combi -> f Combi
*Main
λ> :t t . _T2
t . _T2
  :: Applicative f => (String -> f String) -> Combi -> f Combi

但坦率地说,我无法理解这意味着什么,尤其是为什么这意味着组合不 "work"(或者更可能的是,它确实有效,但我我误解了它在这种情况下的含义)。

不胜感激。

当您使用 Prism 或任何 Traversal 作为 setter 时,只有当参数已经在正确的变体上时它才有效。

Prelude> :module + Control.Lens

Prelude Control.Lens> (Left 4) & (_Left .~ "foo")
Left "foo"

Prelude Control.Lens> (Left 4) & (_Right .~ "foo")
Left 4

因此,当您将 _T2 .~ "bar" 应用于 T1 7 时,您会得到 T1 7 而不是 T2 "bar"