1

I am trying to make a simple graph structure and I wrote the following. But GHG raises error and I stacked there. This is the first time I make my own typeclass so maybe I am doing something terribly wrong. Can somebody explain what is wrong?

I found a similar question but I don't think it applies to my case.: Error binding type variables in instance of typeclass

class Link l where
  node :: (Node n) => l -> n

class Node n where
  links :: (Link l) => n -> [l]

data (Node n) => SimpleLink n =
  SimpleLink
  { simpleLinkNode :: n
  } deriving (Show, Read, Eq)

instance (Node n) => Link (SimpleLink n) where
  node = simpleLinkNode

data (Link l) => SimpleNode l =
  SimpleNode
  { simpleNodeLinks :: [l]
  } deriving (Show, Read, Eq)

instance (Link l) => Node (SimpleNode l) where
  links = simpleNodeLinks

This is the error message I've got:

***.hs:13:10:Could not deduce (n ~ n1)
from the context (Node n)
  bound by the instance declaration
  at ***.hs:12:10-40
or from (Node n1)
  bound by the type signature for
             node :: Node n1 => SimpleLink n -> n1
  at ***.hs:13:3-23
  `n' is a rigid type variable bound by
      the instance declaration
      at ***.hs:12:16
  `n1' is a rigid type variable bound by
       the type signature for node :: Node n1 => SimpleLink n -> n1
       at ***.hs:13:3
Expected type: SimpleLink n -> n1
  Actual type: SimpleLink n -> n
In the expression: simpleLinkNode
In an equation for `node': node = simpleLinkNode

***.hs:21:11:Could not deduce (l ~ l1)
from the context (Link l)
  bound by the instance declaration
  at ***.hs:20:10-40
or from (Link l1)
  bound by the type signature for
             links :: Link l1 => SimpleNode l -> [l1]
  at ***.hs:21:3-25
  `l' is a rigid type variable bound by
      the instance declaration
      at ***.hs:20:16
  `l1' is a rigid type variable bound by
       the type signature for links :: Link l1 => SimpleNode l -> [l1]
       at ***.hs:21:3
Expected type: SimpleNode l -> [l1]
  Actual type: SimpleNode l -> [l]
In the expression: simpleNodeLinks
In an equation for `links': links = simpleNodeLinks

Edit 1

I tried some of Daniel's suggestions. But I couldn't make them work.

constructor class

Got: "`n' is not applied to enough type arguments"

class Link l n where
  node :: Node n l => l n -> n l
class Node n l where
  links :: Link l n => n l -> [l n]

multi-parameter type class (MPTC)

Got: "Cycle in class declarations (via superclasses)"

class (Node n) => Link l n where
  node :: l -> n
class (Link l) => Node n l where
  links :: n -> [l]

MPTC with functional dependencies

Got: "Cycle in class declarations (via superclasses)"

class (Node n) => Link l n | l -> n where
  node :: l -> n
class (Link l) => Node n l | n -> l where
  links :: n -> [l]

Goal (Edit 2)

What I want to implement is a directed acyclic graph structure like the following (more specifically, a Factor graph).

PRML Figure 8.51
(source: microsoft.com)

There are two kinds of node (white circle and red square) and they connect only to the different type of node, meaning that there are two kinds of links.

