How to implement a transcript program in Haskell?

I would like to be able run a Haskell I/O action that interacts with the user through standard input and output, and that automatically inserts every line extracted from the standard input into the standard output.

This would be useful to me for handling programs submitted by students in assignments.

For an idea, the following Python script does exactly what I want with an executable file.

#! /usr/bin/env python

# example of use:
# python transcript ./a.out input.txt > output.txt

import sys, pexpect

argc = len(sys.argv)

executable = sys.argv[1]

if argc >=3:
    infile = sys.argv[2]
    file = open(infile)
else:
    file = sys.stdin

proc = pexpect.spawn(executable)

for line in file:
    proc.send(line)

proc.sendeof()
proc.expect(pexpect.EOF, timeout=10) # timeout default is 30s
print(proc.before.decode("utf-8"))

Example: running the following Haskell program (sum.hs)

module Main (main) where

import System.IO (stdout, hSetBuffering, BufferMode(NoBuffering))

main :: IO ()
main = do hSetBuffering stdout NoBuffering
          putStr "Enter a number: "
          n1 <- readLn
          putStr "Enter another number: "
          n2 <- readLn
          putStrLn ("The sum is " ++ show (n1 + n2))
$ ghc sum.hs
[1 of 2] Compiling Main             ( sum.hs, sum.o )
[2 of 2] Linking sum

$ ./sum
Enter a number: 10
Enter another number: 20
The sum is 30

$ ./sum <<EOF
> 10
> 20
> EOF
Enter a number: Enter another number: The sum is 30

$ ./transcript.py ./sum <<EOF
> 10
> 20
> EOF
Enter a number: 10
Enter another number: 20
The sum is 30

I want a Haskell solution that takes directly an I/O action instead of an executable.

Any clues on this is very welcome.

So you want to intercept writes to stdout, and to be able to write to stdin? Maybe there’s a way of doing that but it would probably be easier if you wrote the program to take two higher order arguments, say

myMain :: IO String -> (String -> IO ()) -> IO ()
myMain readIt writeIt = ...
1 Like