Haskell default io buffering

Вчера я написал небольшое упражнение для моих учеников: сделайте обратную эхо-программу.

Чтобы узнать что-то новое, я попытался реализовать решение Haskell. Тривиальный main = forever $ interact reverse не работает. Я прошел этот вопрос и произвел исправленную версию:

import Control.Monad
import System.IO

main = forever $ interact revLines

revLines = unlines . map (reverse) . lines 

Но эта исправленная версия также не работает. Я прочитал буферную документацию и играл с различными настройками. Если я устанавливаю NoBuffering или LineBuffering, моя программа работает правильно. Наконец, я напечатал режимы буферизации по умолчанию для stdin и stdout

import System.IO

main = do 
  hGetBuffering stdin >>= print 
  hGetBuffering stdout >>= print

У меня есть BlockBuffering Nothing, если я запускаю свою программу из xinetd (echo "test" | nc localhost 7), но из cli у меня есть LineBuffering

  • В чем разница между службой xinetd tcp и программой cli, связанной с буферизацией?
  • Нужно ли вручную настраивать буферизацию, если я хочу написать рабочую программу с использованием обоих методов?

Изменить: Спасибо всем за полезные ответы.

Я принимаю ответ, который дал пламя, он дает мне подсказку с исатти (3). Я снова просмотрел документацию System.IO и обнаружил функцию hIsTerminalDevice, с которой я могу проверить подключение дескриптора.

Для записи здесь моя последняя программа:

{-# OPTIONS_GHC -W #-}

import System.IO

main = do
  hSetBuffering stdin LineBuffering
  hSetBuffering stdout LineBuffering

  interact revLines

revLines = unlines . map (reverse) . lines 

Ответ 1

Это не относится к Haskell (например, стандартная библиотека C делает то же самое). Традиционно, если дескриптор файла соответствует терминалу, буферизация устанавливается в режим линии, иначе режим блокировки. Тип дескриптора файла может быть проверен функцией isatty(3) - не уверен, что он экспортирован в System.IO.

И да, вам нужно установить режим буферизации вручную, если вы зависите от него.

Кстати, вы можете обманывать систему и принудительно блокировать блокировку в командной строке, запустив вашу программу как cat | ./prog | cat.

Ответ 2

Система времени выполнения GHC пытается быть умной, когда выбирает буферизацию по умолчанию. Если это похоже, что stdin и stdout напрямую связаны с терминалом, они будут буферизироваться по строке. Если похоже, что они связаны с чем-то другим, они блокируются. Это может быть проблематично, если вы хотите запустить программу с линейным вводом, который не поступает напрямую с терминала. Например, я думаю, что cat | your-program ведет себя иначе, чем просто your-program.

Нужно ли вручную настраивать буферизацию, если я хочу написать рабочую программу с использованием обоих методов?

Да.