本文介绍了我可以用Monad约束制作镜头吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文:这个问题特别提到 Control.Lens (撰写本文时的版本3.9.1)



我一直在使用镜头库,能够读取和写入结构的一块(或遍历片断)是非常好的。然后我想了解一下镜头是否可以用于外部数据库。当然,我需要在 IO Monad 中执行。所以推广:



问题:

给定一个getter,(s - > ma)和setter (b - > s - > mt)其中 m 是一个Monad,可以构造 Lens stab 其中镜头的Functor现在被包含为Monad?还有可能用(。)与其他纯粹功能的镜头合成这些镜头吗?

例子:



我可以使用 Lens(MVar a)(MVar b)ab code> readMVar 和 withMVar



替代方案:



对于 IO monad中的容器,例如<$ c,是否存在与Control.Lens等效的内容$ c> MVar 或 IORef (或 STDIN )?

解决方案

我一直在考虑这个想法一段时间,我称之为可变镜头。到目前为止,我还没有把它做成一个包装,让我知道,如果你从中受益。

首先让我们回顾一下广义范拉赫霍芬镜片(后一些进口,我们稍后需要):
$ b

  { - #LANGUAGE RankNTypes# - } 
导入限定的Data.ByteString作为BS
导入Data.Functor.Constant
导入Data.Functor.Identity
导入Data.Traversable(Traversable)
将限定的Data.Traversable导入为T
import Control .Monad
导入Control.Monad.STM
导入Control.Concurrent.STM.TVar

类型镜头刺= forall f。 (Functor f)=> (a - > f b) - > (s - > ft)
类型Lens'sa = Lens ssaa

我们可以创建这样一个镜头从getter和setter作为

  mkLens ::(s  - > a) - > ; (s→b→t)→>镜头刺
mkLens gsfx = fmap(sx)(f(gx))

一个镜头后面的getter/setter作为

  get :: Lens stab  - > (s  - > a)
get l = getConstant。 l常量

set :: Lens s t a b - > (s - > b - > t)
set lxv = runIdentity $ l(const $ Identity v)x

为例,下面的镜头访问一对中的第一个元素:

  _1 :: Lens '(a,b)a 
_1 = mkLens fst(\(x,y)x' - >(x',y))
- 或直接:_1 f )=(\ b - >(b,c))`fmap` fa

一个可变镜头应该可以工作?获取一些容器的内容涉及一个monadic动作。并且设置一个值不会改变容器,它仍然是一样的,就像一个可变的内存一样。因此,可变镜头的结果必须是monadic,而不是返回类型容器 t ,我们只需要()。而且, Functor 约束是不够的,因为我们需要将它与单点计算交错。因此,我们需要 Traversable

  type MutableLensM msab 
= forall f。 (可穿越f)=> (a - > f b) - > (s - > m(f()))
type MutableLensM'msa
= MutableLensM msaa

Traversable 是一元计算, Functor 是纯粹的计算)。



我们再一次创建帮助函数

$ $ $ $ $ $ $ $ mkLensM ::(Monad m)=> ; (s→m a)→> (s - > b - > m())
- > MutableLensM m s a b
mkLensM g s f x = g x >> gt; = T.mapM(s x)。 f


mget ::(Monad m)=> MutableLensM m s a b - > s - > m a
mget l s = liftM getConstant $ l常量s

mset ::(Monad m)=> MutableLensM m s a b - > s - > b - > m()
mset lsv = liftM runIdentity $ l(const $ Identity v)s

例如,让我们从 STM 中的 TVar 创建一个可变镜头:

  alterTVar :: MutableLensM'STM(TVar a)a 
alterTVar = mkLensM readTVar writeTVar

这些镜片可单面直接与 Lens 组合,例如

  alterTVar。 _1 :: MutableLensM'STM(TVar(a,b))a 








  • 如果我们允许的话,可变镜头可以变得更强大修改函数以包含效果:

      type MutableLensM2 msab 
    =(Traversable f)=> (a - > m(f b)) - > (s - > m(f()))
    type MutableLensM2'm s a
    = MutableLensM2 m s a a

    mkLensM2 ::(Monad m)=> (s→m a)→> (s - > b - > m())
    - > MutableLensM2 msab
    mkLensM2 gsfx = gx>> = f>> = T.mapM(sx)

    然而,它有两个主要缺点:$ b​​


    1. 它不能与纯的 Lens组合

    2. 由于内部动作是任意的,因此它允许您在变异操作本身期间通过突变该(或其他)镜头来拍摄自己。 / li>


  • 单反镜头还有其他的可能性。例如,我们可以创建一个保留原始容器的monadic copy-on-write镜头(就像 Lens 一样),但是操作涉及一些单点操作: p>

     类型LensCOW mstab 
    = forall f。 (可穿越f)=> (a - > f b) - > (s - > m(ft))


  • https://github.com/petr/jLens =noreferrer> jLens - 一个用于可变镜头的Java库,但该API当然远不如Haskell镜头那么好。



