Xmonad: Prompt with output of shell commands as completion options - haskell

When I try to put the output of a shell command into an xmonad prompt as a completion option, i.e. as something that can be chosen via the prompt, I keep running compile errors, no matter what I try.
After initially getting a basic custom prompt to work, the following sanity check:
mkXPrompt TestPrompt config (mkComplFunFromList config ["a", compltest) testFun
compltest = do
output <- "b"
return output
This may not be terribly idiomatic Haskell, but it works as expected, it compiles and "a" as well as "b" are available options in the prompt. But I just can't get compltest to return the output of a shell command.
I have extensively looked at the source of all instances of xmonad prompt I could find in xmonad.contrib, and I have also checked here and on other websites for questions regarding similar issues, found a few and read them thoroughly.
The problem is that in all these cases, people are either doing something FAR more complex than what I'm attempting to do, or something that is simply very different. If I was better at Haskell I could probably adapt something for my needs, but so far I have spend several hours cobbling together functions and going from one compile error (usually type errors) to the next, no matter what I tried.
What I could so far gather is that I cannot "extract" a string out of an IO String for security reasons, and so should use liftIO in some way. I have also understood that some of the magic should happen in a do block, and that for Xmonad, runProcessWithInput is supposed to work somewhat better than readProcess. But practically applying this knowledge is a different matter.
Here is a tiny subset of the mass of functions I have tried so far, using the command "date" as an example (i.e. the output of the date command at the time of invoking the prompt should be a completion option in the prompt):
compltest = do
output <- liftIO $ putStrLn $ runProcessWithInput "date" [] ""
return output
compltest = do
output <- liftIO $ runProcessWithInput "date" [] ""
return output
compltest = io $ (runProcessWithInput "date" [] "" >>= readIO)

I guess bind your key to something like this:
do
output <- runProcessWithInput "date" [] ""
mkXPrompt TestPrompt config (mkComplFunFromList config [output]) testFun
Of course there are more interesting things to do with output than making it a singleton list; if f is your favorite String -> [String] function, then replace [output] with (f output) in the last line.

Related

How can I log HSpec test output through two formatters simultaneously?

I run my HSpec tests both locally and in the CI. The default specdoc formatter produces nice, colored stdio output. However, for the CI, I need the results in the XML format so that they can be presented on the web.
I added my XML format to the HSpec config, but that disabled the stdio output altogether. I've tried hacking the formatter so that it ran both formatting commands, but that just resulted in an XML file with mixed text and XML messages (since there's only one configOutputFile option).
A few options at this point are:
To run the tests twice, once with each formatter
To run the tests with silent formatter and then somehow try to run the fomatters on the results.
To hack my formatter output so that e.g. some commands go straight to stdio.
Neither of those sound particularly easy and straightforward. Is there a better way? Being able to use just one Formatter at once sounds like a rather annoying limitation.
In the end I've decided that it's not worth the effort, but I did make a working PoC of the workaround:
hspecCi :: String -> Spec -> IO ()
hspecCi filename spec = do
isCiBuild <- (== "true") <$> getEnv "CI" `catch` \(e :: SomeException) -> return ""
let ciConfig = defaultConfig
{ configFormatter = Just xmlFormatter
, configOutputFile = Right $ testResultsPath ++ filename ++ "/results.xml"
}
hspecWith (if isCiBuild then ciConfig else defaultConfig) spec
This will run stdio output in local builds and XML in CI. Not too hard to write, but maybe it'll help someone. xmlFormatter is something you need to get from somewhere or write yourself.

Debugging haskell in VS Code

I am trying to debug a simple Haskell application in VS Code with the phoityne-vscode plugin.
I managed to configure the plugin and run the application - I can see the breakpoints being hit.
The problem is cannot figure out how to interact with the running application. I have a line where I expect user input
do
someValue <- getLine
Once the debugger reaches this line it stops and I cannot figure out how to pass arguments to the program. I would guess it is somewhere in the Debug Console but it looks like the prompt is only for driving the debugger.
I am sure I am missing something very simple - it's my first attempt at tempering with Haskell and I'm new to VS Code too.
As described in repository - You can't use STD[IN|OUT] methods;
Something like putStrLn will be ignored, but IN methods (getLine for example) just will get stuck;
For functions without STD[IN|OUT] methods You can use F10 - select function and send params (for example [1,2,3] for send list or "str" for send string):
With F5 You can run previous configuration or configuration from launch.json:
mainArgs - params, that You can get with getArgs (cmd params for your programm);
startupFunc - name of the function that will be call first;
startupArgs - params for that first function (for example "startupArgs": "666" will the same as <F10> -> 666 -> <Enter>)
stopOnEntry - boolean param for enable\disable breakpoint at the start of the function;
Also, if I understood correctly, F10 will rewrite startupFunc and startupArgs;
I'm really new in haskell so I'm confused a little bit when I can get value of constant in debug console, sometimes I have:
[DAP][ERROR] error occurred while runStmt.
Variable not in scope: <...>
Also look like where and let blocks are ignored :D
Use watch panel for better understanding when You can use some constant:
If You want to debug input\output methods You can use ghci debug commands (:h - Commands for debugging block);
For example, You have a program:
89| test :: IO ()
90| test = do
91| a <- getLine
92| b <- getLine
93| putStrLn $ a ++ b
Use :break 93 to add breakpoint at the 93'th line;
Then run your program in interpreter: test;
Enter values. Now You will stop at putStrLn $ a ++ b - if You type a or b in console - You'll get values of these constants;
Then :step for evaluate ++ and :step for putStrLn:
I hope it will be helpful for someone;

