Reputation: 13908
Here's the function:
comboGraph :: [a] -> Int -> [b]
comboGraph _ 0 = []
comboGraph [] _ = []
comboGraph (x:xs) n =
(buildEdges x xs) : comboGraph xs n
where buildEdges h t = (h, comboGraph t (n-1))
Ths function takes in a list of type a
, a number, and returns a list of type b
. As you can see, though, type b
is actually a recursive type -- it will be something along the lines of [(a, [(a, b1)])]
. When I try to compile, I get this error:
• Couldn't match type ‘b’ with ‘(a, [b0])’
‘b’ is a rigid type variable bound by
the type signature for:
comboGraph :: forall a b. [a] -> Int -> [(a, [b])]
at xxx.hs:15:15
Expected type: [(a, [b])]
Actual type: [(a, [(a, [b0])])]
• In the expression: (buildEdges x xs) : comboGraph xs n
In an equation for ‘comboGraph’:
comboGraph (x : xs) n
= (buildEdges x xs) : comboGraph xs n
where
buildEdges h t = (h, comboGraph t (n - 1))
How do I properly annotate this function?
Upvotes: 2
Views: 157
Reputation: 152682
I like the other answer, and I think you should use it. But it makes some reasoning leaps that require some intuition, and it can be hard to get this intuition without doing things the mechanical way a few times. So in this answer, I will show how to start with a failing definition like the one you have, "turn a crank", and mechanically get a solution that does work. The technique below can be applied to any infinite type error.
You have the following clause (paraphrased slightly):
comboGraph (x:xs) n =
(x, comboGraph xs (n-1)) : {- ... -}
Just doing some straightforward type inference reasoning, we can see that comboGraph
takes a list of some type (from the fact that it pattern matches on x:xs
) and a number (from the fact that it subtracts one). Let's pick a concrete (monomorphic! but not yet known) type a
for the list elements and see what we can infer about what it returns.
Well, it clearly returns a list with tuples inside. And the first part of the tuple is just an a
. What about the second part? The second part of the tuple is... whatever type comboGraph
returns. So comboGraph
returns a type t
satisfying the equation:
t = [(a, t)]
The only solution to this equation is [(a, [(a, [(a, [(a, ...)])])])]
. Such infinite types don't exist raw in Haskell. But there is a standard trick to get quite close: use (type-level) recursion by introducing a newtype. We're solving for t
, but Haskell types have to start with an upper-case letter, so we'll name our solution to this equation T
.
newtype T a = T [(a, T a)] deriving Show
Now we don't quite have T a ~ [(a, T a)]
, but we do have an isomorphism: namely, \(T xs) -> xs :: T a -> [(a, T a)]
and T :: [(a, T a)] -> T a
are inverses. So now we can write your comboGraph
definition by exploiting this isomorphism. Let's name the other half of the isomorphism:
unT :: T a -> [(a, T a)]
unT (T xs) = xs
So:
comboGraph (x:xs) n =
T ((x, comboGraph xs (n-1)) : unT (comboGraph xs n))
The base cases have to get wrapped in T
, as well, of course:
comboGraph _ 0 = T []
comboGraph [] _ = T []
Try it in ghci:
> comboGraph "abc" 3
T [('a',T [('b',T [('c',T [])]),('c',T [])]),('b',T [('c',T [])]),('c',T [])]
Upvotes: 4
Reputation: 34378
To make the issue a bit more evident, let's substitute the definition of buildEdges
in the final case of your definition:
comboGraph (x:xs) n =
(x, comboGraph xs (n-1)) : comboGraph xs n
The result of comboGraph
is supposed to be a list, but one whose elements are pairs that also have a comboGraph
result (i.e. a list of the same type) within. As the type error you got says, that doesn't work -- it's as if you wanted a list with two tails. The fix is switching to a different data structure that reflects what you are trying to do:
-- Feel free to substitute better names.
data Combo a = Empty | Node a (Combo a) (Combo a)
deriving (Eq, Ord, Show)
Empty
covers the base cases which used to result in an empty list, while Node
has one appropriately-typed field for each of the things you want to combine in the recursive case. comboGraph
then becomes:
comboGraph :: [a] -> Int -> Combo a
comboGraph _ 0 = Empty
comboGraph [] _ = Empty
comboGraph (x:xs) n = Node x (comboGraph xs (n-1)) (comboGraph xs n)
(Note that Combo
is actually a binary tree with values on the nodes.)
Upvotes: 6