| So, I've gotten around to implementing stdin, stdout, and stderr. This works easily because Python also exposes those three, but I got to thinking about the Arc interface. Right now, (stdin) (stdout) and (stderr) return input/output ports, which can then be read/written to. This has the downside of unnecessary parentheses, and because it's a function call, it implies that the value is created dynamically (could change at runtime), when in fact the three ports are constants. So here's my plan for the three ports in PyArc: They will be functions. To be more specific, stdin will be a stream that returns a single char when called. stdout and stderr will be functions that when called, write to stdout and stderr. Thus: PyArc: (stdin) -> #\f
pg-arc: (readc (stdin)) -> #\f
PyArc: (disp "foo" stdout)
pg-arc: (disp "foo" (stdout))
PyArc: (stderr #\f)
pg-arc: (writec #\f (stderr))
In addition, the three variables can be overwritten, allowing Arc programs to redirect the three ports. stdin should be a stream that returns successive chars, and should return nil when it reaches eof, which is okay since nil is never eq to a char (unless you overwrite nil, of course...) stdout and stderr should be normal functions that accept a single parameter.What is a stream? A stream is simply a function that returns successive values. Thus, the following is a "stream" that returns the numbers 1 to infinity: (let x 0
(def num-stream ()
(++ x)))
(num-stream) -> 1
(num-stream) -> 2
(num-stream) -> 3
...
To make defining streams easier, I plan to support a (yield) expression, that works similarly to Python's yield, except it avoids the complicated distinction between functions, generators, and iterators: (def 1-to-3 ()
(yield 1)
(yield 2)
(yield 3))
(1-to-3) -> 1
(1-to-3) -> 2
(1-to-3) -> 3
...
(1-to-3) -> 1
(1-to-3) -> 2
(1-to-3) -> 3
...
(def iter (y)
(fn ()
(each x y
(yield x))
(while t (yield))))
(= it (iter '(1 2 3)))
(it) -> 1
(it) -> 2
(it) -> 3
(it) -> nil
(it) -> nil
...
Note the (while t ...) at the end. This causes it to always return nil after it's exhausted the input. If that wasn't there, it would start over again at 1.Thus, to redirect stdin to a string, you could use this: (= stdin (fn ()
(each x "abcdefg"
(yield x))
(while t (yield))))
(stdin) -> #\a
(stdin) -> #\b
(stdin) -> #\c
...
(stdin) -> nil
...
What do you guys think? Do you see any flaws with this, for instance with Unicode vs bytes? Should stdin return bytes or chars? If it returns chars, it would make redirecting-to-a-string easier, and would allow me to remove `readc` and `writec`. On the other hand, you may actually want to read/write bytes.What about the iterator proposal? It's simple and builds on top of functions, but I suspect most iterators will want to use (while t (yield)). That could be wrapped up in a macro, though: (generator iter (y)
(each x y
(yield x)))
|