Send Beginners mailing list submissions to
        beginners@haskell.org

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
        beginners-requ...@haskell.org

You can reach the person managing the list at
        beginners-ow...@haskell.org

When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."


Today's Topics:

   1.  Organizing cmd line flow (Bryan Vicknair)
   2. Re:  Organizing cmd line flow (Martin Drautzburg)
   3. Re:  How to interact with a process (Martin Drautzburg)
   4. Re:  How to interact with a process (David McBride)
   5. Re:  Organizing cmd line flow (John Wiegley)


----------------------------------------------------------------------

Message: 1
Date: Wed, 30 Jan 2013 08:16:37 -0800
From: Bryan Vicknair <bryanv...@gmail.com>
Subject: [Haskell-beginners] Organizing cmd line flow
To: beginners@haskell.org
Message-ID:
        <CAMEjUsxj6Kxx7CzcKeqBwXQHrox5u=o119loasgudx4usxt...@mail.gmail.com>
Content-Type: text/plain; charset=ISO-8859-1

I have an executable, which gets a file path from the command line, and passes
it to this function::

createDb :: FilePath -> IO ()
createDb fpath = do
    fileExists <- doesFileExist fpath
    if fileExists
       then putStrLn "File already exists"
       else do parDirExists <- parentDirExists fpath
               if parDirExists
                  then do con <- openCon (Config fpath)
                          create con
                          closeCon con
                          putStrLn $ "created db @ " ++ fpath
                       else putStrLn "parent dir doesn't exist"

2 checks: File exists? Parent dir exist?  But already, the code is quite
nested.

How can I refactor this so that a few if expressions that check an IO action
doesn't result in very deep nesting of the code?  Is there a pattern here that
I can use?  I read somewhere about wrapping a common pattern like this into a
Monad such that it will somehow signal to the main function that we can't
proceed, but being an absolute beginner coming from Python, I would need some
help with that.  Perhaps exceptions are what I'm looking for since I am working
with IO, but that is what I would do in Python, so I instinctively assume it's
done differently in Haskell :)

In Python, I might write something like this::

def createDb(fpath):
    if doesFileExists(fpath):
        raise FileExistsError(fpath)
    if not parDirExists(fpath):
        return ParentDirNoExistsError(fpath)
    con = openCon(Config fpath)
    create(con)
    closeCon(con)

Is there any way to get closer to the following?  I think this is much clearer,
but perhaps I'm missing a much larger point in how this should be done in
Haskell::

createDb fpath = do
    checkFileExists fpath
    checkParentDirExists fpath
    con <- openCon (Config fpath)
    create con
    closeCon con



------------------------------

Message: 2
Date: Wed, 30 Jan 2013 23:29:45 +0100
From: Martin Drautzburg <martin.drautzb...@web.de>
Subject: Re: [Haskell-beginners] Organizing cmd line flow
To: beginners@haskell.org
Message-ID: <201301302329.45703.martin.drautzb...@web.de>
Content-Type: Text/Plain;  charset="iso-8859-1"

On Wednesday, 30. January 2013 17:16:37 Bryan Vicknair wrote:
> I have an executable, which gets a file path from the command line, and
> passes it to this function::
> 
> createDb :: FilePath -> IO ()
> createDb fpath = do
>     fileExists <- doesFileExist fpath
>     if fileExists
>        then putStrLn "File already exists"
>        else do parDirExists <- parentDirExists fpath
>                if parDirExists
>                   then do con <- openCon (Config fpath)
>                           create con
>                           closeCon con
>                           putStrLn $ "created db @ " ++ fpath
>                        else putStrLn "parent dir doesn't exist"
> 
> 2 checks: File exists? Parent dir exist?  But already, the code is quite
> nested.
> 
> How can I refactor this so that a few if expressions that check an IO
> action doesn't result in very deep nesting of the code?  

Here is my take on that, but note that I am beginner myself and there is 
certainly a better solution.

What you want to achieve is to check filenames for certain conditions. With 
each check you want to report something if the check fails. You want to run 
the next check only when the previous check succeeded. If the filename is 
aleady out of the question, then there is no point in running other checks. In 
the end you want to get an "okay" or "not okay".

One way or another you will need a running Boolean-like state. Otherwise you 
could not tell if it is still sensible to run yet another check, and it would 
be difficult to get a final result. This state tells you whether there is 
still hope for the filename to be okay or not.

However such a state is not suitable for reporting. You cannot check the 
running state and report something if it is "not okay" (False). After the 
first failure, it will remain False and a reporting function may be tempted to 
report a wrong reason, assuming that a check has failed, where really one of 
the previous checks had failed. You need to distinguish between "False because 
a check failed" and "False because a previous check failed and this check 
wasn't run at all". You need the decision wether or not to report in the same 
place where you decde whether or not to run yet another check. You can do this 
e.g. in a spiced up check function.

Your checks all return IO Bool. So in order to chain things it is fortunate to 
let everything return an IO Bool. 

The reporting function could look like this:

report :: Bool -> String-> IO Bool
report b msg = do
        if b then return True
             else putStrLn msg >> return False

When it is invoked with False it prints the message, which gives you an IO(). 
This is "piped" into "return False" which makes the whole function return "IO 
False". So the state is basically just preserved (just changed from Bool to IO 
Bool)

The checks shall also return an IO Bool. Here is one of them:

doesFileNotExist :: Bool -> FilePath -> String -> IO Bool
doesFileNotExist b fpath msg=
        if b then do
                x <- doesFileExist fpath
                report (not x) msg
             else return False

