Я создаю приложение (Windows), используя Haskell для реализации числовых методов решения проблем и Java для графического интерфейса и обработки пользовательского ввода. Для межпроцессного взаимодействия я заставляю их читать и записывать в общий файл JSON. Все это работает, но при попытке выполнить программу, связавшись с Java с cmd и запустив GHCi, я не могу загрузить и запустить файл Haskell после запуска компилятора. Сообщения об ошибках нет, но моя Java-программа часто оказывается в тупике. Есть идеи? Вот мой код:
try {
// Serialize dataMap to JSON and write to file
String jsonString = objectMapper.writeValueAsString(dataMap);
FileWriter fileWriter = new FileWriter("newtonRaphson.json");
fileWriter.write(jsonString);
fileWriter.close();
// Read JSON file
File jsonFile = new File("newtonRaphson.json");
JsonNode rootNode = objectMapper.readTree(jsonFile);
// Extract solution from JSON
Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields();
Map.Entry<String, JsonNode> secondEntry = fields.next();
JsonNode solutionOut = secondEntry.getValue();
String solutionText = solutionOut.asText();
// Display equation on GUI
Text messageTextFxn = new Text(equation);
messageTextFxn.setFont(new Font("System", 15));
fxnPane.getChildren().add(messageTextFxn);
// Display solution on GUI
Text messageTextSol = new Text(solutionText);
messageTextSol.setFont(new Font("System", 15));
solPane.getChildren().add(messageTextSol);
// Launch Haskell REPL and interact with it
String haskellPath = "path\\to\\hsFile";
ProcessBuilder builder = new ProcessBuilder("stack", "repl");
builder.redirectInput(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
Process process = builder.start();
OutputStream outputStream = process.getOutputStream();
PrintWriter writer = new PrintWriter(outputStream);
// Load Haskell module
writer.println(":l " + haskellPath + "\\newtonRaphson");
writer.flush();
System.out.println("Haskell module loaded successfully.");
// Execute Haskell function
writer.println("nr");
writer.flush();
System.out.println("Haskell function executed successfully.");
// Close writer and wait for process to terminate
writer.close();
int exitCode = process.waitFor();
if (exitCode == 0) {
System.out.println("Process completed successfully.");
} else {
System.out.println("Process terminated with an error.");
}
Я пробовал использовать разные способы взаимодействия с терминалом, включая ProcessBuilder и Runtime exec(), но я думаю, что это, вероятно, неправильный подход, просто не знаю, что делать на этом этапе.
@TimRoberts, командная строка Haskell — GHCi, но я получаю к ней доступ, запуская стек и вводя REPL. Я пробовал запустить файл Haskell напрямую с помощью Runtime exec() и, возможно, я делаю что-то неправильно, но у меня пока это не сработало.
Я согласен, реплика предназначена для интерактивного использования человеком. Если вы хотите, чтобы программа взаимодействовала с другой программой, скомпилируйте ее и выберите реальный протокол связи, который они будут использовать.
@amalloy, так ты думаешь, мне следует сосредоточиться на компиляции и запуске программы напрямую через Java? Насколько я знаю, для компиляции файлов Haskell требуется GHC, поэтому мне придется найти способ заставить Java запускать файл Haskell после того, как Java заставит компьютер скомпилировать его с помощью GHC. Я обдумывал такой подход, но, честно говоря, понятия не имею, как мне его реализовать.
Зачем использовать Java для его компиляции? Похоже, что в вашем примере вы не пишете файлы на Haskell на лету, поэтому просто скомпилируйте их один раз, и ваш графический интерфейс будет взаимодействовать с скомпилированной программой.
Также кажется странным использовать INHERIT, когда вы хотите управлять вводом-выводом подпроцесса вручную. Это действительно так? Если я правильно читаю документацию, INHERIT означает, что его входные и выходные данные должны быть такими же, как у родительского процесса.
@amalloy, что вы имеете в виду под написанием файлов Haskell на лету? Идея состоит в том, чтобы в конечном итоге иметь возможность импортировать проект, поэтому я бы предпочел, чтобы пользователь не компилировал вручную каждый файл Haskell при запуске приложения. Или, может быть, есть способ скомпилировать их все каким-то другим способом? Может быть, через другой язык?
@amalloy, устранив методы перенаправления и вместо этого используя bufferedWriter, сработало, большое спасибо за помощь!
«Насколько мне известно, для компиляции файлов Haskell требуется GHC, поэтому мне придется найти способ заставить Java запускать файл Haskell после того, как Java заставит компьютер скомпилировать его с помощью GHC». Вы не компилируете его с помощью Java, вы компилируете его независимо, а затем запускаете его, как и любую другую исполняемую программу, с помощью ProcessBuilder.




Подробности см. в обсуждении ОП, но устранение методов с использованием INHERIT и вместо этого использование bufferedWriter сработало:
try {
// Serialize dataMap to JSON and write to file
String jsonString = objectMapper.writeValueAsString(dataMap);
FileWriter fileWriter = new FileWriter("newtonRaphson.json");
fileWriter.write(jsonString);
fileWriter.close();
// Read JSON file
File jsonFile = new File("newtonRaphson.json");
JsonNode rootNode = objectMapper.readTree(jsonFile);
// Extract solution from JSON
Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields();
Map.Entry<String, JsonNode> secondEntry = fields.next();
JsonNode solutionOut = secondEntry.getValue();
String solutionText = solutionOut.asText();
// Display equation on GUI
Text messageTextFxn = new Text(equation);
messageTextFxn.setFont(new Font("System", 15));
fxnPane.getChildren().add(messageTextFxn);
// Display solution on GUI
Text messageTextSol = new Text(solutionText);
messageTextSol.setFont(new Font("System", 15));
solPane.getChildren().add(messageTextSol);
// Launch Haskell REPL and interact with it
String haskellPath = "C:\\Users\\cezar\\Desktop\\AcademicResources\\Engineering\\Java\\JavaFX_VS\\chemesolver\\haskell\\nr\\src";
ProcessBuilder builder = new ProcessBuilder("stack", "repl");
Process process = builder.start();
OutputStream outputStream = process.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
// Load Haskell module
writer.write(":l " + haskellPath + "\\newtonRaphson");
writer.flush();
System.out.println("Haskell module loaded successfully.");
// Execute Haskell function
writer.write("nr");
writer.flush();
System.out.println("Haskell function executed successfully.");
// Close writer and wait for process to terminate
writer.close();
int exitCode = process.waitFor();
Вы запускаете команду «стек». Это командная строка Haskell? Не можете ли вы скомпилировать файл Haskell и запустить его напрямую по имени?