4

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
Community
  • 1
  • 1
Uli Köhler
  • 13,012
  • 16
  • 70
  • 120
  • 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. – Ganesh Sittampalam Jan 17 '14 at 18:30
  • @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 Jan 17 '14 at 18:32
  • @GaneshSittampalam I removed the nodeNum parameter from the post. – Uli Köhler Jan 17 '14 at 18:33
  • 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? – Ganesh Sittampalam Jan 17 '14 at 18:33
  • 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. – Ganesh Sittampalam Jan 17 '14 at 18:34
  • @GaneshSittampalam I'll try to do that! – Uli Köhler Jan 17 '14 at 18:34
  • @UliKöhler In the new (edited) form, your code has no obvious type errors. Please include the code that actually produces the error. – kosmikus Jan 17 '14 at 18:34
  • @kosmikus Sorry for the nodeNum confusion. I added a minimal runnable example plus the full error message. – Uli Köhler Jan 17 '14 at 18:40
  • You've still got the `nodeNum` parameter in the complete example you posted. – Ganesh Sittampalam Jan 17 '14 at 18:40
  • @GaneshSittampalam My apologies, I should really have re-checked after copy&pasting – Uli Köhler Jan 17 '14 at 18:42
  • Sorry, I just noticed I'm passing the Graph directly into `graphFromEdges'`. I'll fix that immediately. – Uli Köhler Jan 17 '14 at 18:43
  • @GaneshSittampalam Sorry again for the confusion. I now tried to further condense the issue by removing all custom code (most of the errors in it really seem to be caused by `nodeNum` before). I believe my main problem is the inability to handle the value of the `graphFromEdges'` call. – Uli Köhler Jan 17 '14 at 18:54

1 Answers1

4

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.

Ganesh Sittampalam
  • 28,821
  • 4
  • 79
  • 98
  • Thanks for the suggestion, the `(graph, _)` syntax is, now that I think about it, quite intuitive (and it help me a lot). However, when `runghc`-ing it, the following error is printed: `graph.hs:3:5: Couldn't match expected type `[Char] -> Maybe Vertex' with actual type `(Graph, t0)' ` – Uli Köhler Jan 17 '14 at 19:02
  • 1
    `graphFromEdges'` is not in `IO`, so you should `let`-bind it instead. Also, if you really want `["n0","n1","n2"]`, you should do `let (graph, f) = graphFromEdges' ...` and then something like `print $ map ((\ (n, _, _) -> n) . f) (vertices graph)`. – kosmikus Jan 17 '14 at 19:03
  • Apologies, I focused on the tuple and forgot the IO. – Ganesh Sittampalam Jan 17 '14 at 19:05
  • Thanks, the new version mostly works (it prints `[0,1,2]`), I assume you need to keep the second tuple (--> `let (graph, vertexMap) = ...`) to map the vertices. – Uli Köhler Jan 17 '14 at 19:11
  • 1
    Indeed. You probably need to do some playing around with the API and some more experience reading the type error messages to understand what you've got wrong. – Ganesh Sittampalam Jan 17 '14 at 19:11
  • @UliKöhler Just see my comment above. – kosmikus Jan 17 '14 at 19:19
  • @kosmikus Thanks to your code, I figured out how to print `["n0","n1","n2"]`, the edit of Ganeshs post is currently in the review queue! Thanks to both of you for your suggestions! – Uli Köhler Jan 17 '14 at 19:21
  • people generally reject code changes in suggested edits because they alter the poster's intent, but I've manually applied it now – Ganesh Sittampalam Jan 17 '14 at 19:46
  • Thanks @GaneshSittampalam for your note regarding edits. I don't think it really changed the posts intent, assuming the intent was to provide a correct, runnable example. Because it's explicitly noted (originally from my minimal example, which you copied and edited) that its intent is to print `["n0","n1","n2"]`, so I think it's rather a bugfix, but in no way an intent-changing edit. I don't think posting another answer would be a better solution. – Uli Köhler Jan 17 '14 at 21:08
  • 1
    The edit was fine from my point of view which was why I applied it by hand - it was whoever random hit it in the edit queue that would have rejected it without being aware of the context. – Ganesh Sittampalam Jan 17 '14 at 21:22
  • @GaneshSittampalam I can see your point now and I fully agree. Thanks again for helping me resolve this issue! – Uli Köhler Jan 21 '14 at 02:38