views:

56

answers:

1
+2  A: 

First of all, I would strongly recommend that you just provide all the necessary Observable instances. You can use DrIFT, which supports Observable directly, or Derive to automatically derive the instances so you don't need to write them yourself.

The alternatives are less savory.

unshow :: Unshow a => String -> a

This is provided by the Read class. The single operation is read, which has exactly the type you give. The functions read and show should be inverses such that read . show is equivalent to id. Most standard data types provide Read, although third-party libraries may not.

This is going to be much less efficient than an Observable instance because you'll be deserializing all your data every time you observe it. If you have data of any significant size this will be a big hit.

The other alternative is to create an Observable instance for every type.

instance Show a => Observable a where
    observer my = send (show my) (return my)

Again, this does not mean "When 'a' is a member of Show, use this Observable instance." It means "Use this Observable instance for everything, and it's an error if there isn't a Show instance in scope". This requires a lot of somewhat sketchy extensions and leads to problems with overlapping instances.

If you care enough to use Hood, I don't see why you would want to have this instance.

Edit:

It appears that type conversion has a higher precedence than function application: putStrLn . show $ read "Foo"::MyData -- error putStrLn . show $ (read "Foo"::MyData)

This is a fairly common problem. Consider the function:

f1 :: String -> String
f1 = show . read

GHC won't allow this. read :: Read a => String -> a and show :: Show a => a -> String, but when you combine the two, GHC can't figure out what type to instantiate the a type variable at. In other words, you have a string, you're deserializing it then re-serializing it, but what type are you deserializing to? You need to tell GHC by using an explicit type signature for either read or show, which is what your second example does. This isn't type conversion, it's that you've written code the type inferencer can't figure out without help.

John
I appears that type conversion has a higher precedence than function application:putStrLn . show $ read "Foo"::MyData -- errorputStrLn . show $ (read "Foo"::MyData)
lambdor
It's easier sometimes to use "read . observe . show" than to instance classes, especially when performance isn't an issue.
lambdor
But wouldn't "instance Show a => Observable a" only limit the use of observe for Show instances within the module scope?
lambdor
This instance means you will only be able to *use* observe for types with Show instances in scope, but the type checker will still consider it a valid instance for all types. Class contexts don't have an effect on type checking or inferencing until the very end of the process.
John