组合镜头和棱镜用于产品的总和? [英] Combo lenses and prisms for sums of products?
问题描述
makeFields
不会将镜头放入字段中(当然),但只能对它们进行遍历。 declarePrisms
似乎更有前景。根据文件, declarePrisms [d |
数据Exp = Lit Int | Var字符串| Lambda {bound :: String,body :: Exp}
|]
将创建
数据Exp = Lit Int | Var字符串| Lambda {bound :: String,body :: Exp}
_Lit :: Prism'Exp Int
_Var :: Prism'Exp String
_Lambda :: Prism'Exp(String,Exp)
这让我几乎在那里,但我真正想要的更像是这样:
data Exp = Lit Int | Var字符串| Lambda字符串Exp
数据LambdaRec = {_bound :: String,_body :: Exp}
...
_Lambda :: Prism'Exp LambdaRec
- LambdaRec,
- 理想情况下也可以遍历在Exp中查看它们。
class MightBeLambda t其中
类型BoundOptic t
类型BodyOptic t
bound :: BoundOptic t
body :: BodyOptic t
实例MightBeLambda Exp其中
type BoundOptic Exp = Traversal'Exp String
...
instance MightBeLambda LambdaRec其中
类型BoundOptic LambdaRec = Lens'LambdaRec字符串
是否有任何方法可以自动执行此类操作,还是需要手动执行?
< hr>
有人可能希望这样做:
data ExpTag = LitT | VarT | LambdaT
数据Exp':: ExpTag - > *其中
Lit':: Int - > Exp'LitT
Var':: String - > Exp'VarT
Lambda':: {_bound :: String,_body :: Exp} - > Exp'LambdaT
然后棱镜可以被定义为evilly,使用 unsafeCoerce
,以避免任何复制记录的风险。 行为。 (makePrisms在应用于具有单个构造函数的类型时生成Isos)
{ - #LANGUAGE TemplateHaskell# - }
模块演示其中
导入Control.Lens
数据Exp = Lit Int | Var字符串| Lambda字符串Exp
数据LambdaRec = LambdaRec {_bound :: String,_body :: Exp}
makePrisms''Exp
makePrisms''LambdaRec
makeLenses''LambdaRec
_ExpLambdaRec :: Prism'Exp LambdaRec
_ExpLambdaRec = _Lambda。 from _LambdaRec
- 使用_ExpLambdaRec的示例
expBound :: Traversal'Exp字符串
expBound = _ExpLambdaRec。绑定
请注意,由于GHC可以做到的优化,因此不需要使用此中间记录类型生成的代码。
getBound :: Exp - >可能String
getBound =预览expBound
- 为getBound生成的核心
-
- getBound1 =
- \ eta_B1 - >
- _ {
- __DEFAULT - >的案例eta_B1 (Nothing)`cast` ...;
- Lambda y1_a6XB y2_a6XC - > (Just y1_a6XB)`cast` ...
- }
If I have a record type, I can do pretty much anything I want to it with lenses. If I have a sum type, I can do pretty much whatever I want to it with prisms. But if I have a sum that includes a record, makeFields
doesn't give me lenses into the fields (of course), but only traversals for them. declarePrisms
seems a bit more promising. According to the documentation,
declarePrisms [d|
data Exp = Lit Int | Var String | Lambda{ bound::String, body::Exp }
|]
will create
data Exp = Lit Int | Var String | Lambda { bound::String, body::Exp }
_Lit :: Prism' Exp Int
_Var :: Prism' Exp String
_Lambda :: Prism' Exp (String, Exp)
This gets me almost there, but what I really want is more like this:
data Exp = Lit Int | Var String | Lambda String Exp
data LambdaRec = { _bound::String, _body::Exp }
...
_Lambda :: Prism' Exp LambdaRec
-- bound and body lenses into LambdaRec,
-- and ideally also traversals to look at them in Exp.
class MightBeLambda t where
type BoundOptic t
type BodyOptic t
bound :: BoundOptic t
body :: BodyOptic t
instance MightBeLambda Exp where
type BoundOptic Exp = Traversal' Exp String
...
instance MightBeLambda LambdaRec where
type BoundOptic LambdaRec = Lens' LambdaRec String
Is there any way to do something like this automatically, or would I have to do it by hand?
An even crazier way one might wish to do it:
data ExpTag = LitT | VarT | LambdaT
data Exp' :: ExpTag -> * where
Lit' :: Int -> Exp' LitT
Var' :: String -> Exp' VarT
Lambda' :: { _bound::String, _body::Exp } -> Exp' LambdaT
Then the prisms can be defined evilly, using unsafeCoerce
to avoid any risk of copying records.
You can take a detour through another generated Iso to get this behavior. (makePrisms generates Isos when applied to a type with a single constructor)
{-# LANGUAGE TemplateHaskell #-}
module Demo where
import Control.Lens
data Exp = Lit Int | Var String | Lambda String Exp
data LambdaRec = LambdaRec { _bound::String, _body::Exp }
makePrisms ''Exp
makePrisms ''LambdaRec
makeLenses ''LambdaRec
_ExpLambdaRec :: Prism' Exp LambdaRec
_ExpLambdaRec = _Lambda . from _LambdaRec
-- Example using _ExpLambdaRec
expBound :: Traversal' Exp String
expBound = _ExpLambdaRec . bound
Note that thanks to optimizations that GHC can do that this intermediate record type isn't necessarily used in the generated code.
getBound :: Exp -> Maybe String
getBound = preview expBound
-- Generated core for getBound
--
-- getBound1 =
-- \ eta_B1 ->
-- case eta_B1 of _ {
-- __DEFAULT -> (Nothing) `cast` ...;
-- Lambda y1_a6XB y2_a6XC -> (Just y1_a6XB) `cast` ...
-- }
这篇关于组合镜头和棱镜用于产品的总和?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!