Search code examples
javascripthaskellio

interaction between Javascript and haskell (stdin/stdout)


I have an exercise to do where:

  • we need to write a program in whatever language we want (I picked haskell), that program take stdin and return stdout (in an endless while loop fashion). this part works fine when I try it from terminal
  • then a provided JS server must call the program we wrote give inputs and do something with its output. (actually the JS server is supposed to call a bash script that call the program but that, I can hack around) this part does not work (the server works fine when interacting with golang version but its sounds unresponsive with my haskell version)

here is a simplified snippet of the haskell IO loop:

main = do
  guess_loop []

guess_loop xs = do
  x <- readLn
  let new_list = x:xs
  putStr $ show $ doSmothing new_list
  putStr "\n"
  guess_loop new_list

this is the snippet JS that communicate with my program (I think):

  //executes the program with the numbers as stdin entries and saves its ouput in `output`
  let output
  try {
    output = execSync(test_file, {
      input: values_to_test,
    })
  } catch (err) {
    res.send({ status: 500, msg: 'No file found: ' + guesser })
    throw Error('No file found\n' + err)
  }

  let value = output.toString().split('\n')
  value.pop()

  let result = 0
  let returning = []
  let correct = 0

and here is the simplified working golang version snippet

func main() {
    reader := bufio.NewScanner(os.Stdin)
    var k []float64
    for reader.Scan() {
        t := reader.Text()
        n, err := strconv.Atoi(t)
        k = append(k, float64(n))
        fmt.PrintLn(do_something(k)) // return []float64
    }
}

I have no clue of JS (and do not need any), I know some golang , I am learning haskell

nothing actually happen so no error to share.

I understood that the lazy aspect of haskell regarding IO could be at play, so I added hSetBuffering stdin LineBuffering hSetBuffering stdout LineBuffering before the IO loop (below main = do) in vain.

I tried to put console.log('hint') everywhere is JS code to figure out what is happening with no luck. I also made haskell IO loop to write output in a file to check if it was actually active when called by JS and it did.

My guts tells me there is something off between try { output = execSync(test_file,inputs} and PutStr result

If you can help me in any way: spot an obvious error or put me on the right track; that would be great. Ideally I would rather do something about the haskell and leave untouched the JS one. If you have any clue please share. Thanks


Solution

  • The problem may be that your example program reads input forever, and when it encounters an end of file, it terminates with an error. The Golang version reads input until it encounters an end of file and then gracefully exits.

    The Javascript driver appears to provide a finite amount of input from values_to_test, and it responds to any failure of the program to run successfully with a 500 No File Found error, ignoring any output that might have been produced before the program fails.

    You could try rewriting your Haskell program to check for end-of-file at the start of the loop:

    import Control.Monad (when)
    import System.IO (isEOF)
    
    main = do
      guess_loop []
    
    guess_loop xs = do
      eof <- isEOF
      when (not eof) $ do
        x <- readLn
        let new_list = x:xs
        putStr $ show $ doSomething new_list
        putStr "\n"
        guess_loop new_list
    

    This probably looks very awkward, but as you become more experienced with Haskell, you'll discover that a more natural way of writing this program is something like:

    import Data.List
    
    main = do
      input <- getContents                  -- read all of stdin, lazily
      let values = map read (lines input)   -- read one value per line
      let output = map doSomething (tail (inits values))
         -- run the computation on every initial part of the list
      putStr $ unlines (map show output)    -- write outputs one per line