Re: Making argv a constant
Holy cow! It's been a long time since I've had my mailbox packed with letters from the Haskell list. Just when I was beginning to wonder if there were still other people writing Haskell programs... :) | First, Simon, I think you're a little biased on this issue. I'm sure that | making argv a global constant would be a practical benefit for programs | like GHC. You're probably right. I am certainly biased towards large programs. But I really want Haskell to scale to large programs, rather than become unreasonably inconvenient to use when the program becomes big. Well, I don't want to make a federal case out of this issue, but I what I had in mind when I made the above statement was the fact that so many "real" applications these days use a GUI as a front-end. These kinds of programs typically *don't* accept a large number of command line arguments; instead they read in parameters from an initialization file. I would almost go so far as to call the CLI (command line interace) archaic, except that it resembles a function call so much. :) It's still a fact, though, that even in FL programming we avoid functions with a very large number of arguments, and monads are one convenient way around this. I'm not contesting the idea that the way argv is handled in Haskell currently is inadequate. But I am saying that the simple solution of making argv a global constant is not good FL style, for reasons I and others have already mentioned. When you write a function that takes a large number of arguments, do you usually solve it by making all the arguments global?? In Haskell, I think, the preferred approach is to break the function into a small set of combinators which can be composed instead. In a sense, this is a moot point here because conventional OSes impose this kind of CLI "bottleneck", and it only goes to show how out of place FL languages are in such a context. | nooks and crannies of a program. Most applications are not nearly so | configurable, I disagree! Most "real" programs (i.e. products) are highly configurable. Well, re: configurability, I still think a compiler is in another dimension altogether, but even for other highly configurable applications, it is now considered questionable practice to parametrize them at the command-line for several reasons: 1. it's ugly and difficult to understand for beginners 2. it encourages the birth of many, unnecessarily dissimilar throw-away languages with questionable syntax (consider sed, tr, grep, etc.) 3. it is often insufficiently expressive (see #2) 4. it fixes statically Hence, it is usually much better in "real" applications to provide a way to set parameters internally, for example by means of an "Options" menu, etc. [Note: Many of you will insist that programs like sed and tr are so useful precisely because of the flexibility of their CLI. I don't disagree with that; I'm just saying that there is some "unnecessary diversity" -- to quote the Haskell Report.] | Third, I could say that choosing to use a functional language is in some | sense also accepting the fact that you are "forced to accept some benefits" | which you could do without. I don't agree. In exchange for the costs of higher-order functions, or laziness, or the lack of side effects, I get some benefits. In exchange for making argv accessible only via an IO action I get nothing at all. But you do, and you've admitted it yourself! You get more modularity, flexibility and maintainability, like for example the ability to parametrize your program with several sources of input, or input from a file rather than the command line. Besides, the point here is not whether we perform an IO action or not. In fact, I would argue that argv has to do with IO indirectly, in that it provides string values that can be mapped to input files (for instance). The point is, for me at least, the scoping. By this reasoning, we might just as well revert to the "main :: [String] - String" model, though, which I think is inadequate. Since I've gone this far, I might as well explain why I find the whole global argv idea so outright distasteful. Simon keeps talking about Real World Applications, so I might as well lay it on the table here and now. Frankly, I don't give a damn about GUI interfaces, options menus or argument plumbing. My gut reaction to global argv is that it is bad because it encourages writing monolithic applications, rather than communicating processes. The current trend in the Real World now is component-oriented: things like OpenDoc, ActiveX and CORBA, where programs are small and dynamically configurable. In this sense, the old, honored Unix tradition of combinator-like, map- and filter-like programs is still alive and well, and in some ways Haskell is in a great position to capitalize on it. Making it easy to read and farm out arguments from a command line packed with unrelated options is as sure a ticket to the
Subunits (was: Re: Making argv a constant)
Sverker Nilsson wrote: In Ada they have structured a similar kind of code inclusion with something called "subunits". . . . body_stub ::= subprogram_specification IS SEPARATE |PACKAGE BODY package_simple_name IS SEPARATE; |TASK BODY task_simple_name IS SEPARATE; subunit ::= SEPARATE (parent_unit_name) proper_body [End Quote] Also using the package syntax from Ada (to avoid the risk of overloading the Haskell Module Concept excessively) a direct translation of the compiler example to use these subunit directives (without semicolons ;-) would be: [omitted details of example using proposed Haskell subunits] In Ada, they separate specification and implementation of packages. I think this is partly for information hiding purposes and partly to help the compiler to separate-compile efficiently. I have only used the package body part in this example, assuming the specifications be generated in some magic way. But I actually think that also Haskell could take advantage of separation of specification and implementation. Body stubs work in Ada because there is always a full specification of the stubbed unit visible at the position of the body stub, so that the compiler has all the information it needs to handle any subsequent references to the stubbed unit. (For packages and tasks, there must be a full specification preceding the body stub, while for subprograms, the specification is part of the body stub.) It is not necessary for the compiler to read the proper body while it is compiling the parent unit, as the proper body provides only implementation details. On the other hand, the parent unit (with its body stubs) must be compiled before the subunit, so that the compiler has access to the declarations in the parent that are to be made visible to the subunit. A corresponding mechanism in Haskell would likewise require separate specification (or interface) and implementation for each subunit. The specification would either need to be part of the body stub, or appear as some kind of prior declaration visible at the position of the body stub. I'm not familiar enough with Haskell modules to say whether they could be readily adapted to this purpose. - Jim Hassett
Re: Making argv a constant
Simon L Peyton Jones, you wrote: Fergus | I would find Simon's arguments more convincing if he showed | a convenient idiom that did things properly, rather than a | convenient way to write broken programs. | | (Doing it properly is probably not too hard, but I'll leave it up to | the proponents of this proposal to demonstrate this...) It's hard for me to respond to this since I don't know what you have in mind when you say "properly". If you mean proper error reporting, then that's not difficult: main = checkArgForErrors argv rest of prog... [aside: presumably you can drop the argv parameter, since you want argv to be a constant, right?] I'm not sure what else you had in mind. Just that, together with Joe Fasel's suggestions, plus putting the argument-parsing stuff in an exception monad, and defining `checkArgForErrors' to check whether the option structure is `Fail message'. One difference between doing it "properly" and doing it the way you suggested in your original post is that doing it properly is significantly more complex. This alters the trade-offs a bit. (Sorry if I've been a bit pedantic -- see my .sig ;-) P.S. Is this list archived anywhere? -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED] | -- the last words of T. S. Garp.
Re: Making argv a constant
Sigbjorn Finne, you wrote: OK, so stdin is not a global constant anymore, but is stashed away in TLS somewhere. No! stdin _is_ a global constant. What is stashed away in TLS is the binding of stdin to whichever file it happens to be bound to. Of course if your thread implementation doesn't have TLS as such, you can put this binding in the thread-local IO state. (Your thread implementation *must* have this, if redirecting stdin for a thread can be implemented at all.) I don't like this at all, for the following reasons: Your implementation-related arguments are all bogus. [...] But, the standard handles can be made thread-local, just provide access to them through IO! A straightforward implementation of IO in a multi-threaded setting might be type Triple a = (a,a,a) newtype IO a = IO (Triple Handle - _RealWorld - (_RealWorld, a)) Creating new processes (using Concurrent Haskell's forkIO, say) can then easily be augmented to functionally change the interpretation of the standard handles for a process. This solution is simple, modular and does not require TLS or other RTS Magic. ... and works fine if `stdin', `stdout', and `stderr' are constants! I'm only arguing that make the standard handles be connected to IO, which is the only context they can *ever* be used. The fact that the standard handles can only ever be used in conjunction with an IO is exactly why they can be constant. Think of the constant value as an index into your triple in IO. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED] | -- the last words of T. S. Garp.
Re: Making argv a constant
Sigbjorn Finne, you wrote: ... the issue here is *not* how you configure your programs, there's more than one way to do that (to paraphrase the Perl slogan), but the fact that argv is *constant* and should be provided as such. I've yet to see a post contending that argv isn't. I pointed out that although argv is constant over any single program execution, it variables from execution to execution, so it's not really constant. Note that there are existing functional language implemenations, in particular Gofer, that assume that constants are really constant, not just constant over each program execution. For example, the following Gofer program x = 42 y = x + x main = appendChan stdout (show y) abort done requires fewer reductions when you run it the second time around, because `y' has already been evaluated. The behaviour of Hugs is different in this respect (why? was it changed to support hacks such as `argv = unsafePerformIO getArgs'?). It appears that in Hugs, CAFs are reset whenever you get back to the command prompt; evaluating `y' twice gives you the same number of reductions in both cases. Requiring the Hugs behaviour could (a) make implementing Haskell slightly more difficult and (b) reduce efficiency, for interpreters and JIT compilers. -- Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" PGP: finger [EMAIL PROTECTED] | -- the last words of T. S. Garp.
Re: Making argv a constant
I would claim it has really nothing to do with first-class modules or parameterized modules. They can be simulated by ordinary Haskell data types with named fields. No they can not. Haskell records do not allow local polymorphism, so you have a significant loss of power. -- Lennart
Re: Making argv a constant
Lennart Augustsson [EMAIL PROTECTED] writes: ... Can't we agree on a name and make it one of the "commonly implemented extensions" to Haskell? That way I can still feel that Haskell is pure, but if you feel the urge to use it you can still do it. I'd actually prefer the performOnceBeforeMain solution if someone can come up with some simple restrictions to make it work. I think this is a good compromise, leave System.getArgs as it is and have a new extension module called SystemExt (?) that would contain constants for argv and the environment (and leave room for growth for other System overflow). If it turns out that at some future crossroads for the Haskell libraries, argv is so commonly used, then move it to System. I'm happy with that. I haven't thought through `performOnceBeforeMain' and its implications. --Sigbjorn
Re: Making argv a constant
Fergus Henderson [EMAIL PROTECTED] writes: I pointed out that although argv is constant over any single program execution, it variables from execution to execution, so it's not really constant. It is constant within the lexical scope of an executing program; that's constant enough for me, your mileage may vary. ... The behaviour of Hugs is different in this respect (why? was it changed to support hacks such as `argv = unsafePerformIO getArgs'?). It appears that in Hugs, CAFs are reset whenever you get back to the command prompt; evaluating `y' twice gives you the same number of reductions in both cases. Nothing nefarious, I'm afraid. The change from Gofer to Hugs was done to avoid residual CAF garbage building up (the simplest way of implementing CAF GC - I think Nottingham and Yale are discussing how to do this properly). Re: argv in a command-line interpreter, it is not obvious why you want to change argv from within an eval prompt - to configure the invocation of Hugs, yes, but not to set the command-line options for later evaluations. (last time I looked, Hugs had System.getArgs defined to always return []) The latest beta release of Yale Hugs support the use of it as a command-line tool, i.e., a silent option causes it to suppress all interpreter output, reading just expressions to evaluate from stdin and output their results on stdout. Perhaps the command shell is a more appropriate place from where to change argv? --Sigbjorn
Re: Making argv a constant
Simon L Peyton Jones wrote: The point is, if I *want* to have several behaviours in one run I can always use this second technique. But Haskell as it now stands prevents me from using the first, even when I *don't* want several behaviours in one run. I'd be prepared to pay the pain if I wanted the benefit. Since I don't want the "benefit" I don't want the pain. That seems fair enough. Actually I have wanted to do things the first way myself sometimes with other constant things than the environment, but then I have just thought that that's not possible in principle, in Haskell. I see this environment stuff like a special case of some more general problem. There are other constants that one might like to have as constants in the program. For example, the date and time the program started is also a constant and could reasonably be accessed as such. (It could be in the environment, but in general it has to be read into the program in some other way.) So if it turns out to be possible to make the environment a constant, it should open up possibilities for other constants as well. I would rather see a general mechanism. Something like: performOnceBeforeMain:: IO a - a timeStarted = performOnceBeforeMain (getCurrentTime) Just like that one would do with unsafePerformIO, but with safer semantics (hopefully) and blessed by Standard Haskell. Then we wouldn't even need to change the environment to constant. And we would have the benefit of having access to any other things that are constant as well. This message has clarified that there are three (not two) reasons that argv should be constant 1. Easier programming 2. Faster running 3. Extra reasoning ability (this the new one; every call to f will behave the same way) The only real problem I can think of right now, is that this - I would really like to call it this paradigm shift - could complicate implementation of persistent storage systems. But I feel quite fuzzy about that anyway right now, so... Sverker
Re: Making argv a constant
The point is, if I *want* to have several behaviours in one run I can always use this second technique. But Haskell as it now stands prevents me from using the first, even when I *don't* want several behaviours in one run. I'd be prepared to pay the pain if I wanted the benefit. Since I don't want the "benefit" I don't want the pain. First, Simon, I think you're a little biased on this issue. I'm sure that making argv a global constant would be a practical benefit for programs like GHC, but a compiler is a very specialized application, and exactly the sort that takes a huge number of arguments and then threads them to all the little nooks and crannies of a program. Most applications are not nearly so configurable, and I think there is considerably less burden on the programmer when it comes to distributing argument values. I think Simon's bias is towards big systems. The LOLITA Natural Language Processing system which the group here is working on is 54k lines of Haskell and we have experience exactly the same kinds of problem that Simon mentions. Also, Sverker Nilsson wrote: performOnceBeforeMain:: IO a - a timeStarted = performOnceBeforeMain (getCurrentTime) Just like that one would do with unsafePerformIO, but with safer semantics (hopefully) and blessed by Standard Haskell. This doesn't look very safe to me if performOnceBeforeMain get's called more than once. Rick
Re: Making argv a constant
timeStarted = performOnceBeforeMain (getCurrentTime) Just like that one would do with unsafePerformIO, but with safer semantics (hopefully) and blessed by Standard Haskell. I see some serious problems: a = (performOnceBeforeMain m1,performOnceBeforeMain m2) In which order are m1 and m2 performed? Don't know. Maybe it could be acceptable to define it to be nondeterministic, like for the relative interleaving of the branches of forkIO. (Which isn't standard Haskell either...) f x = performOnceBeforeMain x What does this mean? Don't know. Maybe it means a run-time error: I'd think that the system could detect that there is an occurence of performOnceBeforeMain that was not possible to reduce before Main started. Even if we only allow performOnceBeforeMain in declarations of the form name = performOnceBeforeMain expr Yes, this seems to be a safer form. in which order should such declarations be performed? How about only allowing one call to performOnceBeforeMain in your program? It may be a little awkward from a software-engineering point but... module Constants where (options, startTime, addressBook, etc) = performOnceBeforeMain expr I don't see these problems as more serious than the very concept of making things from the environment, in general, available as constants in your program. If we could have the Environment variables as constants, and can solve the semantic difficulties associated with that, and the other problems that might occur, then I think we should amortize this work on a general mechanism. So that you could for example have a constant look-up table array available, read from a file of several megabytes. (Impossible to pass in the Environment, unless you make some really radical changes to some system-defined constants, in the Unix Kernel I think.) (Personally, _I_ would like to get at least that benefit for the work that I think I will have to do to redesign my Haskell persistency library almost from scratch, which would be needed if we made the Environment constant :-) Sverker
Re: Making argv a constant
I think Fergus's efficiency argument may be a red herring. Here is an excerpt from a compiler I wrote recently: data JvlArgs = JvlArgs {optNoLink :: Bool, optVerbose :: Bool, jvlClassNames :: [String]} deriving Show jvlArgs :: JvlArgs jvlArgs = getJvlArgs (unsafePerformIO getArgs) JvlArgs {optNoLink = False, optVerbose = False} getJvlArgs :: [String] - JvlArgs - JvlArgs getJvlArgs ("-c":ss) args = getJvlArgs ss (args {optNoLink = True}) getJvlArgs ("-v":ss) args = getJvlArgs ss (args {optVerbose = True}) getJvlArgs (s@('-':_):_) _ = error ("bad option: " ++ s) getJvlArgs ss args = args {jvlClassNames = map internalClassName ss} Note that argv (= unsafePerformIO getArgs) is a constant (as is jvlArgs), defaults are dealt with systematically, error handling (not very extensive in this case) is done, and the arguments are scanned only once (lazily, in fact). Cheers, --Joe Fergus Henderson wrote: | Simon L Peyton Jones wrote: | | I agree with Sigbjorn about argv, rather strongly, though apparently nobody | else does. | | No, I agree Sigbjorn's proposal is probably a good idea, although I don't | feel strongly either way. (I was just disagreeing with the reasoning that | he used to motivate it.) | | module CmdLineOpts where | | argv = unsafePerformIO getArgs | | unfoldSize :: Int | unfoldSize = lookupInt "-funfold-size" argv | | useCleverFiniteMap :: Bool | useCleverFiniteMap = lookup "-fclever" argv | | I have a comment, and couple of questions. | | First, this will involve scanning argv once for each possible option; | I guess option handling is not likely to be a bottleneck, but still... | this offends some aesthetic sense of mine. | | Second, how do you handle syntax errors in the command line arguments? | What does lookupInt do if the integer overflows, or if the argument | is not valid syntax for an integer? Do you check for misspelt or | invalid option names? | | PS. I'm less steamed up about the stdin issue; but I think you missed | Sigbjorn's point. Yes stdin is a constant now, but he'd like stdin *not* to | be a constant, so that he could take a value of type IO () that used stdin, | and reconnect its stdin to (say) a file. | | Even if stdin remains a constant, you could still do that, because even | if the handle is a constant, the connection between handle and file can | still vary, just as the file contents can vary. | | -- | Fergus Henderson [EMAIL PROTECTED] | "I have always known that the pursuit | WWW: http://www.cs.mu.oz.au/~fjh | of excellence is a lethal habit" | PGP: finger [EMAIL PROTECTED] | -- the last words of T. S. Garp. | Joseph H. Fasel, Ph.D. email: [EMAIL PROTECTED] Technology Modeling and Analysisphone: +1 505 667 7158 University of Californiafax:+1 505 667 2960 Los Alamos National Laboratory postal: TSA-7 MS F609 Los Alamos, NM 87545
Re: Making argv a constant
P.S. Is this list archived anywhere? Not at the moment. But I'm planning on making messages available on the web. I have all the posts since I started moderating the list back in October, and I can probably get hold of stuff before that. Cheers, David. -- David J. King Email: [EMAIL PROTECTED] Department of Computing Science Phone: +44 (0)141 339 8855 ext 8335 University of Glasgow Fax:+44 (0)141 330 4913 Glasgow G12 8QQURL:http://www.dcs.gla.ac.uk/~gnik
Re: Making argv a constant
Fergus, Quite right. I used "error" because I was lazy. In fact, the lazy evaluation of the arguments is also a red herring, because the compiler is in fact strict in argv. (How else does it know what to compile?) All the flag arguments must be scanned in order to retrieve "jvlClassNames jvlArgs". I suppose that a better way to do this is something like this: getJvlArgs :: [String] - JvlArgs - Either JvlArgs String processedArgs :: Either JvlArgs String processedArgs = getJvlArgs argv JvlArgs {optNoLink = False, optVerbose = False} jvlArgs :: JvlArgs jvlArgs = case processedArgs of Left args - args Right msg - error ("mishandled arguments: " ++ msg) Then the main IO monad of the compiler can check processedArgs once when it picks up the list of class names and proceed with compilation or yield an IO error. This scheme still avoids plumbing jvlArgs through the rest of the compiler. But I'm a lazy programmer. ;-) At this point, I may be leaning more toward Lennart's position, as I'm less convinced of how valuable the "convenience" is. --Joe Fergus Henderson wrote, | I'm still not entirely happy with the error handling, though. | As a general rule, I try to use `error' only for internal software | errors, not for error messages that can result from the user's | mistakes. (But perhaps that is another functional programmer's | death wish... what do other people think about this issue?) | | Also, the fact that the arguments are scanned lazily is in fact | slightly worrying -- I hope your program is guaranteed to always | evaluate jvlArgs, since I don't think it would be a good idea to ignore | syntax errors in the command-line options just because you don't happen | to execute a part of the program that needs to examine them. Joseph H. Fasel, Ph.D. email: [EMAIL PROTECTED] Technology Modeling and Analysisphone: +1 505 667 7158 University of Californiafax:+1 505 667 2960 Los Alamos National Laboratory postal: TSA-7 MS F609 Los Alamos, NM 87545
Re: Making argv a constant
Chris Dornan writes: Surely, the argv global-constant proposal is cool. What is being proposed is is a modest extension, namely introducing a constant that is initialised with a copy of the command-line arguments. Cannot this sit along side the existing proposal, with each mechanism being used appropriately. I can think of many programs where I would quite usefully use the global constant -- I suspect all of them. This is spot on I think, if you want to go the extra mile and provide an options file, say, then you will have to get out all your plumbing gear, rather than record options via CAFs, if you so wish. But, the issue here is *not* how you configure your programs, there's more than one way to do that (to paraphrase the Perl slogan), but the fact that argv is *constant* and should be provided as such. I've yet to see a post contending that argv isn't. But hold on a moment. What happens if you have a Stoye-style process model in which you are kicking off processes by running `main' programs from a shared repository of program modules. Now each of the processes would be a separate program with its own command-line arguments, but the global-constant proposal cannot deal with this as many processes with different arguments would be executed from the same collection of modules. Many hackful solutions exist to get around this problem to be sure, but the proposal would seem to be decidedly out of place with this multi-process execution model. If you're going to provide a functional process abstraction like this, then you will have to find another way, perhaps fork :: ([String]{-argv-} - IO ()) - IO () (You'd probably also want to do something about I/O and have the standard handles for each process be separate). Slightly restrictive you might say, but I personally wouldn't lose any sleep over it (do you really want a functional OS with one big, shared heap?) --sigbjorn
Re: Making argv a constant
Fergus | I would find Simon's arguments more convincing if he showed | a convenient idiom that did things properly, rather than a | convenient way to write broken programs. | | (Doing it properly is probably not too hard, but I'll leave it up to | the proponents of this proposal to demonstrate this...) It's hard for me to respond to this since I don't know what you have in mind when you say "properly". If you mean proper error reporting, then that's not difficult: main = checkArgForErrors argv rest of prog... I'm not sure what else you had in mind. Simon
Re: Making argv a constant
"variables won't, constants aren't" (sorry, don't remember the source) The argv-problem and its proposed solutions: 1. fetch argv via the IO monad wherever it is needed + it works - don't want to program everything in monadic style 2. fetch argv via the IO monad once and pass it around explicitly + it works - don't want to pass all these parameters around everywhere - could just as well use a monad for this - see 1. 3. make argv a global constant + may work (for this problem) - needs some ad hoc changes to the language and implementation : hmmm, really global, really constant? Just a thought - wouldn't first class modules help here? Not that they were available right now, but let me explain the idea anyway: a) you have a collection of declarations and some of them need access to what seem to be constants *for these declarations in question*, which is a *local* property. b) these `constants' are not really constant - argv contains the *parameters* to the executable. The correct alternative would be an additional parameter to main, but that's what the IO monad is for, isn't it? So we seem to need a way to *parameterize a collection of declarations* with a *runtime* value. This is why simple parameterized modules would not help and, if I see this correctly, why even a separate module language ala SML extended with higher-order functors would not be enough here - they explicitly address modules *before* runtime. But with true first class modules, we could do something like this: MyCompiler argv = struct parse = . argv ..., typeIt = .. f optimize inline, ... f opt inl = ... argv ..., ... compile = parse typeIt ... end main = getArgv = \argv- checkArgvForErrors argv (MyCompiler argv).compile Not very realistic, not even Haskell (yet), but I hope you get the idea. It will not solve your problem immediately, but it may be the generalization you've been looking for. It avoids passing parameters as global constants and does not bypass the IO monad when doing the system interactions this thing was designed for. It is modular, does not break down for the other problem variants mentioned and it does not need no ad-hoc restrictions to work. Of course, if your MyCompiler had only one entry function, a local definition instead of the local module would do: MyCompiler argv = let parse = type = ... in parse type .. So first class modules seem to be a natural and useful extension, in line with usual functional programming techniques. As mentioned in the other thread, static type inference seems to cost you here: it converts the simple idea into quite a complex problem. I have also ignored the issue of (local) type declarations here. For what its worth, -- Claus ReinkeUniversity of Kiel email: [EMAIL PROTECTED]Department of Computer Science http://www.informatik.uni-kiel.de/~cr/ Preusserstr. 1-9, 24105 Kiel
Re: Making argv a constant
Lennart Augustsson writes: Fergus Henderson wrote: Sigbjorn Finne, you wrote: I don't honestly see what having these handles as constant *gain* you, so why then have them as such, if not having them constant gives you extra expressiveness? But, unless I'm missing something, making them non-constant doesn't give you any extra expressiveness. Remember that the fact they are constant does not imply that the file they are connected to is constant. Even if they are effectively thread-local, i.e if the file that stdin and stdout are connected to depends on which thread you're in, stdin and stdout themselves can still be constants, can't they? Exactly! OK, so stdin is not a global constant anymore, but is stashed away in TLS somewhere. I don't like this at all, for the following reasons: - one of the touted benefits of using continuations is the ease by which you can implement a multi-threaded system, Schemers and ML folks have been doing this for years, as you no doubt know better than me. Threads in such an implementation have no TLS, that's the beauty of them, so your solution would preclude the use of continuations here, right? (same goes for Concurrent Haskell, it does not use continuations as such, but only record the STG state of a thread in a special heap object). - If you do require multi-threaded Haskell implementations to have TLS, your solution would be inefficient, what if you were to communicate a value like the following between two threads (\x y - hPutStr (if x then stdout else y) "Ay,ay,ay!") do you then have to walk over the thunk you're transmitting to to hunt for the uses of the thread-local handles, and somehow relativise them to the thread you're communicating with? But, the standard handles can be made thread-local, just provide access to them through IO! A straightforward implementation of IO in a multi-threaded setting might be type Triple a = (a,a,a) newtype IO a = IO (Triple Handle - _RealWorld - (_RealWorld, a)) Creating new processes (using Concurrent Haskell's forkIO, say) can then easily be augmented to functionally change the interpretation of the standard handles for a process. This solution is simple, modular and does not require TLS or other RTS Magic. I regard stdin, stdout, and stderr as names for abstract versions of 0, 1, and 2 (does my background in C programming on Unix show? :-). These are just handles, the can be reconnected to anything. If you argue that you should be able to change the meaning of stdin I could argue "This piece of code I've got uses the constant 5, this is not what I want, I need to change the value of 5 locally when that code runs. Give me the machinery to do that!" OK if you *really* want that, it's easy enough to do: newtype IntVal = IntVal ((Int - Int) - Int) lift :: Int - IntVal lift v = IntVal (\ f - v f) run :: IntVal - (Int-Int) - Int run (IntVal v) f = v f valPlus :: IntVal - IntVal - IntVal valPlus (Val v1) (Val v2) = Val (\ f - v1 f + v2 f) instance Num IntVal where (+) = valPlus fromInt n = lift n -- not standard haskell, but you get the idea. ... main = print (run (2+2) (+1)) -- prints 6 :-) :-) I'm only arguing that make the standard handles be connected to IO, which is the only context they can *ever* be used. Doing so, adds valuable expressiveness over stdio, which I consider a Good. But then again, Dennis Ritchie is not my hero :-) --Sigbjorn
Re: Making argv a constant
Fergus Henderson writes: ... I'm still not entirely happy with the error handling, though. As a general rule, I try to use `error' only for internal software errors, not for error messages that can result from the user's mistakes. (But perhaps that is another functional programmer's death wish... what do other people think about this issue?) On the use of `error', according to my Partain Dictionary, that's not a death wish, its entry says just Losing :-) --Sigbjorn
Re: Making argv a constant
Lennart says: I've sinced changed my mind, maybe it is "a functional programmers deathwish", but I find the case where you are explicit about what a function depends on to be more honest and true to the FP spirit. I've also found it practically useful since you can then change the values of the given flags locally (which you couldn't do with a global argv). Is this an argument against having lexically scoped variables altogether? (Iknew you were into supercombinators, but this much? :-) Nikhil
Re: Making argv a constant
Fergus Henderson writes: Sigbjorn Finne, you wrote: OK, so stdin is not a global constant anymore, but is stashed away in TLS somewhere. No! stdin _is_ a global constant. What is stashed away in TLS is the binding of stdin to whichever file it happens to be bound to. Of course if your thread implementation doesn't have TLS as such, you can put this binding in the thread-local IO state. (Your thread implementation *must* have this, if redirecting stdin for a thread can be implemented at all.) Right, the implementation of IO could be an environment + state monad, carrying around the current bindings for the standard handles (I gave the type for the obvious way of representing this). But, the *thread implementation* can be oblivious to all of this, which is a Good Thing (threads aren't necessarily IO-bound, you could have one performing ST actions, for instance). If I'm understanding Lennart and you correctly (finally, I hear you say!), the representation you prefer, would have an implementation something like the following in a multi-threaded setting: data Handle = Stdin | Stdout | Stderr | Other _FilePtr -- say stdout = Stdout and then have hPutStr do the following: newtype IO a = IO ((Handle,Handle,Handle) - PrimIO a) hPutStr :: Handle - String - IO () hPutStr h str (stdin,stdout,stderr) = case h of Stdout - primPutStr stdout str Other fp - primPutStr fp str _- fail "" with putStr then just being putStr str = hPutStr stdout str whereas I want stdout = getStdout putStr str = do h - stdout hPutStr h str -- primPutStr (+valid handle check), really. I don't think we're in violent disagreement here - the sink for standard output is fixed wrt. to the IO context of the thread executing, but all IO actions have an associated output stream. My choice of how to do this would require the introduction of IO operations for accessing a standard handle's current bindings (as well as setting them), yours wouldn't require an extension - you win :-) Regards, --Sigbjorn
Re: Making argv a constant
Simon Peyton Jones wrote: It was Just Too Painful to pass the flags everywhere. For example, deep in some dark corner of the transformation system there's a constant that says how big a function body can be before GHC inlines it. Threading the command-line arguments all the way to that site is desperately painful. It's even worse if you discover that you'd like a command-line-controllable thing in a dark corner that doesn't yet have the plumbing... you have to add an extra argument to a chain of functions all the way to the top. I'd naively think that you could have put the flags in some structure that's passed around anyway, like the symbol table? For example just invent some identifier names that's not legal Haskell and put the info there. Passing the flags around costs extra instructions (not many each time, but a lot of times). Furthermore, even the flag test is expensive. You can't pass 100 flags individually, so you pass a single value and do a lookup each time you want to test the flag. Contrast that with a global thunk that gets updated to a boolean the first time you use it. Maybe the symbol table isn't passed around to all dark corners though. Anyway, what it seems to me you lose by doing it the way you described is that you are stuck again if some day you want to set those flags some other way than from the environment variables. For example with pragmas. You can't compile several modules with different settings either, without restarting all of the compiler, it seems to me. Sverker
Re: Making argv a constant
| Maybe the symbol table isn't passed around to all dark corners though. Dead right it ain't. There are plenty of places you don't need a symbol table. | Anyway, what it seems to me you lose by doing it the way you described | is that you are stuck again if some day you want to set those flags | some other way than from the environment variables. For example with | pragmas. You can't compile several modules with different settings | either, without restarting all of the compiler, it seems to me. I regard this as an *advantage*. If I see import CmdLineOpts( myFlag ) f x = if myFlag then A else B then I *know* that every call to f will behave like A, or every call will behave like B. No chance that some may behave like one and some like the other. If I pass the flags as an argument, thus: f flags x = if (lookup "myFlag" flags) then A else B then I can draw no such conclusion. The point is, if I *want* to have several behaviours in one run I can always use this second technique. But Haskell as it now stands prevents me from using the first, even when I *don't* want several behaviours in one run. I'd be prepared to pay the pain if I wanted the benefit. Since I don't want the "benefit" I don't want the pain. This message has clarified that there are three (not two) reasons that argv should be constant 1. Easier programming 2. Faster running 3. Extra reasoning ability (this the new one; every call to f will behave the same way) Simon