组合镜头和棱镜用于产品的总和? [英] Combo lenses and prisms for sums of products?

查看:101
本文介绍了组合镜头和棱镜用于产品的总和?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有记录类型,我可以用镜头做任何我想做的事情。如果我有一个总和类型,我可以做任何我想要的棱镜。但是,如果我有一笔包含记录的金额, 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