When it is called with a False as first parameter it assumes a previous check 
had failed and runs no check itself. Otherwise it runs "doesFileExist" and if 
that fails it reports the failure. This function knows whether it returned 
False (actually IO False) because it didn't run any check or whether it did 
run a check and failed.

The other check is similar:

doesParentDirectoryExist :: Bool -> FilePath -> String -> IO Bool
doesParentDirectoryExist b fpath  msg = 
        if b then do
                x <- doesDirectoryExist $ fst $ splitFileName fpath
                report x msg
             else return False

If you glue it all together you get:
createDb :: FilePath -> IO Bool
createDb fpath = do
        a <- doesFileNotExist True fpath "File already exists"
        b <- doesParentDirectoryExist a fpath "Parent directory does not 
exist"
        return b

Not how the result from the first check "a" is passed to the second check.

It does what you want:

*Main> createDb "/tmp/foomatic-rip.log"
File already exists
False
*Main> createDb "/tmp/foomatic-rip.logx"
True
*Main> createDb "/tmps/foomatic-rip.logx"
Parent directory does not exist
False





















-- 
Martin



------------------------------

Message: 3
Date: Wed, 30 Jan 2013 23:32:13 +0100
From: Martin Drautzburg <martin.drautzb...@web.de>
Subject: Re: [Haskell-beginners] How to interact with a process
To: "The Haskell-Beginners Mailing List - Discussion of primarily
        beginner-level topics related to Haskell" <beginners@haskell.org>
Message-ID: <201301302332.13748.martin.drautzb...@web.de>
Content-Type: Text/Plain;  charset="windows-1252"

On Tuesday, 29. January 2013 11:01:30 Miguel Negrao wrote:

> 
> main =        forever readFromConsole
> 
> readFromConsole = do
>       in <- getLine
>       processInput in
> 
> processInput ?start? = startMidi
> processInput ?stop? = stopMidi
> 
> where startMidi and stopMidi are functions.

How to implement these functions is exactly my problem.

-- 
Martin



------------------------------

Message: 4
Date: Wed, 30 Jan 2013 17:51:03 -0500
From: David McBride <toa...@gmail.com>
Subject: Re: [Haskell-beginners] How to interact with a process
To: The Haskell-Beginners Mailing List - Discussion of primarily
        beginner-level topics related to Haskell <beginners@haskell.org>
Message-ID:
        <can+tr43dt7cpanvr7xvlsi6-xq5k_grmoywawjz5ocjobpf...@mail.gmail.com>
Content-Type: text/plain; charset="windows-1252"

Then the answer depends on how you are going about playing midi.

1. Is the api call for this playMidiFile :: FilePath -> IO (), where it
waits until the file finishes before it returns?
You may have to spawn a thread (forkIO) before you play, save it's threadId
in some sort of state and then attempt to kill it from the input thread
when the time comes.

2. Does it play the file and return an id you can reference for later
manipulation?
You can simply keep that reference in some sort of state and when the
command comes to kill it, just use that reference.

3. Are you sending external system commands to a sound system like mplayer
or alsa?
Just send the commands and let the system handle it.

On Wed, Jan 30, 2013 at 5:32 PM, Martin Drautzburg <martin.drautzb...@web.de
> wrote:

> On Tuesday, 29. January 2013 11:01:30 Miguel Negrao wrote:
>
> >
> > main =        forever readFromConsole
> >
> > readFromConsole = do
> >       in <- getLine
> >       processInput in
> >
> > processInput ?start? = startMidi
> > processInput ?stop? = stopMidi
> >
> > where startMidi and stopMidi are functions.
>
> How to implement these functions is exactly my problem.
>
> --
> Martin
>
> _______________________________________________
> Beginners mailing list
> Beginners@haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
<http://www.haskell.org/pipermail/beginners/attachments/20130130/0aab5022/attachment-0001.htm>

------------------------------

Message: 5
Date: Wed, 30 Jan 2013 15:41:22 -0600
From: "John Wiegley" <jo...@fpcomplete.com>
Subject: Re: [Haskell-beginners] Organizing cmd line flow
To: beginners@haskell.org
Message-ID: <m2boc61h8d....@newartisans.com>
Content-Type: text/plain

>>>>> Bryan Vicknair <bryanv...@gmail.com> writes:

> Is there any way to get closer to the following?  I think this is much
> clearer, but perhaps I'm missing a much larger point in how this should be
> done in Haskell::

> createDb fpath = do
>     checkFileExists fpath
>     checkParentDirExists fpath
>     con <- openCon (Config fpath)
>     create con
>     closeCon con

Here is one way:

    {-# LANGUAGE DeriveDataTypeable #-}

    import Control.Applicative
    import Control.Exception
    import Control.Monad
    import Control.Monad.Trans.Error
    import Data.Data
    import Data.Typeable
    import System.Directory

    data ConfirmationFailed = ConfirmationFailed String
                            deriving (Show, Data, Typeable)

    instance Exception ConfirmationFailed

    confirm desc test = do
        result <- test
        unless result $ throwIO (ConfirmationFailed desc)

    main = do
        confirm "/tmp exists" (doesDirectoryExist "/tmp")
        print "hello"
        confirm "/tmpx exists" (doesDirectoryExist "/tmpx")
        print "hello"

You can also use "assert" or "guard" for this, although neither will be as
descriptive.

-- 
John Wiegley
FP Complete                         Haskell tools, training and consulting
http://fpcomplete.com               johnw on #haskell/irc.freenode.net



------------------------------

_______________________________________________
Beginners mailing list
Beginners@haskell.org
http://www.haskell.org/mailman/listinfo/beginners


End of Beginners Digest, Vol 55, Issue 36
*****************************************

Reply via email to