我正在尝试在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}
;也就是说,通过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
但坦率地说,我无法理解这意味着什么,尤其是为什么这意味着该合成不“起作用”(或更可能它确实起作用了,但是我误解了什么)在此情况下表示)。
所有的启蒙运动都感激不尽。
答案 0 :(得分:1)
使用Prism
或实际上的任何Traversal
作为设置器时,仅当参数已经在正确的变体上时,它才有效。
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"