我正在努力将lens
库用于解决特定问题。我正试图通过
到另一个函数g
。我传递了镜头和数据结构,因为g
需要来自数据结构的一些共享信息以及一条信息。 (如果有帮助,数据结构包含关于联合概率分布的信息,但g
仅适用于边际,并且需要知道我正在看哪个边际。两个边缘之间的唯一区别是它们的平均值其余的定义在数据结构中共享。)
我的第一次尝试看起来像这样
f :: Functor f => Params -> ((Double -> f Double) -> Params -> f Params) -> a
f p l = g (l %~ upd $ p) l
where upd = ...
g p x = go p p^.x
但在编译期间失败,因为f
被推断为更新的Identity
和getter的Const Double
。
完成我想做的最好的方法是什么?我可以想象能够做到以下其中一种:
感谢您的帮助!
答案 0 :(得分:9)
András Kovács answer显示了如何使用RankNTypes
实现此目标。如果您希望避免RankNTypes
,那么您可以使用ALens
和cloneLens
:
f :: a -> ALens' a Int -> (Int, a)
f a l = (newvalue, a & cloneLens l .~ newvalue)
where oldvalue = a^.cloneLens l
newvalue = if oldvalue == 0 then 0 else oldvalue - 1
Control.Lens.Loupe提供适用于ALens
而不是Lens
的运算符和函数。
请注意,在许多情况下,您还应该能够使用<<%~
,类似于%~
,但也会返回旧值,或<%~
,它会返回新值:
f :: a -> LensLike' ((,) Int) a Int -> (Int, a)
f a l = a & l <%~ g
where g oldvalue = if oldvalue == 0 then 0 else oldvalue - 1
这样做的好处是它既可以使用Isos
,也可以使用Traversals
(当目标类型为Monoid
时)。
答案 1 :(得分:7)
您希望您的类型签名如下所示:
f :: Params -> Lens Params Params Double Double -> ...
-- alternatively, instead of the long Lens form you can write
-- Lens' Params Double
这与您在签名中写出的内容不同,因为functor参数在Lens
内量化:
type Lens s t a b = forall f. Functor f => (a -> f b) -> (s -> f t)
正确的签名转换为:
f :: Params -> (forall f. Functor f => (Double -> f Double) -> Params -> f Params) -> ...
这可以防止编译器统一不同镜头使用的不同f
参数,即。即你可以多态地使用镜头。请注意,您需要RankNTypes或Rank2Types GHC扩展名才能写出签名。
答案 2 :(得分:2)
本诺给出了最好的通用答案。
然而,还有另外两个选项,我在这里提供完整性。
1)
Loupe
中有几个Lens
个组合器。
http://hackage.haskell.org/package/lens-4.1.2/docs/Control-Lens-Loupe.html
他们都有名称涉及#
。
^#
和#%=
都采用ALens
,这是在特定的仿函数选择中实例化的镜头。
如果您需要传递镜片列表,或者您确实需要多次通过,这可能很有用。
2。)
另一个选择,也是我的首选策略,是弄清楚如何同时进行这两项操作。
您正在修改,但想要设置的值。好吧,是的,可以使用<%~
代替%~
来为您提供。
现在你只需要在一个仿函数中实例化镜头,你的代码就会变快。