Aliases in Haskell/GHCI

Is it possible to set aliases in the ghci.conf file?
For example I have alias sbh='cd Desktop/Sandbox/Haskell' in bash.bashrc which lets me quickly jump to the specified folder. Is the same thing possible in ghci by putting an alias in the ghci.conf file?
I already have a few commands in ghci.conf but I would like to have multiple aliases set up to jump to folder locations without having to use :cd home/sandbox/foo/bar all of the time. I cant find anything on google so either its never been considered before or am just missing something very simple.
The :def command can do this:
:def sbh const $ return ":cd Desktop/Sandbox/Haskell"
As you can see it is a little more complicated than just giving a substitution string: It takes a Haskell function of type String -> IO String which the newly defined command applies to its argument string to calculate new commands to run.
Then in GHCI :sbh to invoke.
GHCI macros should give you what you're looking for. See: https://www.haskell.org/ghc/docs/7.6.2/html/users_guide/ghci-commands.html as a reference.
Search for "macros" (or :def, which is the command to define macros). You can put these in the ghci.conf file.
For example (from the same URL indicated above):
Prelude> let mycd d = Directory.setCurrentDirectory d >> return ""
Prelude> :def mycd mycd
Prelude> :mycd ..
I hope this helps.
Possible not exactly what you need, but in case the quick-jumping function suffices try this as a first fix (invoked by :sbh):
:def sbh (\arg -> return ("System.Directory.setCurrentDirectory \"Desktop/Sandbox/Haskell\""))
Your later solution might make use of the arg reference like in:
:def sbh (\arg -> return ("System.Directory.setCurrentDirectory " ++ "\"" ++ args ++ "\""))
Invoke the latter which by :sbh Desktop/Sandbox/Haskell then.

Which Monad do I need?

This is something of an extension to this question:
Dispatching to correct function with command line arguments in Haskell
So, as it turns out, I don't have a good solution yet for dispatching "commands" from the command line to other functions. So, I'd like to extend the approach in the question above. It seems cumbersome to have to manually add functions to the table and apply the appropriate transformation function to each function so that it takes a list of the correct size instead of its normal arguments. Instead, I'd like to build a table where I'll add functions and "tag" them with the number of arguments it needs to take from the command line. The "add" procedure, should then take care of composing with the correct "takesXarguments" procedure and adding it to the table.
I'd like to be able to install "packages" of functions into the table, which makes me think I need to be able to keep track of the state of the table, since it will change when packages get installed. Is the Reader Monad or the State Monad what I'm looking for?
No monad necessary. Your tagging idea is on the right track, but that information is encoded probably in a different way than you expected.
I would start with a definition of a command:
type Command = [String] -> IO ()
Then you can make "command maker" functions:
mkCommand1 :: (String -> IO ()) -> Command
mkCommand2 :: (String -> String -> IO ()) -> Command
...
Which serves as the tag. If you don't like the proliferation of functions, you can also make a "command lambda":
arg :: (String -> Command) -> Command
arg f (x:xs) = f x xs
arg f [] = fail "Wrong number of arguments"
So that you can write commands like:
printHelloName :: Command
printHelloName = arg $ \first -> arg $ \last -> do
putStrLn $ "Hello, Mr(s). " ++ last
putStrLn $ "May I call you " ++ first ++ "?"
Of course mkCommand1 etc. can be easily written in terms of arg, for the best of both worlds.
As for packages, Command sufficiently encapsulates choices between multiple subcommands, but they don't compose. One option here is to change Command to:
type Command = [String] -> Maybe (IO ())
Which allows you to compose multiple Commands into a single one by taking the first action that does not return Nothing. Now your packages are just values of type Command as well. (In general with Haskell we are very interested in these compositions -- rather than packages and lists, think about how you can take two of some object to make a composite object)
To save you from the desire you have surely built up: (1) there is no reasonable way to detect the number of arguments a function takes*, and (2) there is no way to make a type depend on a number, so you won't be able to create a mkCommand which takes as its first argument an Int for the number of arguments.
Hope this helped.
In this case, it turns out that there is, but I recommend against it and think it is a bad habit -- when things get more abstract the technique breaks down. But I'm something of a purist; the more duct-tapey Haskellers might disagree with me.

Haskell: Conditionally execute external process with Maybe FilePath

I am struggling to understand a block of code which is extremely easy in imperative world.
That's what I need to do: given an executable full path, which is a Maybe FilePath type, I need to execute it conditionally.
If the path is a Nothing - print an error, if the path is Just Path - execute it and print message that the file has been executed. Only "Hello, World" can be easier,right?
But in Haskell I dug my self into numerous layers of Maybe's and IO's and got stuck.
Two concrete questions arise from here:
How do I feed a Maybe FilePath into a system or rawSystem? liftM does not work for me here.
What is the correct way of doing this kind of conditional branching?
Thanks.
Simple pattern matching will do the job nicely.
case command of
Just path -> system path >> putStrLn "Done"
Nothing -> putStrLn "None specified"
Or, if you'd rather not pattern-match, use the maybe function:
maybe (putStrLn "None specified") ((>> putStrLn "Done") . system) command
That may occasionally be nicer than matching with a case, but not here, I think. The composition with the printing of the success message is clunky. It fares better if you don't print messages but return the ExitCode in both branches:
maybe (return $ ExitFailure 1) system command
This is exactly what the Traversable type class was made for!
Prelude Data.Traversable System.Cmd> traverse system Nothing
Nothing
Prelude Data.Traversable System.Cmd> traverse system (Just "echo OMG BEES")
OMG BEES
Just ExitSuccess

Resources