simplessh library hangs when executing a command - haskell

I'm trying to use simplessh library to run a sequence of commands over SSH (actually, to learn some Haskell). It seems to be going OK, but it hangs on the second runSimpleSSH in this code:
module Main where
import Network.SSH.Client.SimpleSSH
main :: IO ()
main = do
s <- runSimpleSSH $ openSession' "localhost" 22 Nothing
case s of
Left e -> print e
Right s -> do
r <- runSimpleSSH $ execCommand s "ls" -- hangs here
case r of
Left e -> print e
Right (Result { resultOut=resultOut, resultErr=resultErr, resultExit=resultExit }) -> print resultExit
Is there something wrong with my Haskell code, or am I using the library wrong? I'm sure someone can tell based on the types exposed by the library.
PS: I'm actually using a patched version of the library which allows for no known_hosts, but the rest seems unchanged.

Never used this library but looking at the documentation for openSession:
Open a SSH session. The next step is to authenticate.
Also from execCommand description:
One should be authenticated before sending commands on a Session.
I think your code breaks the convention by calling execCommand before successfully authenticating. Not sure this is the problem but looks like you need to fix that anyway.

Related

Xmonad: Prompt with output of shell commands as completion options

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.

Basic error in Paskell

I've just started to try and learn Haskell, and I have encountered a problem. I am using windows CMD and I have created a program.hs .txt file for my first program, which includes a simple a + b function. I have written answer = a + b in the txt file, and then I opened the GHCI in CMD. I loaded my program successfully, but when I typed answer 1 5 instead of returning 6 it returns
Variable not in scope: answer :: Integer -> Integer -> t
Please can you help and explain it to me. Thanks.
It seems that you did not load the file correctly. Maybe you loaded another file? It's hard to tell.
"Not in scope" in GHCi means that the module you loaded (if any) does not export that variable.
Further, your Haskell code is wrong. answer = a + b assumes that variables a and b are already defined when you load the module, e.g. they have been defined there. This will trigger a "not in scope" error for those variables.
If you intend answer to be a function, then you need to define it as a function, e.g.
answer a b = a + b
-- ^^^ --
Note the additional arguments.

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;

Running an Action if part of a file changes

What is the recommended way of running some Action if part of a file changes?
My use-case is given a file that I know exists (concretely elm-package.json), run a shell command (elm package install --yes) if part of the file changes (the dependencies field).
It seems that the Oracle abstraction exposes comparing a value to the last (via Eq). So I tried a newtype like:
newtype ElmDependencies = ElmDependencies () deriving ...
type instance RuleResult ElmDependencies = String
But now, I get stuck actually using this function of type ElmDependencies -> Action String, since the rule I want to write doesn't actually care what the returned String is, it simply wants to be called if the String changes.
In other words,
action $ do
_ <- askOracle (ElmDependencies ())
cmd_ "elm package install --yes"
at the top-level doesn't work; it will run the action every time.
Your askOracle approach is pretty close, but Shake needs to be able to
identify the "output" of the action, so it can give it a persistent name
between runs, so other steps can depend on it, and use that persistent name to avoid recomputing. One way to do that is to make the action create a stamp file, e.g.:
"packages.stamp" *> \out -> do
_ <- askOracle $ ElmDependencies ()
cmd_ "elm package install --yes"
writeFile' out ""
want ["packages.stamp"]
Separately, an alternative to using Oracle is to have a file
elm-package-dependencies.json which you generate from
elm-package.json, write using writeFileIfChanged (which gives you Eq for files), and depend on that
file in packages.stamp. That way you get Eq on files, and can also
easily debug it or delete the -dependencies.json file to force a rerun.

Compiling / testing Haskell without a main function

I'm working on an assignment in Haskell. However, the base code I am working with does not have a main function defined, and from the wording of the assignment I believe I am not expected to have to write any code outside of the solution to the problem. However, when I try to compile my code, I receive the error:
The IO function 'main' is not defined in module 'Main'
I assume this is because the function does not have a main function. However, when I try to write my own main function:
main :: IO ()
main = solve easy // easy is an array
I get the error:
Couldn't match expected type 'IO()' with actual type '[Int]'
The solve function's type is declared as follows:
solve :: [Int] -> [Int]
So it takes an array and returns an array. What am I doing wrong in writing my main function? Even when I try changing the declaration of main to things like:
main :: [Int]
or
main :: IO [Int]
I still can't get it to compile.
Without writing a proper main with a correct type, as described by #G Philip, you can load your file in ghci by writing ghci file.hs in your terminal, or by invoking :l file.hs inside ghci.
Firstly: the function main must have type IO t for some type t. When the program is executed, the computation defined by main is executed, and its result (of type t) is thrown away; see here.
So, in particular, you cannot have the type of main as [Int] and have the compiler not complain.
Depending on whether you want to see the results of solving the easy case or not, you can try one of the following:
If you want to see the results: print them!
main :: IO ()
main = putStrLn $ show (solve easy)
If you are not interested in seeing the results, throw them away:
main :: IO ()
main = let solution = solve easy
in putStrLn ""
Edit: Note, however, that if you do the latter, then (as #yatima2975 mentions in a comment) the "solve easy" part will not be evaluated.
Just to add to the other answers:
you don't need to write a main function and can still compile the file into a lib and the easiest way to do this is just giving a module name (that is not Main):
module MyCode where
solve :: ...
But of course compiling it might not make any sense anymore (as you will not be able to run it - and of course even if you have not specified what to output anyway).
So in this case rather load the file into ghci:
ghci MyFile.hs
and then everytime you changed something in your code you can do :r inside ghci to reload it.
Or even better set up your favorite editor (emacs and vi are quite easy but sublime text and some other works great too) to give you integrated ghci - this explains what you need to do to setup emacs with haskell-mode if you are interested.
Consider
main :: IO ()
main = do
let res = solve easy // easy is an array
return ()
where return () yields a result of type Unit which conveys with the type signature of main. Note solve easy is bound to res which is not used further.

Resources