Recently I was working on a simple Clojure program where I need to read input from STDIN. I hadn't actually done this before, so I searched online and found others had similar questions, but had to cobble an answer together due to two issues:
In most languages you use a while loop, check for some condition (or input) to be false in order to stop. Clojure actually has a while loop construct, but making it work seems tricky and likely involves mutable state. Paying the overhead of STM (Software Transactional Memory) in order to do a simple input loop, seems like excessive overkill.
When you try to read from STDIN using
lein run
, it doesn't work. The input is ignored.
So, here's what I have working that is reasonably idiomatic and concise.
In my code example, I want to read a user's input until they type in a sentinel value, which I chose to be :done
. Here I just echo the output back:
The trick to getting it work with with Leiningen (I'm using lein-2.0.0-preview10) is to use lein trampoline run
, rather than lein run
. The trampoline feature allows you to run your app in a separate JVM process rather than within the Leiningen process (as a child process). Running it as a child process somehow blocks STDIN input from being captured.
Example usage:
$ lein trampoline run -m example.stdin
Enter text:
First line
You entered: >>First line<<
Second line
You entered: >>Second line<<
Foo
You entered: >>Foo<<
:done
End
In all likelihood, you would want to capture and retain the input lines in a collection. To do that use an accumulator in your loop:
Ideas on more idiomatic ways to do this in Clojure are welcome.
Instead of recursing explicitly, you can use a lazy seq like this: https://bpaste.net/show/641408eb2b42
ReplyDelete