Windows10下Java调用Haskell程序,如何无需关闭输入流获取输出
Ah, I see the root of your problem! Let's break this down and fix it step by step.
Why Your Current Setup Isn't Working
Your Haskell script uses the interact function, which is designed to read the entire input stream until it hits EOF (end-of-file), process it all at once, and then write the entire output. That's why you have to close the Java output stream (sending an EOF signal) to get any output—Haskell is waiting for the entire input before it does anything.
When you run simple.exe in CMD, you probably thought you were doing "repeat operations" but were actually restarting the program each time, or sending EOF with Ctrl+Z to trigger the output. It's not truly interactive as you expected.
Step 1: Fix the Haskell Script for Interactive Line-by-Line Handling
To make the program respond to each input line immediately (without waiting for EOF), rewrite it to process lines one at a time. Here are two clean options:
Option 1: Recursive Loop
import Data.Char (toUpper) main = do line <- getLine putStrLn $ map toUpper line main -- Recurse to handle the next line
Option 2: Use forever from Control.Monad
import Data.Char (toUpper) import Control.Monad (forever) main = forever $ do line <- getLine putStrLn $ map toUpper line
Compile this new script to simple.exe—now when you run it in CMD, typing a line and pressing Enter will immediately spit out the uppercase version, and you can keep doing this until you hit Ctrl+C to exit.
Step 2: Adjust Java Code for Asynchronous IO
When Java interacts with external processes, you need to handle input and output on separate threads to avoid deadlocks. If your main thread tries to read output while the Haskell process is waiting for input (or vice versa), both will freeze up.
Here's a revised Java implementation that works interactively without closing the input stream:
import java.io.*; public class Main { public static void main(String[] args) { try { Process process = new ProcessBuilder("simple.exe").start(); // Thread to handle output from the Haskell process (prevents blocking) Thread outputHandler = new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String response; while ((response = reader.readLine()) != null) { System.out.println("Haskell output: " + response); } } catch (IOException e) { e.printStackTrace(); } }); outputHandler.start(); // Main thread handles sending input to Haskell try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()))) { // Send first input writer.write("Hello"); writer.newLine(); writer.flush(); // Critical: pushes data out of Java's buffer to the process // Simulate a delay and send another input Thread.sleep(1000); writer.write("World"); writer.newLine(); writer.flush(); // Keep running or add more inputs as needed // To terminate later: writer.close() or process.destroy() } // Wait for the output thread to finish before exiting outputHandler.join(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }
Key Notes for Java:
- Separate Threads: Always handle the external process's output in a dedicated thread—this prevents your main thread from blocking while waiting for output.
- Flush After Write: Calling
writer.flush()ensures that Java sends the input to the Haskell process immediately, instead of holding it in an internal buffer. - No Early Stream Closure: You no longer need to close the output stream to get output, since the Haskell program now processes each line as it arrives.
With these changes, your Java program can send multiple inputs to the Haskell process and receive outputs in real time, without restarting the process or closing the input stream.
内容的提问来源于stack exchange,提问作者MarwinStockholm

