jfl*_*fla 2 haskell haskell-lens
这是我一直在处理的一些代码的版本,删除了细节.希望很清楚我正在尝试做什么,但如果没有,我可以澄清.我很擅长使用镜头,所涉及的复杂类型往往使它们看起来比它们的价值更麻烦.
-- some data type
type AType = ...
data Thing = Th { _v, _w, _x :: AType, _otherStuff :: ... }
makeLenses ''Thing
-- some operation that can be performed on corresponding x or w or v values in two Things.
f :: AType -> AType -> AType
f = ...
-- apply f to the corresponding v, and w, and x values in a and b; then store each result in the respective field of a, leaving other fields untouched.
transform :: Thing -> Thing -> Thing
transform a b =
let transformVorWorX :: Lens' Thing AType -> AType
transformVorWorX lens =
let vwx = view lens a
vwx' = view lens b
in f vwx vwx'
in foldl (\a' lens -> set lens (transformVorWorX lens) a') a [v,w,x]
Run Code Online (Sandbox Code Playgroud)
当我编译时,ghc吐出来
Could not deduce (f ~ Identity)
from the context (Functor f)
bound by a type expected by the context:
Functor f => (AType -> f AType) -> Thing -> f Thing
at ...
‘f’ is a rigid type variable bound by
a type expected by the context:
Functor f => (AType -> f AType) -> Thing -> f Thing
at ...
Expected type: (AType -> f AType) -> Thing -> f Thing
Actual type: ASetter Thing Thing AType AType
In the first argument of ‘transformVorWorX’, namely ‘lens’
In the second argument of ‘set’, namely ‘(transformVorWorX lens)’
Run Code Online (Sandbox Code Playgroud)
为什么这段代码不起作用?我发现,替换lens用cloneLens lens让它编译,但我不知道为什么,这种感觉非常难看-有一个更优雅的方式做我想做的,这也许是更普遍?
非常感谢
Lens' 普遍量化的是在引擎盖下
type Lens' s a = ? f . Functor f => (a -> f a) -> s -> f s
Run Code Online (Sandbox Code Playgroud)
所以类型[v,w,x]必须是
[? f . Functor f => (AType -> f AType) -> Thing -> f Thing]
Run Code Online (Sandbox Code Playgroud)
- 列表中有一个量子.这种类型称为impredicative类型,Haskell不支持它们.(具有该名称的GHC扩展已存在一段时间,但它不起作用.)
有一种相对简单的方法可以解决这个问题:将量子隐藏在a中newtype.在镜头库中,这是ReifiedLens.有了它,你可以写
transform a b =
let transformVorWorX :: ReifiedLens' Thing AType -> AType
transformVorWorX (Lens lens) =
let vwx = view lens a
vwx' = view lens b
in f vwx vwx'
in foldl (\a' lens -> set (runLens lens) (transformVorWorX lens) a') a
[Lens v, Lens w, Lens x]
Run Code Online (Sandbox Code Playgroud)
这段代码完全符合您所设想的运行时行为,但它显然非常难看.
你发现自己的替代解决方案也有效地做了同样的事情但是更好.为了看看那里到底发生了什么,我会把cloneLens内部transformVorWorX:
transform a b =
let transformVorWorX :: ALens' Thing AType -> AType
transformVorWorX lens =
let vwx = view (cloneLens lens) a
vwx' = view (cloneLens lens) b
in f vwx vwx'
in foldl (\a' lens -> set (cloneLens lens) (transformVorWorX lens) a') a [v,w,x]
Run Code Online (Sandbox Code Playgroud)
这样做的原因是,像ReifiedLens- 但不像Lens- ALens'没有量子.它只是一个具体的实例Functor f,因此它不需要impredicativity但你可以直接使用v,w并x在[ALens' Thing AType]列表中.仿函数实例经过精心挑选,不会失去任何一般性,因此cloneLens能够为您提供完整的,通用量化的镜头.
而不是克隆它,你也可以使用^#运营商,直接意见ALens,并storing代替set:
transform a b =
let transformVorWorX :: ALens' Thing AType -> AType
transformVorWorX lens =
let vwx = a^#lens
vwx' = b^#lens
in f vwx vwx'
in foldl (\a' lens -> storing lens (transformVorWorX lens) a') a [v,w,x]
Run Code Online (Sandbox Code Playgroud)
如果我们将其浓缩成一个不那么冗长的形式,它看起来相当不错IMO:
transform a b = foldl updater a [v,w,x]
where updater a' lens = a' & lens #~ f (a^#lens) (b^#lens)
Run Code Online (Sandbox Code Playgroud)
我还建议一个完全不同的解决方案:定义一个访问所有感兴趣值的光学元件,即
vwx :: Traversal' Thing AType
vwx f (Thing ? ? ? otherVals)
= (\?' ?' ?' -> Thing ?' ?' ?' otherVals)
<$> f ? <*> f ? <*> f ?
Run Code Online (Sandbox Code Playgroud)
您可以让Template Haskell助手为您自己定义:
makeLensesFor [("_v", "vwx"), ("_w", "vwx"), ("_x", "vwx")] ''Thing
Run Code Online (Sandbox Code Playgroud)
现在,遍历可以用作简单的折叠,从而获得所有值的列表.然后,您可以使用标准zip来执行元素的转换:
transform a b = ...
where tfmedVals = zipWith f (a^..vwx) (b^..vwx)
Run Code Online (Sandbox Code Playgroud)
要将修改后的值列表放回容器中,该库还具有以下功能:partsOf.有了它,你的整个转型归结为
transform a b = a & partsOf vwx .~ zipWith f (a^..vwx) (b^..vwx)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
73 次 |
| 最近记录: |