Send Beginners mailing list submissions to
[email protected]
To subscribe or unsubscribe via the World Wide Web, visit
http://www.haskell.org/mailman/listinfo/beginners
or, via email, send a message with subject or body 'help' to
[email protected]
You can reach the person managing the list at
[email protected]
When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."
Today's Topics:
1. Mixing IO and other monads (Brian Victor)
2. Re: Mixing IO and other monads (Daniel Fischer)
3. Re: Mixing IO and other monads (Henk-Jan van Tuyl)
4. Re: Mixing IO and other monads (Brandon S Allbery KF8NH)
5. confusion about parameterized types. (Michael Litchard)
6. Re: confusion about parameterized types. (Michael Litchard)
7. Re: confusion about parameterized types. (Michael Litchard)
8. Re: confusion about parameterized types. (Michael Litchard)
----------------------------------------------------------------------
Message: 1
Date: Sun, 29 Aug 2010 22:56:06 +0000 (UTC)
From: Brian Victor <[email protected]>
Subject: [Haskell-beginners] Mixing IO and other monads
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=us-ascii
I've managed to create the sort of nested case code that all the monad
tutorials warn you against. I haven't found a way to work myself out of
it, though. If I've understood monad transformers conceptually, this
seems like the sort of thing they'd be good for, but I don't know how to
actually make what I need.
Here's the code I have. getDefaultInputDeviceID and
getDefaultOutputDeviceID are from PortMidi, and return IO (Maybe Int).
openInput and openOutput return IO (Either PMStream PMError). At this
point, I'd be happy to get the second half of the function refactored
into monads.
main :: IO ()
main = do
initialize
ideviceId <- getDefaultInputDeviceID
case ideviceId of
Nothing -> putStrLn "No default input device"
Just inputDeviceId -> do
odeviceId <- getDefaultOutputDeviceID
case odeviceId of
Nothing -> putStrLn "No default output device"
Just outputDeviceId -> do
openInputResult <- openInput inputDeviceId
case openInputResult of
Right err -> putStrLn $ show err
Left inputStream -> do
openOutputResult <- openOutput outputDeviceId 0
case openOutputResult of
Right err -> putStrLn $ show err
Left outputStream -> runTranslationLoop
inputStream outputStream
It seems like I ought to be able to change the second half of the
function to something like this:
openStreamsAndLoop :: (Num a) => a -> a -> IO (Maybe err)
openStreamsAndLoop inputDeviceId outputDeviceId = do
inputStream <- openInput inputDeviceId
outputStream <- openOutput outputDeviceId 0
runTranslationLoop inputStream outputStream
return Nothing
But how do I create an IO (Maybe err) monad?
--
Brian
------------------------------
Message: 2
Date: Mon, 30 Aug 2010 02:21:48 +0200
From: Daniel Fischer <[email protected]>
Subject: Re: [Haskell-beginners] Mixing IO and other monads
To: [email protected]
Cc: Brian Victor <[email protected]>
Message-ID: <[email protected]>
Content-Type: text/plain; charset="iso-8859-1"
On Monday 30 August 2010 00:56:06, Brian Victor wrote:
> I've managed to create the sort of nested case code that all the monad
> tutorials warn you against. I haven't found a way to work myself out of
> it, though. If I've understood monad transformers conceptually, this
> seems like the sort of thing they'd be good for, but I don't know how to
> actually make what I need.
>
> Here's the code I have. getDefaultInputDeviceID and
> getDefaultOutputDeviceID are from PortMidi, and return IO (Maybe Int).
> openInput and openOutput return IO (Either PMStream PMError). At this
> point, I'd be happy to get the second half of the function refactored
> into monads.
>
> main :: IO ()
> main = do
> initialize
> ideviceId <- getDefaultInputDeviceID
> case ideviceId of
> Nothing -> putStrLn "No default input device"
> Just inputDeviceId -> do
> odeviceId <- getDefaultOutputDeviceID
> case odeviceId of
> Nothing -> putStrLn "No default output device"
> Just outputDeviceId -> do
> openInputResult <- openInput inputDeviceId
> case openInputResult of
> Right err -> putStrLn $ show err
> Left inputStream -> do
> openOutputResult <- openOutput
> outputDeviceId 0 case openOutputResult of
> Right err -> putStrLn $ show err
> Left outputStream -> runTranslationLoop
> inputStream outputStream
>
>
> It seems like I ought to be able to change the second half of the
> function to something like this:
>
> openStreamsAndLoop :: (Num a) => a -> a -> IO (Maybe err)
> openStreamsAndLoop inputDeviceId outputDeviceId = do
> inputStream <- openInput inputDeviceId
> outputStream <- openOutput outputDeviceId 0
> runTranslationLoop inputStream outputStream
> return Nothing
>
> But how do I create an IO (Maybe err) monad?
Not sure it's so much better, but here you go:
import Control.Monad.Trans
import Control.Monad.Instances
newtype ErrIO a = EIO { unErrIO :: IO (Either String a) }
runErrIO :: ErrIO a -> IO ()
runErrIO eio = unErrIO eio >>= either putStrLn (const $ return ())
instance Functor ErrIO where
fmap f = EIO . fmap (fmap f) . unErrIO
instance Monad ErrIO where
return = EIO . return . Right
eio >>= f = EIO $ do
e <- unErrIO eio
case e of
Left oops -> return $ Left oops
Right val -> unErrIO $ f val
fail = EIO . return . Left
instance MonadIO ErrIO where
liftIO io = EIO $ fmap Right io
tryMaybe :: String -> IO (Maybe a) -> ErrIO a
tryMaybe err act = EIO $ act >>= return . maybe (Left err) Right
reflect :: Show e => IO (Either r e) -> ErrIO r
reflect = EIO . fmap (either Right (Left . show))
main :: IO ()
main = runErrIO $ do
liftIO initialize
inputDeviceId <- tryMaybe "No default input device"
getDefaultInputDeviceID
outputDeviceId <- tryMaybe "No default output device"
getDefaultOutputDeviceID
inputStream <- reflect $ openInput inputDeviceId
outputStream <- reflect $ openOutput outputDeviceId 0
liftIO $ runTranslationLoop inputStream outputStream
------------------------------
Message: 3
Date: Mon, 30 Aug 2010 13:50:31 +0200
From: "Henk-Jan van Tuyl" <[email protected]>
Subject: Re: [Haskell-beginners] Mixing IO and other monads
To: [email protected], "Brian Victor" <[email protected]>
Message-ID: <[email protected]>
Content-Type: text/plain; charset=iso-8859-15; format=flowed;
delsp=yes
On Mon, 30 Aug 2010 00:56:06 +0200, Brian Victor <[email protected]>
wrote:
> main :: IO ()
> main = do
> initialize
> ideviceId <- getDefaultInputDeviceID
> case ideviceId of
> Nothing -> putStrLn "No default input device"
> Just inputDeviceId -> do
> odeviceId <- getDefaultOutputDeviceID
> case odeviceId of
> Nothing -> putStrLn "No default output device"
> Just outputDeviceId -> do
> openInputResult <- openInput inputDeviceId
> case openInputResult of
> Right err -> putStrLn $ show err
> Left inputStream -> do
> openOutputResult <- openOutput
> outputDeviceId 0
> case openOutputResult of
> Right err -> putStrLn $ show err
> Left outputStream -> runTranslationLoop
> inputStream outputStream
>
>
This can be made more readable if main is split up:
> main :: IO ()
> main = do
> initialize
> ideviceId <- getDefaultInputDeviceID
> case ideviceId of
> Nothing -> putStrLn "No default input device"
> Just inputDeviceId -> main2 inputDeviceId
> where
> main2 inputDeviceId = do
> odeviceId <- getDefaultOutputDeviceID
> case odeviceId of
> Nothing -> putStrLn "No default output device"
> Just outputDeviceId -> main3 inputDeviceId outputDeviceId
>
> main3 inputDeviceId outputDeviceId =
> do
> openInputResult <- openInput inputDeviceId
> case openInputResult of
> Right err -> putStrLn $ show err
> Left inputStream -> main4 inputDeviceId outputDeviceId
> inputStream
>
> main4 inputDeviceId outputDeviceId inputStream =
> do
> openOutputResult <- openOutput outputDeviceId 0
> case openOutputResult of
> Right err -> putStrLn $ show err
> Left outputStream -> runTranslationLoop inputStream
> outputStream
This can be made more compact with the aid of the following two functions:
> ifLeft x f =
> x' <- x
> case x' of
> Right err -> putStrLn $ show err
> Left y -> f y
> ifJust x f msg =
> x' <- x
> case x' of
> Nothing -> putStrLn msg
> Just y -> f y
The main program than becomes:
> main :: IO ()
> main = do
> initialize
> ifJust getDefaultInputDeviceID
> main2 "No default input device"
>
> where
> main2 inputDeviceId = ifJust getDefaultOutputDeviceID
> (main3 inputDeviceId) "No default output device"
>
> main3 inputDeviceId outputDeviceId =
> ifLeft (openInput inputDeviceId)
> (main4 inputDeviceId outputDeviceId)
>
> main4 inputDeviceId outputDeviceId inputStream =
> ifLeft (openOutput outputDeviceId 0)
> (runTranslationLoop inputStream)
Of course, another thing to prevent long lines, is to use two spaces for
indentation.
Met vriendelijke groet,
Henk-Jan van Tuyl
--
http://Van.Tuyl.eu/
http://members.chello.nl/hjgtuyl/tourdemonad.html
--
------------------------------
Message: 4
Date: Mon, 30 Aug 2010 11:53:12 -0400
From: Brandon S Allbery KF8NH <[email protected]>
Subject: Re: [Haskell-beginners] Mixing IO and other monads
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=UTF-8
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 8/29/10 18:56 , Brian Victor wrote:
> main :: IO ()
> main = do
> initialize
> ideviceId <- getDefaultInputDeviceID
> case ideviceId of
> Nothing -> putStrLn "No default input device"
> Just inputDeviceId -> do
> odeviceId <- getDefaultOutputDeviceID
> case odeviceId of
> Nothing -> putStrLn "No default output device"
> Just outputDeviceId -> do
> openInputResult <- openInput inputDeviceId
> case openInputResult of
> Right err -> putStrLn $ show err
> Left inputStream -> do
> openOutputResult <- openOutput outputDeviceId 0
> case openOutputResult of
> Right err -> putStrLn $ show err
> Left outputStream -> runTranslationLoop
> inputStream outputStream
>
>
> It seems like I ought to be able to change the second half of the
> function to something like this:
>
> openStreamsAndLoop :: (Num a) => a -> a -> IO (Maybe err)
> openStreamsAndLoop inputDeviceId outputDeviceId = do
> inputStream <- openInput inputDeviceId
> outputStream <- openOutput outputDeviceId 0
> runTranslationLoop inputStream outputStream
> return Nothing
>
> But how do I create an IO (Maybe err) monad?
You don't really need one; use the existing plumbing.
> ioE :: e -> IO (Maybe a) -> IO a
> ioE e a = a >>= \r -> case r of
> Just r' -> r'
> Nothing -> fail e
>
> io :: IO (Either e a) -> IO a
> io a = a >>= \r -> case r of
> Right v -> v
> Left e -> fail $ show e
>
> main = do
> initialize
> iDeviceId <- ioE "No default input device" getDefaultInputDeviceId
> oDeviceId <- ioE "No default output device" getDefaultOutputDeviceId
> inputStream <- io $ openInput inputDeviceId
> outputStream <- io $ openOutput outputDeviceId 0
> runTranslationLoop inputStream outputStream
If you really want a monad transformer, ErrorT is probably the one to look
at (Control.Monad.Error). But it won't be pretty unless you rewrite the
functions getDefault(...) and open(...) to use it as well.
- --
brandon s. allbery [linux,solaris,freebsd,perl] [email protected]
system administrator [openafs,heimdal,too many hats] [email protected]
electrical and computer engineering, carnegie mellon university KF8NH
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.10 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAkx70+cACgkQIn7hlCsL25V+7QCgkBPSUIzfpnRX69utD7o8d4oz
8FkAn3Sn47biInZg7dp56rThwamVmzu9
=tPWe
-----END PGP SIGNATURE-----
------------------------------
Message: 5
Date: Mon, 30 Aug 2010 09:15:03 -0700
From: Michael Litchard <[email protected]>
Subject: [Haskell-beginners] confusion about parameterized types.
To: [email protected]
Message-ID:
<[email protected]>
Content-Type: text/plain; charset=ISO-8859-1
Here is a record I defined
> module PrepData where
> data Value = Cost Int | ID Int
> type Tname = String
> data StudentsOrActivites = Students String | Activities String
> data Table soa v = PopTable
> { tableName :: Tname
> , tableColumns :: [(soa, v)]
> } deriving (Show, Read)
I'm trying to define another record in terms of Table
> data ProcessData =
> ProcessData { flatfile :: String
> , processfunc :: String ->
what I would like processfunc to be is a function that takes a String
and returns a Table. The type variables are tripping me up. Could
someone help me work out the syntax, and the clarifying of my intent?
------------------------------
Message: 6
Date: Mon, 30 Aug 2010 09:55:04 -0700
From: Michael Litchard <[email protected]>
Subject: [Haskell-beginners] Re: confusion about parameterized types.
To: [email protected]
Message-ID:
<[email protected]>
Content-Type: text/plain; charset=ISO-8859-1
Okay, ski (of Freenode fame) helped me with the first problem. Now I
need to figure out how to use specific types.
given
> module Main where
> import System.Environment
> import PrepData
> data ProcessData =
> ProcessData { flatfile :: String
> , processfunc :: [String] -> Table StudentsOrActivities Value
> }
> main = undefined
and
> module PrepData where
> data Value = Cost Int | ID Int
> type Tname = String
> data StudentsOrActivities = Students String | Activities String
> data Table soa v = PopTable
> { tableName :: Tname
> , tableColumns :: [(soa, v)]
> } deriving (Show, Read)
> popStudents :: [String] -> Table Students ID
> popStudents flatFile = undefined
is it clear what I am trying to do in popStudents?
here's the error I get
PrepData.lhs:10:35:
Not in scope: type constructor or class `Students'
PrepData.lhs:10:44: Not in scope: type constructor or class `ID'
------------------------------
Message: 7
Date: Mon, 30 Aug 2010 09:54:30 -0700
From: Michael Litchard <[email protected]>
Subject: [Haskell-beginners] Re: confusion about parameterized types.
To: [email protected]
Message-ID:
<[email protected]>
Content-Type: text/plain; charset=ISO-8859-1
Okay, ski (of Freenode fame) helped me with the first problem. Now I
need to figure out how to use specific types.
given
> module Main where
> import System.Environment
> import PrepData
> data ProcessData =
> ProcessData { flatfile :: String
> , processfunc :: [String] -> Table StudentsOrActivities Value
> }
> main = undefined
and
> module PrepData where
> data Value = Cost Int | ID Int
> type Tname = String
> data StudentsOrActivities = Students String | Activities String
> data Table soa v = PopTable
> { tableName :: Tname
> , tableColumns :: [(soa, v)]
> } deriving (Show, Read)
> popStudents :: [String] -> Table Students ID
> popStudents flatFile = undefined
is it clear what I am trying to do in popStudents?
here's the error I get
PrepData.lhs:10:35:
Not in scope: type constructor or class `Students'
PrepData.lhs:10:44: Not in scope: type constructor or class `ID'
------------------------------
Message: 8
Date: Mon, 30 Aug 2010 09:54:30 -0700
From: Michael Litchard <[email protected]>
Subject: [Haskell-beginners] Re: confusion about parameterized types.
To: [email protected]
Message-ID:
<[email protected]>
Content-Type: text/plain; charset=ISO-8859-1
Okay, ski (of Freenode fame) helped me with the first problem. Now I
need to figure out how to use specific types.
given
> module Main where
> import System.Environment
> import PrepData
> data ProcessData =
> ProcessData { flatfile :: String
> , processfunc :: [String] -> Table StudentsOrActivities Value
> }
> main = undefined
and
> module PrepData where
> data Value = Cost Int | ID Int
> type Tname = String
> data StudentsOrActivities = Students String | Activities String
> data Table soa v = PopTable
> { tableName :: Tname
> , tableColumns :: [(soa, v)]
> } deriving (Show, Read)
> popStudents :: [String] -> Table Students ID
> popStudents flatFile = undefined
is it clear what I am trying to do in popStudents?
here's the error I get
PrepData.lhs:10:35:
Not in scope: type constructor or class `Students'
PrepData.lhs:10:44: Not in scope: type constructor or class `ID'
------------------------------
_______________________________________________
Beginners mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/beginners
End of Beginners Digest, Vol 26, Issue 57
*****************************************