
Sorry it took so long, but I have to thank everyone who responded. You all gave me some good ideas. Brandon's approach most closely resembled what I'm going with for the moment. I did have to make a few changes, which are shown below for archival purposes. The changes are: 1) Made the first parameter to ioE a String since fail requires it. 2) Switched left/right in io since that's how PortMidi does it. 3) Added (Show e) constraint to io 4) Added return to the Just and Left branches of the case. ioE :: String -> IO (Maybe a) -> IO a ioE e a = a >>= \r -> case r of Just r' -> return r' Nothing -> fail e io :: (Show e) => IO (Either a e) -> IO a io a = a >>= \r -> case r of Left v -> return v Right e -> fail $ show e main :: IO () main = do initialize inputDeviceId <- ioE "No default input device" getDefaultInputDeviceID outputDeviceId <- ioE "No default output device" getDefaultOutputDeviceID inputStream <- io $ openInput inputDeviceId outputStream <- io $ openOutput outputDeviceId 0 putStrLn "Starting translation" runTranslationLoop inputStream outputStream Thanks again! -- Brian