Haskell镜头中不同的二传手和吸气剂类型

时间:2013-07-22 17:23:29

标签: haskell lens

我有data type G,其中包含字段_repr :: Data.Graph.Inductive.Gr String String。通常的方式是,在将新节点添加到Gr图中时,我们必须提供一个LNode a对象,它基本上被定义为(Int, a)的元组,其中Int是Graph中的节点索引 - 请参阅下面的示例函数add

我想实现一个函数addx,它会自动计算索引(例如使用Data.Graph.Inductive.newNodes函数)。我希望addx具有addx :: String -> G -> Int的签名,此函数将计算新的自由索引,修改图G并返回此计算的索引。在Haskell中是否可以创建这样的函数(在这种情况下将修改现有对象 - G) - 使用镜头或类似的东西?

我看过,Haskell镜头定义为lens :: (a -> c) -> (a -> d -> b) -> Lens a b c d,镜头基本上是“getter”和“setter”,因此它的签名允许不同类型的getter输出(c),setter值(d)和setter输出(b)。

import qualified Data.Graph.Inductive     as DG

data G = G { _repr :: DG.Gr String String, _name::String} deriving ( Show )

empty :: G
empty = G DG.empty ""

add :: DG.LNode String -> G -> G
add node g = g{_repr = DG.insNode node $ _repr g}

-- is it possible to define it?
addx :: String -> G -> Int
addx name g = undefined

main :: IO ()
main = do
    let g = add (1, "test2")
          $ add (0, "test1")
          $ empty

        n1 = addx "test2" g
        g2 = DG.insEdge(n1,0)
           $ DG.insEdge(0,1)

    print $ g

1 个答案:

答案 0 :(得分:2)

addx的类型已被破坏,因为您无法在不返回修改后的表单G的情况下修改纯函数中的addx1 :: String -> G -> (Int, G)。如果你对Haskell monad有一个聪明的眼睛,你可能会注意到它有一个同构类型,addx2 :: String -> State G Int

我们可以将所有内容与这种“有状态”的方向对齐

add' node = do g <- get
               put $ g { _repr = DB.insNode node $ _repr g }

使镜片更加简洁

add'' node = repr %= DB.insNode node

这里真正的挑战是,在一天结束时,跟踪节点身份。一种方法是将其与您的类型中的repr一起携带

data G = G { _repr :: DG.Gr String String, _name :: String, _index :: Int } 
empty = G DG.empty "" 0

然后在构建节点时使用它(再次使用镜头!)

addx' name = do i <- use index
                repr %= DB.insNode (i, node)
                i += 1