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
*****************************************

Reply via email to