haskell - 在 Haskell 中构建组合自参照镜头

标签 haskell haskell-lens lenses

包含 self 引用的数据并不少见。这肯定会出现在命令式编程中,但它也可以出现在 Haskell 中。例如,可以将 IORefSTRef 作为数据类型中指向数据类型本身的字段(并且可以使用 RecursiveDo 语法或 mfix 来“打结”构造)。

我想知道是否可以使用镜头做类似的事情。

假设我有一些 s 类型的状态和从 s 到该状态中包含的一些数据的镜头。我希望包含的数据自身能够访问这个镜头,或者换句话说,我希望状态中的数据类似于:

import Data.Lens

data Foo s = Foo
  { self :: Lens' s (Foo s)
  , fooData :: Int
  }

-- A silly `Show` instance so that we can print a few things
instance Show (Foo s) where
  show Foo{..} = "Foo <lens> "<>show fooData

这使用起来有点挑战,但它可以与 Fix 类型一起使用,例如:

newtype Fix f = Fix { unFix :: f (Fix f) }

fixIso :: Iso' (Fix f) (f (Fix f))
fixIso = iso unFix Fix

现在,我可以生成以下值:

myFoo :: Foo (Fix Foo)
myFoo = Foo fixIso 2

在这里,我们有一个 Foo (Fix Foo) 类型的值,带有从其状态到自身的透镜。

我还可以创建一对 Foo(通过使用 Generics 中的 :*:):

import GHC.Generics ((:*:)(..))

pairOfFoo :: (Foo :*: Foo) (Fix (Foo :*: Foo))
pairOfFoo = Foo (fixIso . _1) 2 :*: Foo (fixIso . _2) 4

这基本上是有效的,如:

> pairOfFoo ^. _1
Foo <lens> 2
> pairOfFoo ^. _2
Foo <lens> 4
> Fix pairOfFoo ^. (self $ pairOfFoo ^. _1)
Foo <lens> 2

更大的问题是,感觉我应该能够从 myFoo 创建 pairOfFoo compositionally,但我不明白如何去做吧。也就是说,我想写这样的东西:

pairOf :: (Extendable x, Extendable y) => x (Fix x) -> y (Fix y) -> (x :*: y) (Fix (x :*: y))
pairOf x y = extend x _1 :*: extend y _2

pairOfFoo = pairOf (Foo fixIso 2) (Foo fixIso 4)

class Extendable x where
  extend :: Lens' s' s -> x (Fix s) -> x (Fix s')

但这就是我被困的地方。我不知道如何创建实例 Extendable Foo(或者即使这是正确的签名)。我还认为 (Extendable x, Extendable y) => Extendable (x :*: y) 应该有一个实例(或类似的)。或者,也许还有另一种策略?


问题扩展

现在,假设我们有这样定义的第二种数据类型:

data Bar s = Bar
  { barSelf :: Lens' s (Bar s)
  , barFoo  :: Lens' s (Foo s)
  , barData :: String
  }

Bar (Fix Bar) 类型的值是不可能的,因为 Bar 实际上并不包含 Foo。但是,可以制作类似的东西:

fooBar :: (Foo :*: Bar) (Fix (Foo :*: Bar))
fooBar = Foo (fixIso . _1) 2 :*: Bar (fixIso . _2) (fixIso . _1) "bar"

此外,感觉应该可以有一个实例 Extendable Bar,这样我们就可以在 pairOf 中使用 fooBar 作为参数>。这种情况可能吗?

最佳答案

我想也许你想要:

class Extendable a where
  extend :: Lens' (s' (Fix s')) (a (Fix s')) -> a (Fix s) -> a (Fix s')
instance Extendable Foo where
  extend l (Foo self x) = Foo (fixIso . l) x

pairOf :: (Extendable x, Extendable y)
  => x (Fix x) -> y (Fix y) -> (x :*: y) (Fix (x :*: y))
pairOf foo1 foo2 = extend _1 foo1 :*: extend _2 foo2

pairOfFoo = pairOf (Foo fixIso 2) (Foo fixIso 4)

它似乎有效,并扩展到更复杂的组合:

instance (Extendable x, Extendable y) => Extendable (x :*: y) where
  extend l (x :*: y) = extend (l . _1) x :*: extend (l . _2) y

example2 = pairOf (pairOf (Foo fixIso 2) (Foo fixIso 3)) (Foo fixIso 4)

完整代码:

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}

import GHC.Generics ((:*:)(..))
import Control.Lens

data Foo s = Foo
  { self :: Lens' s (Foo s)
  , fooData :: Int
  }

instance Show (Foo s) where
  show Foo{..} = "Foo <lens> " <> show fooData

newtype Fix f = Fix { unFix :: f (Fix f) }

fixIso :: Iso' (Fix f) (f (Fix f))
fixIso = iso unFix Fix

class Extendable a where
  extend :: Lens' (s' (Fix s')) (a (Fix s')) -> a (Fix s) -> a (Fix s')
instance Extendable Foo where
  extend l (Foo self x) = Foo (fixIso . l) x
instance (Extendable x, Extendable y) => Extendable (x :*: y) where
  extend l (x :*: y) = extend (l . _1) x :*: extend (l . _2) y

pairOf :: (Extendable x, Extendable y)
  => x (Fix x) -> y (Fix y) -> (x :*: y) (Fix (x :*: y))
pairOf foo1 foo2 = extend _1 foo1 :*: extend _2 foo2

pairOfFoo = pairOf (Foo fixIso 2) (Foo fixIso 4)
example2 = pairOf (pairOf (Foo fixIso 2) (Foo fixIso 3)) (Foo fixIso 4)

关于haskell - 在 Haskell 中构建组合自参照镜头,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68370630/

相关文章:

具有不同数量参数的 Haskell 函数

haskell - 自己的数据类型 - 它们与 native 数据类型之间的转换

haskell - Control.Lens 中包装类型类的使用

haskell - 如果 map 中不存在则使用默认值

haskell - 为什么我的数据类型需要一个 Monoid 实例才能使用这个镜头?

haskell - Haskell 中的 NA 值

haskell - MaybeT m 的应用实例假设 Monad m

haskell - 如何使用 Control.Lens 中的 IndexedTraversal 为每个元素执行索引感知操作?

scala - 我如何使用 Monocle 的内置法律实现来测试我自己的镜头?

scala - 如何实现改变状态类型的参数化镜头