I want different version of nodes and links which have data (arrays) attached to them. I also want "vanilla" DAG which has only one type of node and link. But for traversing them, I want only one interface to do that.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
tkf
  • 2,990
  • 18
  • 32
  • 1
    This is more of a code review than an answer to the question, but none of this code *does* anything. As such, I'm very suspicious of its existence: can you get by with, say, an empty file instead of this code? (In general, if you have only one instance of a class, you probably should just delete the class. Similarly, there are reasons to have data types that rename an existing type, but it's not clear that they apply here!) – Daniel Wagner Jul 10 '12 at 21:02
  • Thanks for your review. I am planning to put more instances to implement a graph structure like [this (an image)](http://research.microsoft.com/en-us/um/people/cmbishop/prml/prmlfigs-png/Figure8.51.png) (more specifically, a [Factor graph](http://en.wikipedia.org/wiki/Factor_graph)). There are two kinds of node (white circle and red square) and they connect only to the different type of node, meaning that there are two kinds of links. – tkf Jul 11 '12 at 14:05
  • As far as I can see, you do not need to define those type classes. I will post a bipartite graph data structure below. – Chris Kuklewicz Jul 12 '12 at 10:50
  • A Factor Graph is not directed. It is an undirected bipartite graph. There is only one kind of link: with a circle and a square end. – Chris Kuklewicz Jul 12 '12 at 11:17

4 Answers4

6

The signature of the class methods

class Link l where
  node :: (Node n) => l -> n

class Node n where
  links :: (Link l) => n -> [l]

say that "whatever type the caller desires, node resp. links can produce it, as long as it's a member of Link resp. Node", but the implementation says that only one specific type of value can be produced.

It's fundamentally different from interfaces in OOP, where the implementation decides the type and the caller has to take it, here the caller decides.


You are running into kind problems with your constructor class attempt. Your classes take two parameters, l of kind kl and n of kind kn. The kinds of the arguments to (->) must both be *, the kind of types. So for l n to be a well-kinded argument of (->), l must be a type constructor taking an argument of kind kn and creating a result of kind *, i.e.

l :: kn -> *

Now you try to make the result type of node be n l, so that would mean

n :: kl -> *

But above we saw that kl = kn -> *, which yields

n :: (kn -> *) -> *

resp. kn = (kn -> *) -> *, which is an infinite kind. Infinite kinds, like infinite types, are not allowed. But kind-inference is implemented only very rudimentary, so the compiler assumes that the argument to l has kind *, but sees from n l that n has kind kl -> *, hence as an argument to l, n has the wrong kind, it is not applied to enough type arguments.

The normal use of constructor classes is a single-parameter class

class Link l where
    node :: l nod -> nod

class Node n where
    links :: n lin -> [lin]

-- note that we don't have constraints here, because the kinds don't fit

instance Link SimpleLink where
    node = simpleLinkNode

instance Node SimpleNode where
    links = simpleNodeLinks

You have to remove the DatatypeContexts from the data declarations,

  1. They have been removed from the language (they are available via an extension)
  2. They were never useful anyway

then the above compiles. I don't think it would help you, though. As Chris Kuklewicz observed, your types chase their own tail, you'd use them as

SimpleLink (SimpleNode (SimpleLink (SimpleNode ... {- ad infinitum -})))

For the multiparameter classes, you can't have each a requirement of the other, as the compiler says, that causes a dependency cycle (also, in your constraints you use them with only one parameter,

class Node n => Link l n where ...

which is malformed, the compiler would refuse that if the cycle is broken).

You could resolve the cycle by merging the classes,

class NodeLinks l n | l -> n, n -> l where
    node :: l -> n
    links :: n -> l

but you'd still have the problems that your types aren't useful for that.

I don't understand your goal well enough to suggest a viable solution, sorry.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • Are there any way to say that `n` must be a type which `l` "supports" (something like `class (Node n) => Link (l n) where ...`)? – tkf Jul 11 '12 at 14:15
  • 1
    Several, depending on what you want and have. You can have a constructor class `class Link l where node :: Node n => l n -> n`. Or you can define a multiparameter type class, `class (Node n) => Link l n where node :: l -> n`, or an MPTC with functional dependencies, `class (Node n) => Link l n | l -> n where node :: l -> n`, the latter is nowadays more usually done with a type family, `class Link l where { type NodeType l; node :: (Node a, a ~ NodeType l) => l -> a; }`. – Daniel Fischer Jul 11 '12 at 14:47
  • Thanks. I tried some of your suggestions, but couldn't make them work. Please see the code above in my question (Edit 1). – tkf Jul 11 '12 at 15:41
4

Can somebody explain what is wrong?

An initial issue before I explain the error messages: Polymorphic data types are good, but in the end there has to be concrete type being used.

With SimpleNode of kind * -> * and SimpleLinks of kind * -> * there is no concrete type:

