3

Trying to run a simple script in Haskell by using java code. (Windows 10)

The haskell script looks like this:

import Data.Char(toUpper)
main = interact (map toUpper)

I have made an simple.exe file with ghc, and it works as expected from cmd module. I write a simple string and it replies with the same string in uppercase, and I can repeat this until I chose to stop the program.

But when I try to run this program through java it will not work that way. I can feed the input, but in order to get output I need to close the input feed.

public class Main {
    public static void main(String[] args) {
        try {
            Process process = new ProcessBuilder("simple").start();

            InputStream processInputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(processInputStream));
            OutputStream processOutputStream = process.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(processOutputStream));
            char[] bt = ("Hello").toCharArray();
            writer.write(bt);
            writer.flush();
            writer.newLine();
            processOutputStream.close(); -- Only works if I close output stream,.
            System.out.println(reader.readLine());


        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

How can I get output w/o closing my output-feed in java program.

Restarting the process works, but the execution time is horrible.

I have tried threading the program, with same result.

  • 1
    You could compile the haskell script with eta into a jar and call directly from java https://eta-lang.org – jneira Feb 27 '19 at 15:26

2 Answers2

2

BufferedWriter does not automatically flush its output when you do newLine(). So your Haskell program will not receive the newline and wait forever.

When you call close() all the remaining buffered output is flushed (but of course, you don't want to do that).

So the solution should be

writer.write(bt);
writer.newLine();
writer.flush();

You could also use a PrintWriter, which has an automatic flush-on-newline option and understands Strings (via the print method), so no need to mess with char[].

Finally, if you are planning to send large amounts of text in or out at the same time, you may have to adjust the buffer sizes or use multiple threads to avoid deadlocks when your program cannot send output anymore because the receiving Java-side buffer is full and you are not currently reading it out. See this thread for solutions.

Thilo
  • 257,207
  • 101
  • 511
  • 656
1

I experimented a bit, and I needed to require line buffering on the Haskell side, otherwise the output is not flushed in a timely fashion.

import Data.Char(toUpper)
import System.IO

main = do
  hSetBuffering stdout LineBuffering
  interact (map toUpper)

Indeed, I discovered that stdout is initially set to BlockBuffering when called from java, and that will buffer too much.

Note that, as Thilo pointed out, in order to use LineBuffering on the Haskell side, you have to writer.newLine() on the java side before you flush (in java), otherwise the uppercase output won't get flushed (in Haskell).

chi
  • 111,837
  • 3
  • 133
  • 218