
I'm finding it hard to write robust code that isn't really ugly. Suppose I want to write
execute :: FilePath -> [String] -> IO (Either ExecuteError ExitCode)
where 'ExecuteError' is a data type representing all the ways 'execute' could fail and that 'execute p args' is supposed to * ensure p exists * get p's permissions * ensure p is readable * ensure p is executable * execute p and return its exit code So 'ExecuteError' would have constructors corresponding to these ways to fail: * could not determine whether p exists * p does not exist * could not get p's permissions * p is not readable * p is not executable * could not execute p for some other reason So if I start to code it, I 'tryJust' 'doesFileExist'. If an exception is thrown, I convert it to Left $AppropriateExecuteError. If not, we know whether p exists. If it doesn't, convert it to Left. Otherwise, 'tryJust' 'getPermissions', etc. It's starting to staircase. I'm needing it to be easier to stop my computation and return specific things when exceptions are thrown or when certain requirements aren't met. Thanks for any help.

What about ErrorT monad transformer? Well, if it's not what you really want, you can define your own one without ugly "Error" class, so it'd be something like execute :: FilePath -> [String] -> MyCoolErrorT ExecuteError IO ExitCode On 14 Apr 2009, at 23:29, brian@lorf.org wrote:
I'm finding it hard to write robust code that isn't really ugly. Suppose I want to write
execute :: FilePath -> [String] -> IO (Either ExecuteError ExitCode)
where 'ExecuteError' is a data type representing all the ways 'execute' could fail and that 'execute p args' is supposed to
* ensure p exists * get p's permissions * ensure p is readable * ensure p is executable * execute p and return its exit code
So 'ExecuteError' would have constructors corresponding to these ways to fail: * could not determine whether p exists * p does not exist * could not get p's permissions * p is not readable * p is not executable * could not execute p for some other reason
So if I start to code it, I 'tryJust' 'doesFileExist'. If an exception is thrown, I convert it to Left $AppropriateExecuteError. If not, we know whether p exists. If it doesn't, convert it to Left. Otherwise, 'tryJust' 'getPermissions', etc. It's starting to staircase.
I'm needing it to be easier to stop my computation and return specific things when exceptions are thrown or when certain requirements aren't met. Thanks for any help. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Tuesday, 14.04.09 at 23:36, Miguel Mitrofanov wrote:
What about ErrorT monad transformer?
I don't see how it helps in my situation. ErrorT doesn't catch exceptions, for example. Suppose I did make something like ErrorT that catches exceptions and turn them into Lefts. Where would (>>=) get my specific error constructor? Maybe I need to make helper functions to use in runErrorT blocks that turn exceptions, Bools, Maybes, etc., into Eithers? Confused.

On Tue, 14 Apr 2009, brian@lorf.org wrote:
On Tuesday, 14.04.09 at 23:36, Miguel Mitrofanov wrote:
What about ErrorT monad transformer?
I don't see how it helps in my situation. ErrorT doesn't catch exceptions, for example. Suppose I did make something like ErrorT that catches exceptions and turn them into Lefts. Where would (>>=) get my specific error constructor?
With explicit-exception you would call Exc.fromEitherT $ try $ execute path options The package has some hidden modules, where I experiment with a special IO type that cannot throw exceptions.

On Tue, 14 Apr 2009, Miguel Mitrofanov wrote:
What about ErrorT monad transformer? Well, if it's not what you really want, you can define your own one without ugly "Error" class, so it'd be something like
execute :: FilePath -> [String] -> MyCoolErrorT ExecuteError IO ExitCode
My MyCoolErrorT is named Control.Monad.Exception.Synchronous.ExceptionalT and can be found in the explicit-exception package. In contrast to ErrorT it puts no restrictions on the exception type.

There's no way to make all your tests atomically, e.g, between the
test that p exists and executing p, some other process could removce
p.
So the right way to do this (like opening a file), is to try executing
it and let the OS tell you if it failed.
On Tue, Apr 14, 2009 at 9:29 PM,
I'm finding it hard to write robust code that isn't really ugly. Suppose I want to write
execute :: FilePath -> [String] -> IO (Either ExecuteError ExitCode)
where 'ExecuteError' is a data type representing all the ways 'execute' could fail and that 'execute p args' is supposed to
* ensure p exists * get p's permissions * ensure p is readable * ensure p is executable * execute p and return its exit code
So 'ExecuteError' would have constructors corresponding to these ways to fail: * could not determine whether p exists * p does not exist * could not get p's permissions * p is not readable * p is not executable * could not execute p for some other reason
So if I start to code it, I 'tryJust' 'doesFileExist'. If an exception is thrown, I convert it to Left $AppropriateExecuteError. If not, we know whether p exists. If it doesn't, convert it to Left. Otherwise, 'tryJust' 'getPermissions', etc. It's starting to staircase.
I'm needing it to be easier to stop my computation and return specific things when exceptions are thrown or when certain requirements aren't met. Thanks for any help. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Tuesday, 14.04.09 at 22:13, Lennart Augustsson wrote:
So the right way to do this (like opening a file), is to try executing it and let the OS tell you if it failed.
I know, but the various functions that create processes don't help me know whether the program actually ran or not. For example,
createProcess (proc "nosuch" []) >>= \(_,_,_,ph) -> waitForProcess ph
returns ExitCode 127. If I use the same code to run 'test', a C program that returns 127, I get the same thing.

Quoth brian@lorf.org,
On Tuesday, 14.04.09 at 22:13, Lennart Augustsson wrote:
So the right way to do this (like opening a file), is to try executing it and let the OS tell you if it failed.
I know, but the various functions that create processes don't help me know whether the program actually ran or not. For example,
createProcess (proc "nosuch" []) >>= \(_,_,_,ph) -> waitForProcess ph
returns ExitCode 127. If I use the same code to run 'test', a C program that returns 127, I get the same thing.
Right, awkward problem. Don't know if it can be solved in a portable way, but you (as author of a createProcess-like function) can use a pipe from the forked process. The code demo I append works, given FFI support for the POSIX functions it uses, but may not be appropriate for use with GHC (I'm using nhc98.) Donn ------------ spawn file cmd env = do (e0, e1) <- pipe -- F_CLOEXEC: close fd on (sucessful) exec. fcntlSetFlag e1 F_CLOEXEC t <- fork (fex e0 e1) close e1 rx <- readFd e0 256 if null rx then return t else ioError (userError rx) where fex e0 e1 = do close e0 catch (execve file cmd env) (\ e -> writeFd e1 (ioeGetErrorString e))

Quoth brian@lorf.org,
execute :: FilePath -> [String] -> IO (Either ExecuteError ExitCode)
where 'ExecuteError' is a data type representing all the ways 'execute' could fail and that 'execute p args' is supposed to
* ensure p exists * get p's permissions * ensure p is readable * ensure p is executable
* read UNIX "magic number" from first 2 bytes of p case number of "#!" -> interpret rest of line, start validation process over on interpreter file valid executable type for OS -> OK _ -> not OK
* execute p and return its exit code
Donn
participants (5)
-
brian@lorf.org
-
Donn Cave
-
Henning Thielemann
-
Lennart Augustsson
-
Miguel Mitrofanov