开发者

What, if any, is wrong with this approach to declarative I/O

I'm not sure exactly how much this falls under 'programming' opposed to 'program language design'. But the issue is this:

Say, for sake of simplicity we have two 'special' lists/arrays/vectors/whatever we just call 'ports' for simplicity, one called stdIn and another stdOut. These conceptually represent respectively

  • All the user input given to the program in the duration of the program
  • All the output written to the terminal during the duration of the program

In Haskell开发者_如何学编程-inspired pseudocode, it should then be possible to create this wholly declarative program:

 let stdOut =   ["please input a number", 
                "and please input another number", 
                "The product of both numbers is: " ++ stdIn[0] * stdIn[1]]

Which would do the expected, ask for two numbers, and print their product. The trick being that stdOut represents the list of strings written to the terminal at the completion of the program, and stdIn the list of input strings. Type errors and the fact that there needs to be some safeguard to only print the next line after a new line has been entered left aside here for simplicity's sake, it's probably easy enough to solve that.

So, before I go of to implement this idea, are there any pitfalls to it that I overlooked? I'm not aware of a similar construct already existing so it'd be naïve to not take into account that there is an obvious pitfall to it I overlooked.

Otherwise, I know that of course:

 let stdOut =   [stdIn[50],"Hello, World!"]

Would be an error if these results need to be interwoven in a similar fashion as above.


A similar approach was used in early versions of Haskell, except that the elements of the stdin and stdout channels were not strings but generic IO 'actions'--in fact, input and output were generalized to 'response' and 'request'. As long as both channels are lazy (i.e. they are actually 'iterators' or 'enumerators'), the runtime can simply walk the request channel, act on each request and tack appropriate responses onto the response channel. Unfortunately, the system was very hard to use, so it was scrapped in favor of monadic IO. See these papers:

  • Hudak, P., and Sundaresh, R. On the expressiveness of purely-functional I/O systems. Tech. Rep. YALEU/DCS/RR-665, Department of Computer Science, Yale University, Mar. 1989.
  • Peyton Jones, S. Tackling the Awkward Squad: monadic input/output, concurrency, exceptions, and foreign-language calls in Haskell. In Engineering theories of software construction, 2002, pp. 47--96.


The approach you're describing sounds like "Dialogs." In their award-winning 1993 paper Imperative Functional Programming, Phil Wadler and Simon Peyton Jones give some examples where dialogs really don't work very well, and they explain why monadic I/O is better.


I don't see how you will weave them considering this example compared to your own:

let stdOut =   ["Welcome to the program which multiplies.",
                "please input a number", 
                "and please input another number", 
                "The product of both numbers is: " ++ stdIn[0] * stdIn[1]]

Should the program prompt for the number represented by stdIn[0] after outputting one line (as in your example) or two lines? If the index 0 represents the 0th input from stdin, then it seems something similar to:

let stdOut =   ["Welcome to the program which multiplies.",
                "please input a number",
                some_annotation(stdIn[0]),
                "and please input another number", 
                some_annotation(stdIn[1]),
                "The product of both numbers is: " ++ stdIn[0] * stdIn[1]]

will be required in order to coordinate the timing of output and input.

I like your idea. Replace some_annotation with your preference, perhaps something akin "synchronize?" I couldn't come up with the incisive word for it.


This approach seems to be the "most obvious" way to add I/O to a pure λ-calculus, and other people have mentioned that something along those lines has been tried in Haskell and Miranda.

However, I am aware of a language, not based on a λ-calculus, that still uses a very similar system:

How to handle input and output in a language without side effects? In a certain sense, input and output aren't side effects; they are, so to speak, front- and back-effects. (...) [A program is] a function from the space of possible inputs to the space of possible outputs.

Input and output streams are represented as lists of natural numbers from 0 to 255, each corresponding to one byte. End-of-file is represented by the value 256, not by end of list. (This is because it is often easier to deal with EOF as a character than as a special case. Nevertheless, I wonder if it wouldn't be better to use end-of-list.)

(...)

It's not difficult to write interactive programs (...) [but] doing so is, technically speaking, a sin. (...) In a referentially transparent language, anything not explicitly synchronized is fair game for evaluation in any order whatsoever, at the run-time system's discretion.

(...) The most obvious way of writing this particular program is to cons together the "Hello, [name]!" string in an expression which is conditioned on receipt of a newline. If you do this you are safe, because there's no way for any evaluator to prove in advance that the user will ever type a newline.

(...)

So there's no practical problem with interactive software. Nevertheless, there's something unpleasant about the way the second case is prevented. A referentially transparent program should not have to rely on lazy evaluation in order to work properly.

How to escape this moral dilemma? The hard way is to switch to a more sophisticated I/O system, perhaps based on Haskell's, in which input and output are explicitly synchronized. I'm rather disinclined to do this, as I much prefer the simplicity of the current system. The easy way out is to write batch programs which happen to work well interactively. This is mainly just a matter of not prompting the user.

Perhaps you would enjoying doing some programming in Lazy K?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