2017-01-03 6 views
3

Я новичок как в purescript, так и в haskell (и даже в javascript и узле), поэтому я в тупике пытаюсь понять, как сэкономить выходные данные дочернего процесса в purescript. Я использую модули purescript-node-childprocess и purescript-node-fs. В принципе, проблема, которую я имею это:Как сделать вывод дочернего процесса в purescript?

import Node.ChildProcess (CHILD_PROCESS, SpawnOptions, defaultSpawnOptions, Exit(..), spawn, onExit, stdout) 
import Node.Stream (onData) 

type CPEffect = forall e 
       . Eff (cp :: CHILD_PROCESS 
        , console :: CONSOLE 
        , err :: EXCEPTION 
        , buffer :: BUFFER | e 
        ) Unit 

-- | Basically a wrapper around the spawn command. 
-- | Takes a command, an array of arguments, and a record of 
-- | options to pass to spawn. 
launch :: String -> Array String -> SpawnOptions -> CPEffect 
launch cmd args opts = do 
    cmd' <- spawn cmd args opts 
    onExit cmd' defaultExitHdlr 
    -- My problem is with onData due to its return type 
    onData (stdout cmd') -- what do I put as the callback handler arg? 
    log $ "done with " <> cmd 

Как показано на мой комментарий выше, проблема заключается функция Ondata, которая исходит от модуля Node.Stream. Проблема заключается в 2-арг и тип возвращаемого:

onData :: forall w eff 
     . Readable w (err :: EXCEPTION | eff) 
     -> (Buffer -> Eff (err :: EXCEPTION | eff) Unit) 
     -> Eff (err :: EXCEPTION | eff) Unit 

С возвращением является Eff, который возвращает блок, как я могу спасти от вывода дочернего процесса? Второй аргумент - это функция, которая принимает буфер, а также возвращает тот же тип. Действительно, именно эта функция получает данные из Readable (который является эталоном дочернего процесса). Другими словами, первым аргументом является поток stdout из узла дочернего процесса, а второй arg - обработчик обратного вызова, который будет заполнять буфер из потока stdout.

Но поскольку обработчик обратного вызова возвращает Unit, я не вижу, как я могу накапливать выходные данные дочернего процесса. Я все еще участвую в процессе обучения Monad Transformers, так это решение? Могу ли я создать монаду Writer, которая каким-то образом обертывает это?

ответ

3

Если я собираюсь сделать это в purescript без использования FFI, я бы использовал ссылку Ref и в обратном вызове onData прочитал значение ref, создав новую строку, добавив новые данные в значение ref и затем обновите значение ref этой новой строкой.

WriterT действительно полезен для такого рода вещей, хотя я, вероятно, не буду использовать его в данном конкретном случае, потому что может быть немного неудобно работать над тем, как получить типы для выравнивания, поскольку обратный вызов принимает значение Eff, не WriterT что-то Eff.

Другой вариант, который я использовал для решения этой точной проблемы в Pulp, заключается в использовании пакета npm concat-stream. На самом деле вы вполне можете найти Pulp полезным в качестве реального примера использования этих библиотек. Я думаю, что модули Pulp.Exec и Pulp.System.Stream вас интересуют.

+0

Спасибо hdgarrood, я взгляну на пульпу, чтобы посмотреть, как это было сделано. Я изучаю большую часть своего purescript, рассматривая исходный код существующих модулей. –

1

pscid Я использую STRef, чтобы объединить вывод процесса и распечатать его результаты на консоли, когда процесс завершается. Я скопировал соответствующий код, чтобы у вас был пример:

execCommand 
    ∷ ∀ e 
    . String 
    → String 
    → Eff (cp ∷ CHILD_PROCESS, console ∷ CONSOLE | e) Unit 
execCommand name command = 
    catchLog (name <> " threw an exception") $ 
    runST do 
     let cmd = unsafePartial fromJust (uncons (split (Pattern " ") command)) 
     output ← newSTRef "" 
     log ("Running: \"" <> command <> "\"") 
     cp ← spawn cmd.head cmd.tail defaultSpawnOptions 

     let stout = stdout cp 
      sterr = stderr cp 

     onDataString stout UTF8 \s → 
     modifySTRef output (_ <> s) $> unit 

     onDataString sterr UTF8 \s → 
     modifySTRef output (_ <> s) $> unit 

     onExit cp \e → case e of 
     Normally 0 → logColored Green (name <> " successful!") 
     Normally code → do 
      log =<< readSTRef output 
      logColored Red (name <> " errored with code: " <> show code) 
     BySignal _  → pure unit 
Смежные вопросы