On Fri, May 10, 2013 at 9:00 AM, Andres Löh <andres@well-typed.com> wrote:
 
> This twist is very simple to deal with if you have real existential types,
> with the relevant part of the interface looking approximately like
>
> init :: exists a. IO (Inotify a)
> addWatch :: Inotify a -> FilePath -> IO (Watch a)
> rmWatch :: Inotify a -> Watch a -> IO ()

You can still do the ST-like encoding (after all, the ST typing trick
is just an encoding of an existential), with init becoming "like
runST":

> init :: (forall a. Inotify a -> IO b) -> IO b
> addWatch :: Inotify a -> FilePath -> IO (Watch a)
> rmWatch :: Inotify a -> Watch a -> IO ()

Right, but my interface the Inotify descriptor has an indefinite extent,  whereas your interface enforces a dynamic extent.   I'm not sure to what degree this would impact use cases of this particular library,  but in general moving a client program from the the first interface to the second can require significant changes to the structure of the program,   whereas moving a client program from the second interface to the first is trivial.    So I'd say my interface is more expressive.

Best,
Leon