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"
我正在尝试在 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"