I would like to solve this problem in Clean (a language very similar to Haskell):
There is a class Node t
, with two instances: instance Node EdgeList
and instance Node Adjacency
. I would like to create a Graph, which is an array or list of Nodes.
The definition of the Graph
is:
class Graph t1 t2 | Node t2 where
resetGraph :: (t1 t2) -> (t1 t2)
graphSize :: (t1 t2) -> Int
...
I would like to write the instances. One with array, and one with list. First, I tried with list, but I get an error: t2 not defined
instance Graph [t1] t2 | t2 t1 where
(resetGraph) :: [t1] -> [t1]
(resetGraph) x = []
...
It will be called for example like this: resetGraph listAdj
where listAdj is a list of Adjacency
Nodes
If I just write: instance Graph [tt] tt
then I get this error: Error: this type variable occurs more than once in an instance type
.
The first thing to understand here is that when you write
class Graph l t | Node t where
resetGraph :: (l t) -> l t
you give l
kind *->*
. Kinds are an abstraction from types. Roughly, kind *
means you have a 'complete' type. For example, Int
, [Char]
, a -> String
are all of kind *
. When a type still 'needs an argument', it has kind *->*
. For example, if you have :: Maybe a = Just a | Nothing
, then Maybe Int
is of kind *
, but simply Maybe
is of kind *->*
because it still needs one argument. So, when writing resetGraph :: (l t) -> l t
, the compiler recognises that t
is an argument to l
, so the only way to give resetGraph
kind *
(which is necessary for a function), is to give l
kind *->*
(and t
kind *
).
The second thing you need to know is that types as [Char]
, (Int,Int)
and a -> Real
kan all be written prefix as well: [] Char
, (,) Int Int
, (->) a Real
. You can compare []
to Maybe
: it still needs one argument (here Char
) to be a complete type. Hence, the type []
has kind *->*
. Similarly, (,)
has kind *->*->*
, because it still needs two types to be complete, as does (->)
. (Note: this is documented in section 4.5 of the language report).
Combining these two, you should write:
instance Graph [] Adjacency where
...
Then, the type of resetGraph
is resolved to ([] Adjacency) -> [] Adjacency
which is the same as [Adjacency] -> [Adjacency]
.
For arrays, the prefix notation is {} Adjacency
for {Adjacency}
.
By the way: something similar to this is done in StdEnv
with the class length
:
// StdOverloaded.dcl
class length m :: !(m a) -> Int
// StdList.icl
instance length [] where ...