Ambiguous types for collection keys

Hello everyone, I've been banging my head against my desk a bit so I figured it's time to ask for help :-) I'm writing an application that persists data to disk. The hard stuff is pretty much done (binary serialisation, etc...) The big stumbling block is that I want users to be able to define their own schemas. This means that the keys for a data store may be Integer, Strings, etc... I have a BTreeIndex type: data BTreeIndex a b = Branch { parentBT :: !BlockPtr, keysBT :: ![a], kidsBT :: ![BlockPtr] } | Leaf { parentBT :: !BlockPtr, valsBT :: !(LeafMap a b), prevBT :: !PrevLeaf, nextBT :: !NextLeaf } type IdxPS = BTreeIndex PackedString BlockPtr type IdxInt = BTreeIndex Integer BlockPtr When a user queries I have to read the input from IO and then somehow cast the key/index type without angering the type checker. If I omit the following function I get the ominous "Ambiguous type variable a..." error. If I use it I get a complaint that the checker was expecting an Integer rather than a PackedString. coerceIndex f (Schema _ SInt SPtr _) (r,hdl,o,hdr) = f (r::IdxInt,hdl,o,hdr) coerceIndex f (Schema _ SStr SPtr _) (r,hdl,o,hdr) = f (r::IdxPS,hdl,o,hdr) Where f is a curried select function (e.g. selectAll, selectByKey, etc...) waiting for the final tuple containing the index and other miscellanea that is needed to read the data. I'm hopelessly lost and I assume that the many brilliant minds on this list will chuckle, pat me on the head and tell me what I've been doing wrong. Thanks for your time guys, Scott

On 4/12/06, Scott Weeks
Hello everyone,
I've been banging my head against my desk a bit so I figured it's time to ask for help :-)
When a user queries I have to read the input from IO and then somehow cast the key/index type without angering the type checker. If I omit the following function I get the ominous "Ambiguous type variable a..." error. If I use it I get a complaint that the checker was expecting an Integer rather than a PackedString.
Well, if you get an "ambiguous type variable" error, you probably (I think) need to add some type annotations. For example: class Foo a where foo :: a bar :: a -> String Evaluating bar foo will result in an error, but bar (foo :: Integer) will work just fine.

Well, if you get an "ambiguous type variable" error, you probably (I think) need to add some type annotations. For example:
class Foo a where foo :: a bar :: a -> String
Evaluating bar foo will result in an error, but bar (foo :: Integer) will work just fine. ____________________
The problem is that I get an incoming value which is a key of some sort. There's no way of knowing what type that value is supposed to be until I compare it with the schema from the above example. where I _am_ adding type annotations. coerceIndex f (Schema _ SInt SPtr _) (r,hdl,o,hdr) = f (r::IdxInt,hdl,o,hdr) coerceIndex f (Schema _ SStr SPtr _) (r,hdl,o,hdr) = f (r::IdxPS,hdl,o,hdr) When I try to add type annotations I get a complaint from the typechecker that says (In the case of the above example) Expected type: Integer, Inferred Type: PackedString. Is the alternative to write different "select" methods for each key type (selectInt, selectPS, ...)? God I hope not, that would be a bit scary.

On Apr 12, 2006, at 3:18 PM, Scott Weeks wrote:
Well, if you get an "ambiguous type variable" error, you probably (I think) need to add some type annotations. For example:
class Foo a where foo :: a bar :: a -> String
Evaluating bar foo will result in an error, but bar (foo :: Integer) will work just fine. ____________________
The problem is that I get an incoming value which is a key of some sort. There's no way of knowing what type that value is supposed to be until I compare it with the schema from the above example. where I _am_ adding type annotations.
coerceIndex f (Schema _ SInt SPtr _) (r,hdl,o,hdr) = f (r::IdxInt,hdl,o,hdr) coerceIndex f (Schema _ SStr SPtr _) (r,hdl,o,hdr) = f (r::IdxPS,hdl,o,hdr)
When I try to add type annotations I get a complaint from the typechecker that says (In the case of the above example) Expected type: Integer, Inferred Type: PackedString.
Is the alternative to write different "select" methods for each key type (selectInt, selectPS, ...)? God I hope not, that would be a bit scary.
I'm not 100% sure I understand your use case, but I think you might be able to crack this by using Data.Dynamic: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data- Dynamic.html Speak softly and drive a Sherman tank. Laugh hard; it's a long way to the bank. -- TMBG

Robert Dockins wrote:
On Apr 12, 2006, at 3:18 PM, Scott Weeks wrote:
Well, if you get an "ambiguous type variable" error, you probably (I think) need to add some type annotations. For example:
class Foo a where foo :: a bar :: a -> String
Evaluating bar foo will result in an error, but bar (foo :: Integer) will work just fine. ____________________
The problem is that I get an incoming value which is a key of some sort. There's no way of knowing what type that value is supposed to be until I compare it with the schema from the above example. where I _am_ adding type annotations.
coerceIndex f (Schema _ SInt SPtr _) (r,hdl,o,hdr) = f (r::IdxInt,hdl,o,hdr) coerceIndex f (Schema _ SStr SPtr _) (r,hdl,o,hdr) = f (r::IdxPS,hdl,o,hdr)
When I try to add type annotations I get a complaint from the typechecker that says (In the case of the above example) Expected type: Integer, Inferred Type: PackedString.
Is the alternative to write different "select" methods for each key type (selectInt, selectPS, ...)? God I hope not, that would be a bit scary.
I'm not 100% sure I understand your use case, but I think you might be able to crack this by using Data.Dynamic:
http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data- Dynamic.html
Or carry an instance in along with a type parameter, using existentials or GADT. Brandon Moore