Context: This question is specifically in reference to Control.Lens (version 3.9.1 at the time of this writing)

I've been using the lens library and it is very nice to be able to read and write to a piece (or pieces for traversals) of a structure. I then had a though about whether a lens could be used against an external database. Of course, I would then need to execute in the IO Monad. So to generalize:

Question:

Given a getter, (s -> m a) and an setter (b -> s -> m t) where m is a Monad, is possible to construct Lens s t a b where the Functor of the lens is now contained to also be a Monad? Would it still be possible to compose these with (.) with other "purely functional" lenses?

Example:

Could I make Lens (MVar a) (MVar b) a b using readMVar and withMVar?

Alternative:

Is there an equivalent to Control.Lens for containers in the IO monad such as MVar or IORef (or STDIN)?

解决方案

I've been thinking about this idea for some time, which I'd call mutable lenses. So far, I haven't made it into a package, let me know, if you'd benefit from it.

First let's recall the generalized van Laarhoven Lenses (after some imports we'll need later):

{-# LANGUAGE RankNTypes #-}
import qualified Data.ByteString as BS
import           Data.Functor.Constant
import           Data.Functor.Identity
import           Data.Traversable (Traversable)
import qualified Data.Traversable as T
import           Control.Monad
import           Control.Monad.STM
import           Control.Concurrent.STM.TVar

type Lens s t a b = forall f . (Functor f) => (a -> f b) -> (s -> f t)
type Lens' s a = Lens s s a a

we can create such a lens from a "getter" and a "setter" as

mkLens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
mkLens g s  f x = fmap (s x) (f (g x))

and get a "getter"/"setter" from a lens back as

get :: Lens s t a b -> (s -> a)
get l = getConstant . l Constant

set :: Lens s t a b -> (s -> b -> t)
set l x v = runIdentity $ l (const $ Identity v) x

as an example, the following lens accesses the first element of a pair:

_1 :: Lens' (a, b) a
_1 = mkLens fst (\(x, y) x' -> (x', y))
-- or directly: _1 f (a,c) = (\b -> (b,c)) `fmap` f a

Now how a mutable lens should work? Getting some container's content involves a monadic action. And setting a value doesn't change the container, it remains the same, just as a mutable piece of memory does. So the result of a mutable lens will have to be monadic, and instead of the return type container t we'll have just (). Moreover, the Functor constraint isn't enough, since we need to interleave it with monadic computations. Therefore, we'll need Traversable:

type MutableLensM  m s  a b
    = forall f . (Traversable f) => (a -> f b) -> (s -> m (f ()))
type MutableLensM' m s  a
    = MutableLensM m s a a

(Traversable is to monadic computations what Functor is to pure computations).

Again, we create helper functions

mkLensM :: (Monad m) => (s -> m a) -> (s -> b -> m ())
        -> MutableLensM m s a b
mkLensM g s  f x = g x >>= T.mapM (s x) . f


mget :: (Monad m) => MutableLensM m s a b -> s -> m a
mget l s = liftM getConstant $ l Constant s

mset :: (Monad m) => MutableLensM m s a b -> s -> b -> m ()
mset l s v = liftM runIdentity $ l (const $ Identity v) s

As an example, let's create a mutable lens from a TVar within STM:

alterTVar :: MutableLensM' STM (TVar a) a
alterTVar = mkLensM readTVar writeTVar

These lenses are one-sidedly directly composable with Lens, for example

alterTVar . _1 :: MutableLensM' STM (TVar (a, b)) a


Notes:

  • Mutable lenses could be made more powerful if we allow that the modifying function to include effects:

    type MutableLensM2  m s  a b
        = (Traversable f) => (a -> m (f b)) -> (s -> m (f ()))
    type MutableLensM2' m s  a
        = MutableLensM2 m s a a
    
    mkLensM2 :: (Monad m) => (s -> m a) -> (s -> b -> m ())
             -> MutableLensM2 m s a b
    mkLensM2 g s  f x = g x >>= f >>= T.mapM (s x)
    

    However, it has two major drawbacks:

    1. It isn't composable with pure Lens.
    2. Since the inner action is arbitrary, it allows you to shoot yourself in the foot by mutating this (or other) lens during the mutating operation itself.

  • There are other possibilities for monadic lenses. For example, we can create a monadic copy-on-write lens that preserves the original container (just as Lens does), but where the operation involves some monadic action:

    type LensCOW m s t a b
        = forall f . (Traversable f) => (a -> f b) -> (s -> m (f t))
    

  • I've made jLens - a Java library for mutable lenses, but the API is of course far from being as nice as Haskell lenses.

这篇关于我可以用Monad约束制作镜头吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 05:01