Working with process always felt clumsy to me. While sometimes, there are simple primitives that fit my purpose, oftentimes I end up using createProcess and dealing with all the complexity myself.
A recent example is that I started out with readProcess but at the point where I wanted to use arbitrary shell commands I again had to switch to createProcess.
One thing that I think would increase composability would be to work with CmdSpec instead of either String or (FilePath, [String]) in most places. Again, taking readProcess as an example, that would mean to change the type to
readProcess :: CmdSpec -> String -> IO String
which then can be used to run either raw commands
readProcess (RawCommand "echo" ["foo"]) ""
or shell commands
readProcess (ShellCommand "echo foo") ""
In addition, we could also allow to use string literals as shell commands by adding an IsString instance for CmdSpec:
readProcess "echo foo" ""
(If you want to get fancy + are not afraid of partial functions, you can do something similar for raw commands and OverloadedLists.)
Working with process always felt clumsy to me. While sometimes, there are simple primitives that fit my purpose, oftentimes I end up using
createProcessand dealing with all the complexity myself.A recent example is that I started out with
readProcessbut at the point where I wanted to use arbitrary shell commands I again had to switch tocreateProcess.One thing that I think would increase composability would be to work with
CmdSpecinstead of eitherStringor(FilePath, [String])in most places. Again, takingreadProcessas an example, that would mean to change the type towhich then can be used to run either raw commands
or shell commands
In addition, we could also allow to use string literals as shell commands by adding an
IsStringinstance forCmdSpec:(If you want to get fancy + are not afraid of partial functions, you can do something similar for raw commands and
OverloadedLists.)