有没有办法构建物化镜头?

use*_*596 6 haskell haskell-lens

我试图使用镜头库来解决以下问题:

给定树的列表版本,创建一棵树。例子:

Given:
  [1,2,3,4,5,6,7]

I should make a tree:
     1
   2   3
  4 5 6 7
Run Code Online (Sandbox Code Playgroud)

我的解决方案是使用状态单子和透镜根据深度创建节点。

我的树数据类型:

data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Show)
Run Code Online (Sandbox Code Playgroud)

我想要的一个字符串版本,用于计算镜片:

calculateSetters 1 = ["_Node . _2", "_Node . _3"]
calculateSetters n = (++) <$> calculateSetters (n-1) <*> [ "_Node . _2", "_Node . _3" ]

-- where "_Node" is a prism and "_2" and "_3" are lenses
Run Code Online (Sandbox Code Playgroud)

非拉丝版本会将所有镜头输出到给定深度的空孩子,我可以使用 进行设置.~。非字符串版本的要点如下:

calculateSetters n = Setter <$> combinations where
  combinations = (.) <$> calculateSetters (n-1) <*> [ _Node . _2, _Node . _3 ]
Run Code Online (Sandbox Code Playgroud)

我遇到的两个问题

  1. 我显然无法映射具体构造函数( fmap Setter [ _1, _1]是一个错误,但[Setter _1, Setter _1]不是)。我读到这可能是因为镜片是多态的,最终会绑定到具体的东西上,除非我立即具体化它们。
  2. 我无法制作一个具体化的镜头[Setter _1],然后以某种方式将其与另一个具体化的镜头结合起来[Setter _2]以获得[Setter $ _1 . _2]。看起来你可以在 ghci 中进行一次性操作::t Setter $ runSetter (Setter _2) . runSetter (Setter _2)似乎可以进行类型检查,但我无法使用列表。

我最终只是硬编码了几个,如下所示:

calculateSetters :: Int -> [ReifiedSetter (Tree Int) (Tree Int) (Tree Int) (Tree Int)]
calculateSetters 1 =
  [ Setter $ _Node . _2,
    Setter $ _Node . _3
  ]
calculateSetters 2 =
  [ Setter $ _Node . _2 . _Node . _2,
    Setter $ _Node . _2 . _Node . _3,
    Setter $ _Node . _3 . _Node . _2,
    Setter $ _Node . _3 . _Node . _3
  ]
calculateSetters 3 =
  [ Setter $ _Node . _2 . _Node . _2 . _Node . _2,
    Setter $ _Node . _2 . _Node . _2 . _Node . _3,
    Setter $ _Node . _2 . _Node . _3 . _Node . _2,
    Setter $ _Node . _2 . _Node . _3 . _Node . _3,
    Setter $ _Node . _3 . _Node . _2 . _Node . _2,
    Setter $ _Node . _3 . _Node . _2 . _Node . _3,
    Setter $ _Node . _3 . _Node . _3 . _Node . _2,
    Setter $ _Node . _3 . _Node . _3 . _Node . _3
  ]
calculateSetters 4 =
  [ Setter $ _Node . _2 . _Node . _2 . _Node . _2 . _Node . _2,
    Setter $ _Node . _2 . _Node . _2 . _Node . _2 . _Node . _3,
    Setter $ _Node . _2 . _Node . _2 . _Node . _3 . _Node . _2,
    Setter $ _Node . _2 . _Node . _2 . _Node . _3 . _Node . _3,
    Setter $ _Node . _2 . _Node . _3 . _Node . _2 . _Node . _2,
    Setter $ _Node . _2 . _Node . _3 . _Node . _2 . _Node . _3,
    Setter $ _Node . _2 . _Node . _3 . _Node . _3 . _Node . _2,
    Setter $ _Node . _2 . _Node . _3 . _Node . _3 . _Node . _3,
    Setter $ _Node . _3 . _Node . _2 . _Node . _2 . _Node . _2,
    Setter $ _Node . _3 . _Node . _2 . _Node . _2 . _Node . _3,
    Setter $ _Node . _3 . _Node . _2 . _Node . _3 . _Node . _2,
    Setter $ _Node . _3 . _Node . _2 . _Node . _3 . _Node . _3,
    Setter $ _Node . _3 . _Node . _3 . _Node . _2 . _Node . _2,
    Setter $ _Node . _3 . _Node . _3 . _Node . _2 . _Node . _3,
    Setter $ _Node . _3 . _Node . _3 . _Node . _3 . _Node . _2,
    Setter $ _Node . _3 . _Node . _3 . _Node . _3 . _Node . _3
  ]
calculateSetters _ = error "unsupported; too lazy"
Run Code Online (Sandbox Code Playgroud)

哪个有效,但我想知道是否以及如何以编程方式执行此操作?

lef*_*out 6

您当然可以编写具体化的设置器,尽管我不知道执行此操作的标准函数。但这可以通过明显的方式完成:

composeSetters :: ReifiedSetter' a b -> ReifiedSetter' b c -> ReifiedSetter' a c
composeSetters (Setter f) (Setter g) = Setter (f . g)
Run Code Online (Sandbox Code Playgroud)

然后,其他一切都可以仅使用具体化的 setter 来完成,因此不会出现必然的问题:

calculateSetters :: Int -> [ReifiedSetter' (Tree Int) (Tree Int)]
calculateSetters 1 =
  [ Setter (_Node . _2)
  , Setter (_Node . _3)
  ]
calculateSetters n
     = composeSetters <$> calculateSetters (n-1) <*> calculateSetters 1
Run Code Online (Sandbox Code Playgroud)

可编译版本:

{-# LANGUAGE RankNTypes      #-}
{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

data Tree a = Nil
            | Node { _nodeValue :: a
                   , _lSubtree :: Tree a
                   , _rSubtree :: Tree a
                   }
  deriving (Show)

makeLenses ''Tree

composeSetters :: ReifiedSetter' a b -> ReifiedSetter' b c
                        -> ReifiedSetter' a c
composeSetters (Setter f) (Setter g) = Setter (f . g)

subtreeSetters :: [[ReifiedSetter' (Tree Int) (Tree Int)]]
subtreeSetters
   = [Setter id]
   : [ composeSetters <$> strs <*> [Setter lSubtree, Setter rSubtree]
     | strs <- subtreeSetters ]
Run Code Online (Sandbox Code Playgroud)