4
votes

I'm currently trying to write a GraphML reader for Haskell (see this previous question). As I'm quite new to Haskell, despite the steep learning curve I haven't fully managed to learn the ability to deduce examples from the Hackage documentation only.

What I currently have is a Graph instance:

data Graph = Graph
  { graphId :: String,
    nodes :: [String],
    edges :: [(String, String)] -- (Source, target)
  }
  deriving (Show, Eq)

I want to convert them to a Data.Graph. I assume graphFromEdges or graphFromEdges' are the correct functions to do this.

My current attempt is to create the 3-tuples (nodeid, nodeid, [edge targets])

-- Convert a graph node into a Data.Graph-usable
getDataGraphNode :: Graph -> String -> (String, String, [String])
getDataGraphNode graph node = (node, node, getTargets node graph)

-- Convert a Graph instance into a Data.Graph list of (node, nodeid, edge) tuples
getDataGraphNodeList :: Graph -> [(String, String, [String])]
getDataGraphNodeList graph = map (getDataGraphNode graph) (nodes graph)

However, GHC won't compile this because of type errors:

 Couldn't match expected type `[(node0, key0, [key0])]'
            with actual type `(String, String, [String])'

Could you point me to an example, or preferably even describe a general way how to deduce an example from the documentation (function signature, in this case)? Do I need to use the Vertex type from Data.Graph? I'm currently unable to figure out what the node0 and key0 types need to be.

Here's a minimal example that shows the issue I'm having with Data.Graph. I'm unsure of how this relates to misused types, even if many of the errors in my own code occured because of problems with the type system:

import Data.Graph
main = do
    graph <- graphFromEdges' [("n0","n0",["n1"]), ("n1","n1",["n2"]), ("n2","n2",[])]
    -- This should print ["n0","n1","n2"]
    print $ vertices graph

It produces the following error message:

bar.hs:4:5:
Couldn't match type `IO' with `(,) Graph'
    Expected type: (Graph, ())
      Actual type: IO ()
    In a stmt of a 'do' block: print $ vertices graph
    In the expression:
      do { graph <- graphFromEdges'
                      [("n0", "n0", ["n1"]), ("n1", "n1", ["n2"]), ....];
           print $ vertices graph }
    In an equation for `main':
        main
          = do { graph <- graphFromEdges' [("n0", "n0", [...]), ....];
                 print $ vertices graph }

bar.hs:4:22:
    Couldn't match type `Vertex -> ([Char], [Char], [[Char]])'
                  with `GHC.Arr.Array Vertex [Vertex]'
    Expected type: Graph
      Actual type: Vertex -> ([Char], [Char], [[Char]])
    In the first argument of `vertices', namely `graph'
    In the second argument of `($)', namely `vertices graph'
    In a stmt of a 'do' block: print $ vertices graph
1
What's the nodeNum parameter to getDataGraphNode supposed to be for? It's not being used and its at least one source of your errors as it means that getDataGraphNode graph has type String -> Int -> (String, String, [String]) so map (getDataGraphNode graph) has type [String] -> [Int -> (String, String, [String])] (i.e. it produces a list of functions) which is unlikely to be what you want.GS - Apologise to Monica
@GaneshSittampalam Ah, sorry, I added that a few minutes before (desperately) posting on SO, because I assumed I have to create a Vertex or Node from Data.Graph -- they seemed to need numeric indices for the nodes. However I think this was entirely the wrong direction.Uli Köhler
@GaneshSittampalam I removed the nodeNum parameter from the post.Uli Köhler
Can you edit to clarify what code is producing what error message, including the full text of the error message that states what doesn't match what?GS - Apologise to Monica
I would expect the fragment you've given (after your edit) to compile, but I may have missed something or your problem may be elsewhere.GS - Apologise to Monica

1 Answers

4
votes

Try this:

import Data.Graph
main = do
    let (graph, vertexMap) = graphFromEdges' [("n0","n0",["n1"]), ("n1","n1",["n2"]),("n2","n2",[])]
    -- This should print ["n0","n1","n2"]
    print $ map ((\ (vid, _, _) -> vid) . vertexMap) (vertices graph)

graphFromEdges' returns a pair of values and the Graph is the first of that pair. Also, in this context <- is for things that return IO something whereas graphFromEdges' returns a pure value.

The key error was this:

Couldn't match type `IO' with `(,) Graph'
    Expected type: (Graph, ())
      Actual type: IO ()

although it was reported in a slightly misleading way - the report would have been better if you'd given main a type signature main :: IO (). In general if you're confused by type errors it's worth trying to work out what type you think things should be and using explicit type signatures.