基于Monad类型类,是否有一个van Laarhoven光学实现?

9
据我所了解,每个van Laarhoven光学类型可以通过对类型构造函数的限制来定义:
type Lens      s t a b = forall f. Functor f     => (a -> f b) -> s -> f t
type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t
-- etc.

如果我们选择Monad作为约束条件,它是否以某种有意义的方式形成了一种“光学”?
type Something s t a b = forall f. Monad f => (a -> f b) -> s -> f t

我的直觉是,Monad约束可能过于严格,无法从这样的结构中获得任何价值:由于 Const 函子不是 Monad,我们无法通过将 f 特化为 Const 来推导出类似于 view 的函数。尽管如此,我们仍然可以对这个 Something 类型做一些事情;只是我不确定我们能否从中获得任何特别有用的东西。
我感兴趣的原因是,van Laarhoven 光学的类型与修改“可变引用”类型(如 IORef)的函数的类型非常相似。例如,我们可以轻松地实现
modifyIORefM :: MonadIO m => IORef a -> (a -> m a) -> () -> m ()

当部分应用于 IORef 时,具有以下形式

type SomethingIO s t a b = forall f. MonadIO f => (a -> f b) -> s -> f t

a = bs = t = () 时,我不确定这是一个有意义的还是无意义的巧合。


1
有MLens,但是经过快速扫描,我认为那不是你想要的。 - Bartek Banachewicz
我也很久以前就在想这个问题! - Abastro
1个回答

1
实际上,这样的光学元件是一个稍微有些不方便的“遍历”。

这是因为实际上我们使用了一个“遍历”:

type Traversal s t a b = forall f. (Applicative f) => (a -> f b) -> (s -> f t)

有两件事情。从一个 s 中获取 a 列表,我们可以使用 Const 函子来实现:

toListOf :: Traversal s t a b -> s -> [a]
toListOf t = getConst . t (Const . (:[]))

a替换为b,将s变成t。一种方法是使用State函数器,忽略匹配ab的计数问题,我们有:

setListOf :: Traversal s t a b -> [b] -> s -> t
setListOf t bs s = evalState (t (\a -> state (\(b:bs) -> (b, bs))) s) bs

如果我们使用一个带有Monad约束的光学器:

type TraversalM s t a b = forall f. (Monad f) => (a -> f b) -> (s -> f t)

我们仍然可以执行这两个操作。由于State是一个单子,setListOf操作可以使用相同的实现:

setListOfM :: Traversal s t a b -> [b] -> s -> t
setListOfM t bs s = evalState (t (\a -> state (\(b:bs) -> (b, bs))) s) bs

对于 toListOfConst [a] 没有 Monad 实例,但我们可以使用 Writer monad 提取 a 值,只要我们有一个虚拟的 b 值来让类型检查器满意即可。
toListOfM :: TraversalM s t a b -> b -> s -> [a]
toListOfM t dummy_b s = execWriter (t (\a -> tell [a] >> pure dummy_b) s)

或者,由于Haskell有底部:

toListOfM' :: TraversalM s t a b -> s -> [a]
toListOfM' t s = execWriter (t (\a -> tell [a] >> pure undefined) s)

自包含代码:

import Data.Functor.Const
import Control.Monad.State
import Control.Monad.Writer

type Traversal s t a b = forall f. (Applicative f) => (a -> f b) -> (s -> f t)

toListOf :: Traversal s t a b -> s -> [a]
toListOf t = getConst . t (Const . (:[]))

setListOf :: Traversal s t a b -> [b] -> s -> t
setListOf t bs s = evalState (t (\a -> state (\(b:bs) -> (b, bs))) s) bs

type TraversalM s t a b = forall f. (Monad f) => (a -> f b) -> (s -> f t)

toListOfM :: TraversalM s t a b -> b -> s -> [a]
toListOfM t dummy_b s = execWriter (t (\a -> tell [a] >> pure dummy_b) s)

toListOfM' :: TraversalM s t a b -> s -> [a]
toListOfM' t s = execWriter (t (\a -> tell [a] >> pure undefined) s)

setListOfM :: TraversalM s t a b -> [b] -> s -> t
setListOfM t bs s = evalState (t (\a -> state (\(b:bs) -> (b, bs))) s) bs

listItems :: Traversal [a] [b] a b
listItems = traverse

listItemsM :: TraversalM [a] [b] a b
listItemsM = mapM

main = do
  -- as a getter
  print $ toListOf listItems [1,2,3]
  print $ toListOfM listItemsM 99 [1,2,3]  -- dummy value
  print $ toListOfM' listItemsM [1,2,3]    -- use undefined
  -- as a setter
  print $ setListOf listItems [4,5,6] [1,2,3]
  print $ setListOfM listItemsM [4,5,6] [1,2,3]

1
有趣!我认为这回答了我的问题。TraversalMTraversal似乎不太可能是同构的,所以我想知道是否有任何TraversalM不能变成Traversal - Katie Casamento

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接