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.