views:

67

answers:

3

Hi all,

I have an ambiguous type variable error on the definition of "trial" below, I am wondering if there is anything that can be done to make this situation work? I want to really just deal with instances and not explicit data types (such as the MO1, MO2 included below).

module Tc102 where

class (Show a, Read a) => MyObj a where
    alpha :: a->String
    beta  :: a->Int

data MO1 = MO1 { a1 :: String, b1 :: Int } deriving (Show,Read)
data MO2 = MO2 { a2 :: String, b2 :: Int } deriving (Show,Read)

instance MyObj MO1 where
    alpha = a1
    beta = b1

instance MyObj MO2 where
    alpha = a2
    beta = b2


a = MO1 "a" 3
b = MO2 "b" 4

test :: MyObj a => a->String
test = alpha


showMe :: (MyObj a)=> a -> String
showMe = show

readMe :: (MyObj a) => String -> a
readMe = read

trial :: MyObj a => a -> String
trial = test . readMe . showMe

thanks in advance all! I fear however i might need to go to a helper function that would convert old ADT to the 'latest versions...

Simon

EDIT To clarify, imagine that I first show to a file, then later reload the object. Then the function i have is more like

trial :: String -> Int
trial s = beta x
  where x = readMe s
+1  A: 

You get the error because the compiler doesn't know what concrete type readMe should return, since all that test requires is that it be an instance of MyObj. It could be MO1, MO2, or something else entirely, and readMe could return any of those.

Assuming you want readMe . showMe to act like id and output the same type given as input, the quick-and-dirty way is to define a function that does just that, and give it a type signature that equates the input and output types:

trial :: MyObj a => a -> String
trial = test . readShowMe
    where readShowMe :: MyObj a => a -> a
          readShowMe = readMe . showMe

Now the compiler can figure out what concrete type to give to test as input.

Paul Kuliniewicz
Ah, don't you just love it when examples go wrong, please check the original post for a clarification! :)Although I still believe your the first part of your comment is still the problem. It's a shame that given the typeclass info and the string format, that the runtime cannot determine the obj is MO1 etc etc....
simonjpascoe
A: 

You can't read compile-time type from run-time file. You should explicitly implement run-time type-varying value.

As example:

data AnyMyObj = forall a . MyObj a => AnyMyObj a

test :: AnyMyObj -> String
test (AnyMyObj x) = alpha x

showMe :: AnyMyObj -> String
showMe (AnyMyObj x) = show x

readMe :: String -> AnyMyObj
readMe s = AnyMyObj (read s :: MO1) -- maybe something more complicated

trial :: AnyMyObj -> String
trial = test . readMe . showMe
ony
With a bit more type-foo, got something that should work. Thanks!
simonjpascoe
A: 

There are fancy tricks to this, and the ultimate answer is indeed to read into some sort of existential. For a fancy exposition of a very powerful/general way to do this, see Oleg's Typed-tagless final lecture notes: http://okmij.org/ftp/tagless-final/course/#type-checking.

But that's serious overkill for an example like yours -- the trickiness generally enters when you have tree structures, and especially when you have tree structures representing lambda terms.

For your purposes, ony's example above is roughly right.

But keep in mind that even though you can create new instances of MyObj, your read function will only be able to read a fixed universe, at least without high-level hackery.

So this is a case where I'd question whether you want a typeclass to begin with, or just more constructors in a single ADT.

data MyObj = MO1 { a1 :: String, b1 :: Int }
           | MO2 { a2 :: String, b2 :: Int } deriving (Show,Read)

... for example.

sclv