Reputation: 11803
I've got a data type
G
, which have got field _repr :: Data.Graph.Inductive.Gr String String
. The normal way, when adding new node into Gr
graph, we have to provide an LNode a
object, which basically is defined as a tuple of (Int, a)
, where Int is the nodes index in Graph - see the example function add
below.
I want to implement a function addx
, which will compute the index automatically (for example by using Data.Graph.Inductive.newNodes
function). I want the addx
to have signature of addx :: String -> G -> Int
and this function will compute new free index, modify the graph G and return this computed index. Is it possible in Haskell to create such function (which will modify an existing object - G
in this case) - by using lenses or something like that?
I have seen, that Haskell lens is defined like lens :: (a -> c) -> (a -> d -> b) -> Lens a b c d
and lens is basically a "getter" and "setter", so its signature allows for different types of getter output (c
), setter value (d
) and setter output (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
Upvotes: 1
Views: 485
Reputation: 74334
Your type for addx
is broken since you can't modify G
in a pure function without returning the modified form like addx1 :: String -> G -> (Int, G)
. If you have a clever eye for Haskell monads, you might notice that this has an isomorphic type, addx2 :: String -> State G Int
.
We can align everything to this "stateful" orientation
add' node = do g <- get
put $ g { _repr = DB.insNode node $ _repr g }
and make it more succinct with lenses
add'' node = repr %= DB.insNode node
The real challenge here is, at the end of the day, tracking the node identity. One way would be to carry it alongside the repr
in your type
data G = G { _repr :: DG.Gr String String, _name :: String, _index :: Int }
empty = G DG.empty "" 0
then use that when building nodes (using lenses again!)
addx' name = do i <- use index
repr %= DB.insNode (i, node)
i += 1
Upvotes: 2