Re: [Haskell-cafe] Monad of no `return` Proposal

littering the code with #ifdefs
Bonus points for keeping the #ifdefs centralized
Littering is the right word. "Bonus" is merely less negative points. It is ironic that the beautiful Haskell should progress by adopting the worst feature of C. #ifdef is a sticking-plaster for non-portable code. It is inserted at global level, usually to effect changes at the bottom of the code hierarchy. (Maybe in Haskell it should be a monad, in competition with IO for the outermost layer.) Plan 9 showed the way out of ifdef, by putting (non-ifdef-ed) inescapably nonportable code, such as endianity, in compilation units and #include files in a distinct part of the file system tree selected by the shell. Another trick is to use plain if rather than #if or #ifdef, and let the compiler optimize away the unwanted side of the branch. In any event, #ifdef is, as Simon evidently agrees, evidence

Doug McIlroy
littering the code with #ifdefs
Bonus points for keeping the #ifdefs centralized
Littering is the right word. "Bonus" is merely less negative points.
It is ironic that the beautiful Haskell should progress by adopting the worst feature of C. #ifdef is a sticking-plaster for non-portable code. It is inserted at global level, usually to effect changes at the bottom of the code hierarchy. (Maybe in Haskell it should be a monad, in competition with IO for the outermost layer.)
Plan 9 showed the way out of ifdef, by putting (non-ifdef-ed) inescapably nonportable code, such as endianity, in compilation units and #include files in a distinct part of the file system tree selected by the shell.
Another trick is to use plain if rather than #if or #ifdef, and let the compiler optimize away the unwanted side of the branch.
In any event, #ifdef is, as Simon evidently agrees, telling evidence of non-portability. It is hard to imagine an uglier "solution" to keeping up with the drip-drip-drip of Haskell evolution.
By the nature of the problem -- that is, because we want to isolate the compiler as much as possible -- the conditionalization must be performed as a separate pass. The conclusion is that some sort of pre-processor is inevitable. The remainder of the available choice space is about how ugly this pre-processor must be -- and indeed, CPP tends towards the undesirable end of the spectrum. As an example of the opposite end, consider Common Lisp, which: 1. reifies READ as the third processing phase (macroexpansion[1] and actual compilation being the other two) 2. operates at expression granularity 3. makes the full power of the language available to support the decision process of what subexpressions are available in which contexts As a rough example of how this works: http://clhs.lisp.se/Body/24_abaa.htm I don't know how much of Common Lisp preachery the list have suffered over the years, but I can't help but find this uniformity between the three parts of the language very appealing: - syntax-level of READ - macro-level of MACROEXPAND - semantics-level of the post-MACROEXPAND part of EVAL Maybe, just maybe, Haskell could borrow something from that.. -- с уважениeм / respectfully, Косырев Серёга -- 1. Macroexpansion is actually defined by the ANSI CL standard to be a part of minimal compbilation, but for the purposes of illustration it makes sense to distinguish between them.

On 6/10/2015, at 10:16 pm, Kosyrev Serge <_deepfire@feelingofgreen.ru> wrote:
As an example of the opposite end, consider Common Lisp, which:
1. reifies READ as the third processing phase (macroexpansion[1] and actual compilation being the other two) 2. operates at expression granularity 3. makes the full power of the language available to support the decision process of what subexpressions are available in which contexts
Except that the syntax for conditional reading in Common Lisp, #+ <feature> <datum> #- <feature> <datum> (a) is utterly different from conditional evaluation, like (if <expr> <expr> <expr>). (b) does NOT make the full power of the language available. The feature test is a Boolean combination, using and, or, not of atoms, where the value of an atom is true iff it is a member of *features* (the asterisks are part of the name).

"Richard A. O'Keefe"
On 6/10/2015, at 10:16 pm, Kosyrev Serge <_deepfire@feelingofgreen.ru> wrote:
As an example of the opposite end, consider Common Lisp, which:
1. reifies READ as the third processing phase (macroexpansion[1] and actual compilation being the other two) 2. operates at expression granularity 3. makes the full power of the language available to support the decision process of what subexpressions are available in which contexts
Except that the syntax for conditional reading in Common Lisp, #+ <feature> <datum> #- <feature> <datum>
(a) is utterly different from conditional evaluation, like (if <expr> <expr> <expr>). (b) does NOT make the full power of the language available. The feature test is a Boolean combination, using and, or, not of atoms, where the value of an atom is true iff it is a member of *features* (the asterisks are part of the name).
There are several issues mixed up here: 1. Desirability of expression-level granularity of pre-processing. 2. The desired palette of options for this pre-processing (if desired), that varies in expressive power. 3. The difference between #+/#- and #. (aka *READ-EVAL*, http://clhs.lisp.se/Body/v_rd_eva.htm) in Common Lisp. My original message was about point #1, but point #2 deserves a similar kind of attention. The point #3 is purely didactic -- the READ-time processing is layered in Common Lisp into: 1. the basic layer, covering 95% of cases, which uses the following primitives, to decide if an expression suffix needs to be included: - *FEATURES*, a variable containing a list of features - ability to test for presence of a certain symbol within *FEATURES* - ability to combine atomic tests using AND, OR, NOT It is, indeed, what is covered in the example that I have provided. 2. the full language layer, available through #. -- whatever is returned by the expression following #. is inserted as program text. This layered approach has some merit: - It provides a kind of frugal brevity, that is sufficient in 95% of cases - Whenever the soft option fails, it unleashes the whole language to express basically anything during that pre-processing phase. Except, in the latter case it /probably/ doesn't count as pre-processing anymore, since the inserted text is no longer external to the pre-processor directives. -- с уважениeм / respectfully, Косырев Серёга -- “And those who were seen dancing were thought to be insane by those who could not hear the music.” – Friedrich Wilhelm Nietzsche

On 6/10/2015, at 11:59 am, Doug McIlroy
Another trick is to use plain if rather than #if or #ifdef, and let the compiler optimize away the unwanted side of the branch.
The University of Edinburgh had their own systems implementation language, IMP. Fortran -> Algol 60 -> Atlas Autocode -> IMP. It was no surprise to find that %if <constant condition> %then <true part> %else <false part> eliminated whichever branch would not be executed. But it was astonishing to discover that %if <constant condition> %then %start <declarations T> %finish %else %start <declarations F> %finish would only process the declarations in the chosen part. If you want to eliminate the preprocessor, you have got to handle conditional declarations. Even IMP did not handle %record %format table ( ... %if keeping statistics %then %start %integer accesses, hits, misses, %endif ...) which is something that a preprocessor can handle.
participants (3)
-
Doug McIlroy
-
Kosyrev Serge
-
Richard A. O'Keefe