SimpleNode (SimpleLink (SimpleNode (SimpleLink (SimpleNode (...

You cannot have and infinite type in Haskell, though newtype and data get you closer:

type G0 = SimpleNode (SimpleLink G0)  -- illegal
newtype G1 = G1 (SimpleNode (SimpleLink G1))   -- legal
data G2 = G2 (SimpleNode (SimpleLink G2))   -- legal

Perhaps you need to rethink your data types before creating the type class.

Now on to the error message explanation: Your type class Link defines a function node

class Link l where
  node :: (Node n) => l -> n

The node is a magical OOP factory that, given the type and value of l, can then make any type n (bounded by Node n) the caller of node wishes. This n has nothing to do with the n in your instance:

instance (Node n) => Link (SimpleLink n) where
  node = simpleLinkNode

To repeat myself: the n in the instance above is not the same n as in the node :: (Node n) => l -> n definition. The compiler makes a related but fresh name n1 and gives you the error:

  `n' is a rigid type variable bound by
      the instance declaration
      at ***.hs:12:16
  `n1' is a rigid type variable bound by
       the type signature for node :: Node n1 => SimpleLink n -> n1
       at ***.hs:13:3

The n in the instance is taken from the type (SimpleLink n) of the input to the node function. The n1 is the type that the caller of node is demanding that this magical factory produce. If n and n1 were the same then the compiler would be happy...but your definition of the type class and instance do not constrain this and thus the code snippet is rejected.

The analogous story is repeated for the error in SimpleLink. There is no silver-bullet fix for this. I expect that you need to rethink and redesign this, probably after reading other people's code in order to learn ways to accomplish your goal.

What is your goal? Graph data structures can be quite varied and the details matter.

Chris Kuklewicz
  • 8,123
  • 22
  • 33
  • What I want to implement is graph structure (more specifically, a [Factor graph](http://en.wikipedia.org/wiki/Factor_graph)) like [this (an image)](http://research.microsoft.com/en-us/um/people/cmbishop/prml/prmlfigs-png/Figure8.51.png). There are two kinds of node (white circle and red square) and they connect only to the different type of node. I want to represent the kinds of node as Haskell types. – tkf Jul 11 '12 at 14:03
1

I am breaking stack overflow etiquette and adding a second answer to keep this separate. This is a simple code example for a bipartite undirected graph with unlabeled edges, which might be useful to model a Factor Graph:

-- Bipartite graph representation, unlabeled edges

-- Data types to hold information about nodes, e.g. ID number
data VariableVertex = VV { vvID :: Int }  deriving (Show)
data FactorVertex = FV { fvID :: Int }  deriving (Show)

-- Node holds itself and a list of neighbors of the oppostite type
data Node selfType adjacentType =
  N { self :: selfType
    , adj :: [Node adjacentType selfType] }

-- A custom Show for Node to prevent infinite output
instance (Show a, Show b) => Show (Node a b) where
  show (N x ys) = "Node "++ show x ++ " near " ++ show (map self ys)

-- Type aliases for the two node types that will be used
type VariableNode = Node VariableVertex FactorVertex
type FactorNode = Node FactorVertex VariableVertex

data FactorGraph = FG [VariableNode] [FactorNode]  deriving (Show)

v1 = N (VV 1) [f1,f2]
v2 = N (VV 2) [f2]
v3 = N (VV 3) [f1,f3]
f1 = N (FV 1) [v1,v3]
f2 = N (FV 2) [v1,v2]
f3 = N (FV 3) [v3]

g = FG [v1,v2,v3] [f1,f2,f3]
Chris Kuklewicz
  • 8,123
  • 22
  • 33
  • Thanks, I think I can start from your Node data type. I guess I should start with concrete types without making classes. – tkf Jul 12 '12 at 15:47
  • BTW, In your example, you store connection in both directions (e.g., v1 to f1 and f1 to v1). It's like doubly linked list, right? I read this (http://stackoverflow.com/questions/10386616/how-to-implement-doubly-linked-lists) and I thought I'd better avoid this implementation. That's why I wanted to implement it as a DAG. I think you actually can hold the same graph as DAG (although mapping is not unique). I thought it's better because you can "update" some data attached to node using something like zipper (I am not sure about this because I am not familiar with zipper). – tkf Jul 12 '12 at 15:48
  • I still can't see how you can "attach" some data to `Node` (having, e.g., `getDataFromFactorNode :: FactorNode -> [Double]`) and share traversing function (say, `adjacent :: Node a b -> [Node b a]`). – tkf Jul 12 '12 at 16:02
  • You can attach anything you want to VariableVertex and/or FactorVertex. Just add some fields. – Chris Kuklewicz Jul 12 '12 at 16:31
  • Indeed, I noticed while writing my own answer (http://stackoverflow.com/a/11456561/727827). I think your solution is much better! – tkf Jul 12 '12 at 16:42
0

With the hint from Chris Kuklewicz (http://stackoverflow.com/a/11450715/727827), I got the code I wanted in the first place.

However, I think Crhis's answer (using *Vertex to hold data) is simple and better. I am leaving this here to clarify what I wanted.

class NodeClass n where
  adjacent :: n a b -> [n b a]

data Node selfType adjacentType =
  N
  { selfNode :: selfType
  , adjNode :: [Node adjacentType selfType] }

data NodeWithData selfType adjacentType =
  NWD
  { selfNodeWithData :: selfType
  , adjNodeWithData :: [NodeWithData adjacentType selfType]
  , getDataWithData :: [Double]
  }

instance NodeClass Node where
  adjacent = adjNode

instance NodeClass NodeWithData where
  adjacent = adjNodeWithData

data VariableVertex = VV { vvID :: Int }  deriving (Show)
data FactorVertex = FV { fvID :: Int }  deriving (Show)

type VariableNode = Node VariableVertex FactorVertex
type FactorNode = Node FactorVertex VariableVertex

type VariableNodeWithData = NodeWithData VariableVertex FactorVertex
type FactorNodeWithData = NodeWithData FactorVertex VariableVertex
tkf
  • 2,990
  • 18
  • 32