
Dear list members, as an exercise, I tried to define a type class for structures with ordered key values. The key values could be numbers, but also strings. For instance, I intend to define an address list containing records with names as key values. So far, my type class definition contains a function "key", which should extract the key values from the structures, and a function which compares the key values. So I tried: class StructsWithOrderedKeys a where -- no default definition for key key :: (Ord b) => a -> b () :: a -> a -> Bool x y = (key x) < (key y) Here I get the following error message from GHCI: "Ambiguous type variable 'b' in the constraint: 'Ord b' arising from a use of 'key' ... Probable fix: add a type signature that fixes these type variable(s)" Could anybody explain what ambiguity arises here? As the arguments of () are of the same type, I expected also the results (key x) and (key y) to be of the same type, which should be by the type constraint for "key" an instance of Ord. Why I am not allowed to use "key" in the definition of () ? I would be grateful for any hint! Johannes

On Sat, Sep 24, 2011 at 12:21 PM, Johannes Engels
Dear list members,
as an exercise, I tried to define a type class for structures with ordered key values. The key values could be numbers, but also strings. For instance, I intend to define an address list containing records with names as key values. So far, my type class definition contains a function "key", which should extract the key values from the structures, and a function which compares the key values. So I tried:
class StructsWithOrderedKeys a where -- no default definition for key key :: (Ord b) => a -> b
() :: a -> a -> Bool x y = (key x) < (key y)
Here I get the following error message from GHCI:
"Ambiguous type variable 'b' in the constraint: 'Ord b' arising from a use of 'key' ... Probable fix: add a type signature that fixes these type variable(s)"
Could anybody explain what ambiguity arises here? As the arguments of () are of the same type, I expected also the results (key x) and (key y) to be of the same type, which should be by the type constraint for "key" an instance of Ord. Why I am not allowed to use "key" in the definition of () ?
If a function has type:
key :: (Ord b) => a -> b
You have declared that the type 'b' can be whatever the caller wants (given the Ord constraint). So in the computations:
(key x) > (key y)
You haven't told it *which* 'b' you want to pick. And because the return value is consumed by a polymorphic function, the type inference engine doesn't have anything to go on. Does that make sense? Antoine

On Saturday 24 September 2011, 19:21:17, Johannes Engels wrote:
Dear list members,
as an exercise, I tried to define a type class for structures with ordered key values. The key values could be numbers, but also strings. For instance, I intend to define an address list containing records with names as key values. So far, my type class definition contains a function "key", which should extract the key values from the structures, and a function which compares the key values. So I tried:
class StructsWithOrderedKeys a where -- no default definition for key key :: (Ord b) => a -> b
This does not mean what I think you intend. This type signature means that key can produce any type belonging to Ord, whatever the caller desires. So, I need a String, key can produce it, Bool? too, from the same value, Integer? yes, also that...
() :: a -> a -> Bool x y = (key x) < (key y)
Here I get the following error message from GHCI:
"Ambiguous type variable 'b' in the constraint: 'Ord b' arising from a use of 'key' ... Probable fix: add a type signature that fixes these type variable(s)"
Could anybody explain what ambiguity arises here? As the arguments of () are of the same type, I expected also the results (key x) and (key y) to be of the same type, which should be by the type constraint for "key" an instance of Ord. Why I am not allowed to use "key" in the definition of () ?
Because, as said above, key's type says it can produce values of different types from the same argument, so the compiler can't know which type to pick, should it produce x y = (key x :: Integer) < key y or x y = (key x :: String) < key y or ... So the type at which to do the comparison is ambiguous. What you probably intended is that for every type s which is an instance of StructsWithOrderedKeys, there is a type k, belonging to Ord, such that the function key produces values of type k from values of type s. You can achieve that by using multiparameter type classes (with functional dependencies) or associated types. With associated types, it would be ========== {-# LANGUAGE TypeFamilies, FlexibleContexts #-} module Structs where class (Ord (Key a)) => StructsWithOrderedKeys a where type Key a key ::a -> Key a () :: a -> a -> Bool x y = key x < key y data Pair = P { pkey :: String, pval :: Int } deriving Show instance StructsWithOrderedKeys Pair where type Key Pair = String key = pkey ========== and with multiparameter type classes ========== {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} module Structs where -- The "| a -> b" is the functional dependency saying that a -- uniquely determines b class (Ord b) => StructsWithOrderedKeys a b | a -> b where key :: a -> b () :: a -> a -> Bool x y = key x < key y data Pair = P { pkey :: String, pval :: Int } deriving Show instance StructsWithOrderedKeys Pair String where key = pkey ========= Use what you prefer, some things are easier to express using FunDeps, others using associated types. Generally, FunDeps were there first, but people tend to rather use type families nowadays.
participants (3)
-
Antoine Latter
-
Daniel Fischer
-
Johannes Engels