Or carry an instance in along with a type parameter, using existentials or GADT.
Brandon Moore
Do you know of an example that would apply to my situation? I think I neglected to state part of my problem. I am storing the root nodes of btree indexes in a heterogeneous list using Typeable. When they come out of the list they need to be unwrapped I think this will clarify my situation because I've been doing a poor job of explaining: import Data.Dynamic data Foo a = FVal a deriving (Show, Typeable) type FooStr = Foo String type FooInt = Foo Integer main = do fooType <- getLine fooVal <- getLine let foo = toDyn (FVal fooVal) fs = [foo] (f:_) = fs Just dynFoo = fromDynamic f dostuff dynFoo dostuff :: (Show a) => Foo a -> IO () dostuff (FVal x) = print x This fails with: Ambiguous type variable `a' in the constraints: `Typeable a' arising from use of `fromDynamic' at /Users/weeksie/workspace/haskell/Main.hs:243:20-30 `Show a' arising from use of `dostuff' at /Users/weeksie/workspace/haskell/Main.hs:247:2-8 Probable fix: add a type signature that fixes these type variable(s) However, changing main: main = do fooType <- getLine fooVal <- getLine let foo = toDyn (FVal fooVal) fs = [foo] (f:_) = fs Just dynFoo = fromDynamic f if fooType == "str" then dostuff (dynFoo::FooStr) else dostuff (dynFoo::FooInt) Fails with: Couldn't match `Integer' against `String' Expected type: FooInt Inferred type: FooStr In the expression: dynFoo :: FooInt In the first argument of `dostuff', namely `(dynFoo :: FooInt)' I'm probably going about this in the wrong way. I'd love some advice on how to either do it better or weave some Type Magic to achieve the same effect.

On Apr 12, 2006, at 4:09 PM, Scott Weeks wrote:
Or carry an instance in along with a type parameter, using existentials or GADT.
Brandon Moore
Do you know of an example that would apply to my situation?
I think I neglected to state part of my problem. I am storing the root nodes of btree indexes in a heterogeneous list using Typeable. When they come out of the list they need to be unwrapped
I think this will clarify my situation because I've been doing a poor job of explaining:
import Data.Dynamic
data Foo a = FVal a deriving (Show, Typeable)
type FooStr = Foo String type FooInt = Foo Integer
main = do fooType <- getLine fooVal <- getLine let foo = toDyn (FVal fooVal) fs = [foo] (f:_) = fs Just dynFoo = fromDynamic f dostuff dynFoo
dostuff :: (Show a) => Foo a -> IO () dostuff (FVal x) = print x
This fails with:
Ambiguous type variable `a' in the constraints: `Typeable a' arising from use of `fromDynamic' at /Users/weeksie/workspace/ haskell/Main.hs:243:20-30 `Show a' arising from use of `dostuff' at /Users/weeksie/workspace/haskell/ Main.hs:247:2-8 Probable fix: add a type signature that fixes these type variable(s)
However, changing main:
main = do fooType <- getLine fooVal <- getLine let foo = toDyn (FVal fooVal) fs = [foo] (f:_) = fs Just dynFoo = fromDynamic f if fooType == "str" then dostuff (dynFoo::FooStr) else dostuff (dynFoo::FooInt)
You are trying to assign two distinct types to dynFoo; that's a no- no. You need to move the usage of the polymorphic function out of the let so that the use at each distinct type doesn't get unified. {-# OPTIONS -fglasgow-exts #-} import Data.Dynamic data Foo a = FVal a deriving (Show, Typeable) type FooStr = Foo String type FooInt = Foo Integer main = do fooType <- getLine fooVal <- getLine let foo = toDyn (FVal fooVal) fs = [foo] (f:_) = fs if fooType == "str" then dostuff (fromDyn f undefined :: Foo String) else dostuff (fromDyn f undefined :: Foo Int) dostuff :: (Show a) => Foo a -> IO () dostuff (FVal x) = print x BTW, this little example always stuffs a string into the FVal, so trying to get anything else out will fail. Rob Dockins Speak softly and drive a Sherman tank. Laugh hard; it's a long way to the bank. -- TMBG

You are trying to assign two distinct types to dynFoo; that's a no-no. You need to move the usage of the polymorphic function out of the let so that the use at each distinct type doesn't get unified.
{-# OPTIONS -fglasgow-exts #-} import Data.Dynamic
data Foo a = FVal a deriving (Show, Typeable)
type FooStr = Foo String type FooInt = Foo Integer
main = do fooType <- getLine fooVal <- getLine let foo = toDyn (FVal fooVal) fs = [foo] (f:_) = fs if fooType == "str" then dostuff (fromDyn f undefined :: Foo String) else dostuff (fromDyn f undefined :: Foo Int)
dostuff :: (Show a) => Foo a -> IO () dostuff (FVal x) = print x
BTW, this little example always stuffs a string into the FVal, so trying to get anything else out will fail.
Thanks for the example it makes a bit of sense now, I really appreciate you taking the time to help me on this. Cheers, Scott
participants (4)
-
Brandon Moore
-
ihope
-
Robert Dockins
-
Scott Weeks