PureScriptで標準入力を扱うサンプルコード

PureScriptで標準入力を扱う方法が案外分かりにくいのでサンプルコードを貼ってみます。

※ 以下のコードはPureScript 0.15.15で動作確認しています。

# 一行ずつ処理しなくて良い場合（かつMac/Linuxのみ）

大量のデータが入力されるユースケースでは標準入力を1行ずつ読み込んで都度処理しないとメモリを使い切ってしまう場合がありますが、そこまで大量でもない入力であれば以下のコードが一番シンプルです。

```purescript
module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)
import Node.Encoding (Encoding(..))
import Node.FS.Sync (readTextFile)

main :: Effect Unit
main = do
  cs <- readTextFile UTF8 "/dev/stdin"
  log cs
```

ただし、/dev/stdinはWindowsでは使えないので、MacかLinuxで実行する場合しかこの方法は使えません。

プロジェクトの作成開始からの流れは以下のようになります。

```shell-session
spago init
spago install node-buffer node-fs
# src/Main.pursを編集
echo "hello" | spago run # テストを実行
```

# 一行ずつ処理しないといけない場合、もしくはWindowsにも対応したい場合

前述のプログラムと同様の処理をするコードは以下のようになります（node-readlineというパッケージを使うのがキモです）。

```purescript
module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)
import Node.EventEmitter (on_)
import Node.Process (stdin)
import Node.ReadLine (createInterface, lineH)

main :: Effect Unit
main = do
  interface <- createInterface stdin mempty
  interface # on_ lineH \s ->
    log s
```

プロジェクト作成開始からの流れは以下です。

```shell-session
spago init
spago install node-event-emitter node-process node-readline
# src/Main.pursを編集
echo "hello" | spago run # テストを実行
```

このコードの派生型として、その他のユースケースのサンプルコードも以下に載せておきます。

# ユーザーとインタラクティブにやり取りしたい場合

```purescript
module Main where

import Prelude

import Effect (Effect)
import Effect.Class.Console (log)
import Node.EventEmitter (on_)
import Node.ReadLine (close, createConsoleInterface, lineH, noCompletion, prompt, setPrompt)

main :: Effect Unit
main = do
  interface <- createConsoleInterface noCompletion
  setPrompt "> " interface
  prompt interface
  interface # on_ lineH \s ->
    if s == "quit"
    then close interface
    else do log $ "Got " <> s
            prompt interface
```
# 先頭一行だけ無視したい場合

```purescript
module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)
import Node.EventEmitter (on_, once_)
import Node.Process (stdin)
import Node.ReadLine (createInterface, lineH)

main :: Effect Unit
main = do
  interface <- createInterface stdin mempty
  interface # once_ lineH \_ ->
    interface # on_ lineH \s ->
      log s
```

# 標準入力から1行ずつではなくすべて一括で処理したい場合

```purescript
module Main where

import Prelude

import Data.Array as A
import Data.String as S
import Effect (Effect)
import Effect.Console (log)
import Effect.Ref as Ref
import Node.EventEmitter (on_)
import Node.Process (stdin)
import Node.ReadLine (closeH, createInterface, lineH)

main :: Effect Unit
main = do
  readAll log

readAll :: (String -> Effect Unit) -> Effect Unit
readAll callbak = do
  inputStrings <- Ref.new []
  interface <- createInterface stdin mempty
  interface # on_ lineH \s ->
    Ref.modify_ (A.cons s) inputStrings
  interface # on_ closeH do
    cs <- (S.joinWith "\n" <<< A.reverse) <$> Ref.read inputStrings
    callback cs
```

このコードでは、一行ずつ読み込んだテキストをinputStringsという参照に溜め込んでいき、標準入力から読み込むものがなくなったら、コールバックとして渡された関数にまとめて渡しています。

もっと良い方法があったら教えてください。他にも「こんなときどうすればよいか？」といったご質問があれば（分かる範囲で）回答したいと思いますので、お気軽にコメントください。

参考にしたソース：
https://github.com/purescript-node/purescript-node-readline/blob/master/test/Main.purs