by_type :: Storage a => String -> String -> IO a
This function must, for any Storage type, take any two strings and produce an IO value of that type. (by_type "disk" "xyz" :: Memory must be valid.) It doesn't really have the option of choosing which instance of Storage to use.
In pure Haskell, you would probably have to do something like
type Storage = Disk Handle | Memory String
by_type :: String -> String -> Storage
That way, by_type can return any Storage it wants.
I'm sure there are also ways to do what you want with extensions.