 |
 |
|
 |
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> foo(a, bar(b, c), d, e)
>
> If you wrote that as a Haskell-style no-delimiters function call,
> I suppose it would look like:
>
> foo a bar b c d e
> Exactly how does Haskell decide how many parameters 'bar' takes there
> (and consequently how many of the rest of the parameters are for 'foo')?
It's curried. Each function only takes one.
In your example, I'm pretty certain you *have* to have parens.
foo a (bar b c) d e
> I can't even begin to imagine how you would write that using the
> "point syntax", seen in many examples posted by Andrew. Perhaps you
> use parentheses to enclose the 'bar' call?
Hmmmm. I don't know you can do it with "point syntax" either. I think
the a . b . c syntax only works when the result of "c" is the one and
only argument to "b", and the result of "b" is the one and only argument
to "a".
So if you wanted a function that returns the sum of the lengths of a
list of strings, you might write
length . concat
or something, if "concat" was a function that took a list of strings and
returned their concatenation.
> There just is something about the Haskell paradigm and syntax that
> makes it confusing and hard to assimilate.
I think most of the powerful languages have syntax that's confusing and
hard to assimilate. C and C++ are also very confusing in syntax. (For
example, I can barely follow the reference-counting pointer code you
posted, and that only because I am only trying to read it and not write
it. :-)
Even Tcl, which has exactly 12 rules of syntax, winds up being confusing
to newbies because it's *too* simple. LISP has too many parentheses,
even tho the grammar is about 3 lines of BNF.
About the only languages that aren't confusing are the weak ones: BASIC,
Pascal, etc.
If the language is powerful because it does a lot (like Ada or C++) you
wind up with lots and lots of syntax and lots of rules about the
interactions. If the language is powerful because it's simple and
regular (like LISP or FORTH or Tcl or Haskell) then it's confusing
because there's no *obvious* way to accomplish the powerful stuff you
know the language is capable of. (Sort of like Go or Chess or something
- simple rules but very unobvious combinations.)
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Orchid XP v8 wrote:
> Warp wrote:
>> Wouldn't it be simpler to do it like:
>>
>> if(number == target) break;
>> if(number < target) std::cout << "Too low.\n";
>> else std::cout << "Too high.\n";
>
> Mmm, I guess that would also work...
The pendant in me wants to have an actual condition as the argument to
"while". :-) Or at least
do {
....
} while (number != target);
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New wrote:
> The pendant in me wants to have an actual condition as the argument to
> "while". :-) Or at least
>
> do {
> ....
> } while (number != target);
Yeah - although that would require "number" to be declared outside the
while loop so that it actually exists. ;-) More subtly, it would have to
be initialised to a value that is guaranteed to not equal "target". Ooo,
*that* could be a nasty bug to try to find...!
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Orchid XP v8 wrote:
> Darren New wrote:
>
>> The pendant in me wants to have an actual condition as the argument to
>> "while". :-) Or at least
>>
>> do {
>> ....
>> } while (number != target);
>
> Yeah - although that would require "number" to be declared outside the
> while loop so that it actually exists. ;-)
Yes, I think so.
> More subtly, it would have to
> be initialised to a value that is guaranteed to not equal "target". Ooo,
> *that* could be a nasty bug to try to find...!
No, because you're using a "do while" instead of just a "while". That
like "repeat until" in Pascal (except with a reversed boolean, of course).
And yes, you'd have to initialize the value, but that's all high-school
programming type stuff. Probably the compiler at this point would warn
you the variable isn't initialized.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Slime wrote:
>> I have a similar issue with stuff like
>>
>> while (*x++ = *y++) {}
>
> FYI, I have an issue with stuff like that too. C and C++ do make it easy to
> write bad code. This is the second time you've mentioned that example, so I
> assume someone once showed it to you and thought they were being clever.
> They weren't! Or maybe it's just a popular example, because I was just
> reading a book about coding style last night that gave that as a bad
> example.
The truth is that there is no shortage of programmers out there who
write like this. I've literally read a C (or was it C++) book that
admitted it was obfuscated, and in the same breath suggesting that the
reader should become accustomed to it because those constructs are common.
--
The meek shall inherit the dearth.
/\ /\ /\ /
/ \/ \ u e e n / \/ a w a z
>>>>>>mue### [at] nawaz org<<<<<<
anl
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> The above example code can thus be written as:
>
> while(true)
> {
> int value;
> std::cin >> value; // Try to read an integer
> if(std::cin.fail()) // If there was no integer in the input
> {
> std::cin.clear(); // Clear the error flags
> std::string s;
> std::cin >> s; // Read a string
> if(!std::cin.good()) break;
> std::cout << "The input was something else: " << s << "\n";
> }
> else
> {
> std::cout << "The input was an integer: " << value << "\n";
> }
> }
>
> Thus removing the need for stringstreams.
This doesn't appear to work. It waits for me to type a second token, and
then works with that. (Either that or my program has some other random
bug... If I ever figure out how to work VisualStudio's debugger I'll let
you know!)
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On Sun, 21 Sep 2008 16:24:37 +0200, Darren New <dne### [at] san rr com> wrote:
> The pendant in me
Don't worry. It will come out naturally in a few days...
--
FE
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Fredrik Eriksson wrote:
> On Sun, 21 Sep 2008 16:24:37 +0200, Darren New <dne### [at] san rr com> wrote:
>> The pendant in me
> Don't worry. It will come out naturally in a few days...
Normally, complaining about spelling is silly. In this case, tho...
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Orchid XP v8 <voi### [at] dev null> wrote:
> This doesn't appear to work. It waits for me to type a second token, and
> then works with that.
It works here. Did you try it with gcc?
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Orchid XP v8 <voi### [at] dev null> wrote:
>> This doesn't appear to work. It waits for me to type a second token, and
>> then works with that.
>
> It works here. Did you try it with gcc?
Just for completeness, yeah. Also didn't work. Unless I did something
"else" wrong with the code as well...
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Orchid XP v8 wrote:
> Now, if it was pretty-printed like THIS:
>
> http://hpaste.org/10565
>
> you wouldn't be as confused. (?) If you have some kind of text editor
> with syntax-hilighting, this isn't an issue.
So I'm guessing either nobody noticed this tucked away here, or it
doesn't make much difference. (Or nobody even cares anymore... o_O )
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 21-Sep-08 6:27, Darren New wrote:
> andrel wrote:
>> Indeed, but only if you assume you have a language that makes that
>> distinction.
>
> That's what I said.
>
>> Maths does not have 'reserved words'. I.e. for the casual observer
>> things like '=' and '+' looks as though they are universally defined
>> and understood the same way. In practice even these are sometimes
>> redefined to suit a specific field or application in a way that may
>> even go deeper than overloading.
>
> Correct. Except that given the programming language, reserved words are
> fundamentally different from functions or variables. That's why they're
> reserved.
>
> Kind of like parens in math - once you decide you have a grouping
> operator that's written ( ), you really can't use it to define functions
> called ( and ).
>
> The confusion, I think, is that you think maths doesn't have reserved
> words. It does. They're just outside of the maths.
I definitely think they exist, only that which ones are reserved and
what they actually mean can vary wildly from field to field. And yes, I
have seen a serious paper where they redefined '=' in a way that is
slightly incompatible with standard use (and with a good reason).
Perhaps you understood me a bit wrong. I am not arguing that you have
variables, constants, reserved words, functions or whatever in Haskell.
My only point was that on a fundamental level the difference between the
categories is so blurred that you should not expect a language to have
visual clues to distinguish between them. At least not for languages
based on an abstract concept. For languages designed to just give a
slightly abstract representation of current hardware and current
programming techniques that may be quite different.
> For example, the "summation function" is kind of the same as the for
> loop: start variable and value below the sigma, stop value above the
> sigma, expression to evaluate to the right of the sigma. The reserved
> words, in this case, is the English sentence I just wrote describing the
> functionality.
>
> Unfortunately, to communicate with the computer about how a compiler
> works, we need to speak something other than English. That's where the
> reserved words come in. They're a way for the compiler writer to let the
> compiler user communicate with the compiler. They are part of the syntax
> of a language that is implicit in the rules for evaluating the
> functions, just like (A^A)->C you can do a production to find that A->C
> because of the rules of pattern matching and substitution. It's
> virtually impossible (or even utterly impossible) to formalize the
> productions you use in your formalization.
>
>> want to model a language on the way mathematics is used, defining
>> 'reserved words' is something you might want to leave out.
>
> Granted. But Haskell doesn't model a language on the way mathematics is
> used. It models a language on one particular mathematics. The reserved
> words are a syntactic shortcut for a much larger lambda expression, just
> like "193" is a syntactic shortcut for a big long string of bits in a
> turing machine.
>
>>> Knowing what's an argument and what's a function is also pretty
>>> important for understanding how a given line of code works.
>>
>> Again absolutely true, for a imperative language. In lambda calculus
>> and any functional language derived from that this distinction make no
>> sense at all.
>
> Except that Haskell is obviously beyond lambda calculus in its
> implementation and meaning. See any of Andrew's postings about writing
> "2+2" in lambda calculus.
I did both a course on lambda calculus and one on functional programming
languages when I was at university. That was 20 years ago, but I don't
think lambda calculus and functional programming has changed a lot since
then. The point I was trying to make is that the fact that there is no
difference in syntax of functions and arguments in lambda calculus is so
fundamental that it is not a good idea to use these terms to analyse
Haskell.
>>> If you can't look at the code and figure out which function bodies
>>> get evaluated, it makes it really rough to work out what's going on,
>>> even in a functional language.
>>
>> I don't think so. If you have an entity called current_temperature it
>> should not matter if that is derived by looking at a specific memory
>> location (a variable), by looking at an index in a circular buffer
>> (array) or by doing something more complicated like calling a function
>> of generating an interrupt. OK, it does matter sometimes but only in
>> an implementation. It should not have any influence on understanding
>> the code conceptually.
>
> Sure. And if you have an entity called "o" or "+" or "case" or "start"?
yes?
>> I think you understood that my mean reason for my response was that
>> Warp is looking with imperative spectacles to lines of code in a
>> functional language and desperately trying to force it into his own
>> mindframe.
>
> I wouldn't go that far, but yes. But if you have a language that defines
> complex syntax (which Haskell does a little and C++ does a lot) and you
> can't even parse the syntax tree because you can't distinguish reserved
> words from variables or functions, then you must admit it's going to be
> difficult to read the code. Indeed, if you can't even tell where one
> expression ends and another starts, it can be very difficult to
> understand the code.
I don't think I ever claimed a program in Haskell or any functional
language is easy to understand. Nor can I easily understand a paper in
the field of string theory for that matter. That does not mean however
that people working in that field or with that language can see the
structure immediately. I know that, so I won't complain if I don't get
it myself. Just as that I won't tell a Korean that she should use our
alphabet because otherwise it won't make sense to me.
(Next time I'll snip unnecessary parts, I promise. Now I am just to tired.)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
andrel wrote:
> My only point was that on a fundamental level the difference between th
e
> categories is so blurred that you should not expect a language to have
> visual clues to distinguish between them. At least not for languages
> based on an abstract concept.
I'm just going to have to disagree here. I think any category that has
syntax[1] is going to have syntactic equivalents of reserved words.
[1] By which I mean to rule out things like FORTH and Tcl and such, much
of whose syntax is defined by the code itself rather than the compiler.
> For languages designed to just give a
> slightly abstract representation of current hardware and current
> programming techniques that may be quite different.
I don't think it has anything to do with how high-level it is. It has to
do with whether you can parse the syntax of the language. If "case" is
allowed to be a variable, and "->" is allowed to be a function, then
there's no way to write the code in http://hpaste.org/10565 and have it
make sense.
> The point I was trying to make is that the fact that there is no
> difference in syntax of functions and arguments in lambda calculus is s
o
> fundamental that it is not a good idea to use these terms to analyse
> Haskell.
And can you name a variable λ or . or ) or ( in the lambda calculus?
>> Sure. And if you have an entity called "o" or "+" or "case" or "start"
?
> yes?
How do you distinguish that variable name from the syntactic form that
"case" currently introduces in Haskell?
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez wrote:
> I understand the C++ one and the Haskell one looks like line noise.
I just found this in the Haskell humour section:
bos: come on, real programmers use "(((,) <$>) .) . (<*>)"
(It's an IRC quote.)
I wish to point out that I had to actually go look up some of those
operators just to find out what they do!
(,) takes two values and returns a 2-element tuple.
<$> is a generalised version of "map" (not just for lists).
. is function composition, as always.
<*> takes a collection of functions and a collection of values and
applies one to the other.
So that little expression has the type signature
(Applicative col) => col (x -> y) -> col x -> col (z -> (y, z))
which more or less tells you what it does (once you realise that "col"
means "a collection of"). Quite why this particular function would be
useful for anything is another matter...
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 22-Sep-08 2:19, Darren New wrote:
> andrel wrote:
>> My only point was that on a fundamental level the difference between
>> the categories is so blurred that you should not expect a language to
>> have visual clues to distinguish between them. At least not for
>> languages based on an abstract concept.
>
> I'm just going to have to disagree here. I think any category that has
> syntax[1] is going to have syntactic equivalents of reserved words.
I agree totally ;) . The point where we might disagree is that I think
that there are reserved words that are vital to the language ( e.g. your
'λ' and '.' and ')' and '(' of the snipped part below), reserved words
that are better left untouched because they are too common and it would
make code incomprehensible if redefined ('for', 'case' and other
constructs that could be substituted by other constructs) but also
things that are not reserved but could be (I'd like to have control flow
'if'/'fi' and 'do'/'od'[1] in C with Dijkstra's non deterministic guards
and I like them to behave as if they are reserved words). So for me the
concept of 'reserved words' is too fuzzy to merit special handling.
[1] with probably different names, because 'do' and 'if' already have a
meaning.
> [1] By which I mean to rule out things like FORTH and Tcl and such, much
> of whose syntax is defined by the code itself rather than the compiler.
I think that some functional and declarative languages can also be
included in your list. If we put Haskell on that list, I think we safely
conclude that we totally agree on everything in this thread.
>> For languages designed to just give a slightly abstract representation
>> of current hardware and current programming techniques that may be
>> quite different.
>
> I don't think it has anything to do with how high-level it is. It has to
> do with whether you can parse the syntax of the language. If "case" is
> allowed to be a variable, and "->" is allowed to be a function, then
> there's no way to write the code in http://hpaste.org/10565 and have it
> make sense.
Even if you allow total freedom, nothing prevents a community to impose
restrictions. E.g. on the 1802 all registers are totally equivalent, any
of the 16 registers can function as a numeric value, a stackpointer or a
program counter. Nonetheless the programcounter and stackpointer are
more or less fixed by convention. Indeed to prevent unreadable code.
Another, possibly unrelated, remark. I always though that π (pi) was a
reserved word throughout mathematics and physics. Until someone
introduced it as a generalized impulse (with arrow on top, but still). I
also know that once we had a formula of the energy of a particle in a
electric field. Something along the lines of:
e=e^(e/..)
where the first e is the energy, but lower case because it is per
particle. The second e is the conventional constant 2.71... and the
third is the unit charge 1.6e-19 coulomb (hmm, might have been e^2, it
is too long ago). Nobody had trouble parsing that.
[I promised to snip something, this part was the most likely candidate]
>>> Sure. And if you have an entity called "o" or "+" or "case" or "start"?
>> yes?
>
> How do you distinguish that variable name from the syntactic form that
> "case" currently introduces in Haskell?
>
you don't. you refrain from using 'case' as a variable by self
censoring. (and you refrain from introducing new reserved words in
future versions. Is anybody from Mathworks listening? I repeat, you don't.)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
andrel wrote:
>> I'm just going to have to disagree here. I think any category that has
>> syntax[1] is going to have syntactic equivalents of reserved words.
>
> I agree totally ;) . The point where we might disagree is that I think
> that there are reserved words that are vital to the language ( e.g. you
r
> 'λ' and '.' and ')' and '(' of the snipped part below), reserved
words
> that are better left untouched because they are too common and it would
> make code incomprehensible if redefined ('for', 'case' and other
> constructs that could be substituted by other constructs) but also
> things that are not reserved but could be (I'd like to have control flo
w
> 'if'/'fi' and 'do'/'od'[1] in C with Dijkstra's non deterministic guard
s
> and I like them to behave as if they are reserved words). So for me the
> concept of 'reserved words' is too fuzzy to merit special handling.
Sure, I agree. Reserved words should have a meaning that introduces synta
x.
In Tcl, there are what you might think of as "reserved words" like
braces and dollar signs and whitespace and newlines - things where
escaping them probably changes the meaning of what you wrote. Then
there's predefined functions, like "for" and "if" and "proc" (which
defines new procedures), which you'll wind up breaking everything if you
change the meaning too much.
But people rewrite things like "proc" all the time, to do things like
extend the syntax or add tracing or something like that. It's not a
reserved word if you can use it for something else.
> I think that some functional and declarative languages can also be
> included in your list. If we put Haskell on that list, I think we safel
y
> conclude that we totally agree on everything in this thread.
No, because you can't change Haskell syntax, as far as I know. You can't
name an operator :: and you can't name a function "case". They're
reserved. As far as I know, you can't make up new syntax for the
language, and you can't change the syntax it has. I may be wrong about
that.
>> I don't think it has anything to do with how high-level it is. It has
>> to do with whether you can parse the syntax of the language. If "case"
>> is allowed to be a variable, and "->" is allowed to be a function,
>> then there's no way to write the code in http://hpaste.org/10565 and
>> have it make sense.
>
> Even if you allow total freedom, nothing prevents a community to impose
> restrictions.
But that wouldn't be a "reserved word". You can't name a function in C
with the name "if", because then
if (x == 5) ;
would change meaning. That's *why* it's a reserved word. Not to avoid
confusion for the user, but to avoid confusion for the compiler.
> Nobody had trouble parsing that.
Right. Because the words serving the function of reserved words told you
which variable meant which. In math, those pseudo-reserved words are the
english telling you what symbols mean. If I just wrote
e = e ^ ( ... )
and didn't tell you verbally which field of measurements each "e"
belonged to, you wouldn't be able to understand what the equation means.
Math is an abbreviation for a whole lot of prose - you can't understand
a mathematical equation unless the rules are explained outside the
system of mathematics. The lambda calculus is meaningless until you
specify how the substitution rules work, and you can't specify that in
lambda calculus.
Computer programs aren't like that. You don't get to explain in English
what the compiler should do with your code.
>>>> Sure. And if you have an entity called "o" or "+" or "case" or "star
t"?
>>> yes?
>>
>> How do you distinguish that variable name from the syntactic form that
>> "case" currently introduces in Haskell?
>>
> you don't. you refrain from using 'case' as a variable by self
> censoring.
OK, maybe I misphrased it. How does the compiler distinguish the
variable name from the syntactic form that "case" currently introduces
in Haskell? If "you don't", then it means your compiler's behavior is
unspecified when you use the word "case" as a variable. People generally
don't like compiler behavior to be unspecified for valid programs.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
andrel wrote:
> you don't. you refrain from using 'case' as a variable by self
> censoring.
Or, to phrase it differently, it's not just a convention of
self-censoring that you can't name a function ) in the lambda calculus.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 22-Sep-08 22:40, Darren New wrote:
> andrel wrote:
[snip]
>> I think that some functional and declarative languages can also be
>> included in your list. If we put Haskell on that list, I think we
>> safely conclude that we totally agree on everything in this thread.
>
> No, because you can't change Haskell syntax, as far as I know. You can't
> name an operator :: and you can't name a function "case". They're
> reserved. As far as I know, you can't make up new syntax for the
> language, and you can't change the syntax it has. I may be wrong about
> that.
My point was always the other way around: does Haskell allow you to
introduce concepts and control structures that from that point on will
become conceptually 'part of the language' for you? To the extent that
for you they behave like 'reserved words'. In the sense that overriding
them will result in mayhem everywhere, not least within your own brain.
I think it does.
[snip]
>>>>> Sure. And if you have an entity called "o" or "+" or "case" or
>>>>> "start"?
>>>> yes?
>>>
>>> How do you distinguish that variable name from the syntactic form
>>> that "case" currently introduces in Haskell?
>>>
>> you don't. you refrain from using 'case' as a variable by self censoring.
>
> OK, maybe I misphrased it. How does the compiler distinguish the
> variable name from the syntactic form that "case" currently introduces
> in Haskell? If "you don't", then it means your compiler's behavior is
> unspecified when you use the word "case" as a variable. People generally
> don't like compiler behavior to be unspecified for valid programs.
Normal scoping rules may apply.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 22-Sep-08 22:42, Darren New wrote:
> andrel wrote:
>> you don't. you refrain from using 'case' as a variable by self censoring.
>
> Or, to phrase it differently, it's not just a convention of
> self-censoring that you can't name a function ) in the lambda calculus.
>
no of course not. You can not even define a function '0' or '+" or
'increment'. i.e. you can define things that behave that way, given some
interpretation, but you can not name them as such.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
andrel wrote:
> My point was always the other way around: does Haskell allow you to
> introduce concepts and control structures that from that point on will
> become conceptually 'part of the language' for you?
No, I don't think it does. I don't think it's possible to write "case"
or "let" in Haskell. That's kind of the point. I think you're taking
"reserved words" to mean the same as "meaningful concepts." I'm talking
from the point of view of someone building a compiler or something, not
from someone conceptualizing about an application.
> To the extent that
> for you they behave like 'reserved words'.
I don't care what it behaves like in my brain. I care what happens when
it compiles.
>> OK, maybe I misphrased it. How does the compiler distinguish the
>> variable name from the syntactic form that "case" currently introduces
>> in Haskell? If "you don't", then it means your compiler's behavior is
>> unspecified when you use the word "case" as a variable. People
>> generally don't like compiler behavior to be unspecified for valid
>> programs.
>
> Normal scoping rules may apply.
Then it wouldn't be a reserved word, would it? :-)
I don't know if Haskell actually disallows the use of variables named
the same as "reserved words", but if it doesn't, then that's a reserved
word.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 22-Sep-08 23:16, Darren New wrote:
> andrel wrote:
>> My point was always the other way around: does Haskell allow you to
>> introduce concepts and control structures that from that point on will
>> become conceptually 'part of the language' for you?
>
> No, I don't think it does. I don't think it's possible to write "case"
> or "let" in Haskell. That's kind of the point. I think you're taking
> "reserved words" to mean the same as "meaningful concepts." I'm talking
> from the point of view of someone building a compiler or something, not
> from someone conceptualizing about an application.
>
>> To the extent that for you they behave like 'reserved words'.
>
> I don't care what it behaves like in my brain. I care what happens when
> it compiles.
OK, so there is where we part. I don't want to think about hardware or
compiler issues when I am solving a problem. (except when I end up
writing a compiler of course). I want to express my thoughts in as clear
a way as possible. If the language does not give me the freedom to
express myself as I think is the most clear, I'll try to extend the
language. Just as when I am doing math and I feel that I should need to
introduce a new syntax. Standard and new syntax will form my new
framework. Whatever someone else at any one time though were his
fundamental concepts is immaterial to me.
>
>>> OK, maybe I misphrased it. How does the compiler distinguish the
>>> variable name from the syntactic form that "case" currently
>>> introduces in Haskell? If "you don't", then it means your compiler's
>>> behavior is unspecified when you use the word "case" as a variable.
>>> People generally don't like compiler behavior to be unspecified for
>>> valid programs.
>>
>> Normal scoping rules may apply.
>
> Then it wouldn't be a reserved word, would it? :-)
>
> I don't know if Haskell actually disallows the use of variables named
> the same as "reserved words", but if it doesn't, then that's a reserved
> word.
>
Let me reiterate, I don't even care if Haskell allows it or not. The
discussion was if Haskell should visually make the distinction or not.
To which my answer is no, because what is and what isn't a reserved word
is too arbitrary. i.e. some are legitimate reserved (most of your
examples are from that stock), some are reserved but a case could be
made to relieve them of that status and some are not reserved, but might
be. So fuzziness all over, hence: do not treat them visually different.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > There just is something about the Haskell paradigm and syntax that
> > makes it confusing and hard to assimilate.
> I think most of the powerful languages have syntax that's confusing and
> hard to assimilate. C and C++ are also very confusing in syntax. (For
> example, I can barely follow the reference-counting pointer code you
> posted, and that only because I am only trying to read it and not write
> it. :-)
OTOH, I assume his code was something relatively simple. Something you
would write when you are creating a simple and straightforward program.
My reference counting pointer code uses rather advanced techniques
(related to, among other things, how allocators are used and how templates
behave) which are quite unusual in "normal" C++.
One good thing in C++ is that usually you can *hide* all that overly
complicated and advanced code behind a simple and nice public interface,
so that the code which *uses* that code can be simple, straightforward
and easy to understand.
With the Haskell examples I often get the feeling that the code which
I have hard time understanding is code which you write normally, when
implementing your main code, not some advanced techniques you hide behind
a nice module interface.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> OTOH, I assume his code was something relatively simple. Something you
> would write when you are creating a simple and straightforward program.
It was "guess a number from 1 to 100, and I'll tell you if you're too
high or too low." I don't imagine it would be any more straightforward
in C or C++, really. Sure, there were a couple of somewhat odd lines
that you had to rely on English to understand, like
case compare number target of
But I found that pretty easy to understand given what the rest of the
program was doing. I might not have been able to write it or tell you if
it was wrong/buggy, but I could read it OK.
Throw in the syntax of printf() or "cout << std::eoln" or whatever, and
I think the syntax in something like C++ is certainly something you need
to learn to read.
There are some languages (like Ada, for example) that do all that C++
does and more, but which use spelled-out words. That doesn't help too
much when you don't know what the words mean. Telling you it's a limited
tagged access type doesn't really tell you what it means any more than
void xyz(void) = 0;
is comprehensible without knowing what it means.
> My reference counting pointer code uses rather advanced techniques
> (related to, among other things, how allocators are used and how templates
> behave) which are quite unusual in "normal" C++.
OK. Question: I think I've figured out what some of your advice means...
When you say "don't use new", are you really saying "use the calls to
new that are in the libraries, and you shouldn't have to do that
yourself"? Or are you really saying "C++ programs rarely need to execute
the 'new' operator"?
The former I can understand. The latter would seem really confusing to
me, and I've been interpreting it as the latter, methinks.
> One good thing in C++ is that usually you can *hide* all that overly
> complicated and advanced code behind a simple and nice public interface,
> so that the code which *uses* that code can be simple, straightforward
> and easy to understand.
Welll..... There's still a bunch of syntax. Template instantiation, <<
and >> for I/O, etc. I mean, compare Haskell add-up-a-list-of-numbers
to C++'s. Something like
fold (+) my_list
to something like
for (j = std::list.begin(); j != std::list.end(); j = j.next())
i += j.value;
or some such. :-)
> With the Haskell examples I often get the feeling that the code which
> I have hard time understanding is code which you write normally, when
> implementing your main code, not some advanced techniques you hide behind
> a nice module interface.
Agreed. I just think it's hard for you for a different reason than C++
is hard for Andrew. Haskell is hard to read because there's a lot of
powerful stuff going on without enough syntax. C++ is hard to read
because there's a lot of powerful stuff going on with too much syntax.
If you're used to a lot of syntax, C++ might be easier to read than
Haskell, and vice versa.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
andrel wrote:
> OK, so there is where we part. I don't want to think about hardware or
> compiler issues when I am solving a problem.
Me neither. But someone has to. If you're using a language with reserved
words, you're going to have to avoid them.
> Just as when I am doing math and I feel that I should need to
> introduce a new syntax. Standard and new syntax will form my new
> framework. Whatever someone else at any one time though were his
> fundamental concepts is immaterial to me.
Yep. Then you should be using FORTH or LISP or Tcl or something like
that, right? What language *do* you prefer? There aren't very many that
let you introduce new syntax to the language.
In any case, you're still going to have to explain the new syntax, which
means you're going to have to use a language comprehensible to your
listeners. You can't just make up a new language from scratch and expect
to be able to communicate with someone else, even if that other person's
brain is flexible enough to learn it if taught it.
> Let me reiterate, I don't even care if Haskell allows it or not. The
> discussion was if Haskell should visually make the distinction or not.
> To which my answer is no, because what is and what isn't a reserved word
> is too arbitrary. i.e. some are legitimate reserved (most of your
> examples are from that stock), some are reserved but a case could be
> made to relieve them of that status and some are not reserved, but might
> be. So fuzziness all over, hence: do not treat them visually different.
OK. I'd say I'd want them treated differently if they actually are
reserved. I.e., if I have a variable called "case", I'd want to see the
statement starting
case case of
to have two different color words, for example.
I take it you don't like syntax coloring in your editor?
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> OK. Question: I think I've figured out what some of your advice means...
> When you say "don't use new", are you really saying "use the calls to
> new that are in the libraries, and you shouldn't have to do that
> yourself"? Or are you really saying "C++ programs rarely need to execute
> the 'new' operator"?
> The former I can understand. The latter would seem really confusing to
> me, and I've been interpreting it as the latter, methinks.
It was not a generic advice for making C++ programs. It was an advice
for a *complete beginner* who is starting to learn the basics of the
language.
It is obvious that at some point a C++ programmer will have to deal
with 'new' and memory management. However, IMO that can be left for later.
First get to know the program without the dirty details.
Of course using managed memory is a good advice for all C++ programmers,
beginners and experts. Even experts should avoid raw pointers and 'new',
unless there's a good reason not to. When you *must* use them, then it's
good to follow certain programming practices.
> > One good thing in C++ is that usually you can *hide* all that overly
> > complicated and advanced code behind a simple and nice public interface,
> > so that the code which *uses* that code can be simple, straightforward
> > and easy to understand.
> Welll..... There's still a bunch of syntax. Template instantiation, <<
> and >> for I/O, etc. I mean, compare Haskell add-up-a-list-of-numbers
> to C++'s. Something like
> fold (+) my_list
> to something like
> for (j = std::list.begin(); j != std::list.end(); j = j.next())
> i += j.value;
> or some such. :-)
At least the latter states more explicitly what you are doing.
(And by the way, you advance an iterator with the ++ operator.)
In the haskell case, it's not at all clear what you are doing. In fact,
I don't even think that your haskell example is correct. I can't find the
function "fold", but the closest thing I can find is the (rather unclearly
named): http://www.zvon.org/other/haskell/Outputprelude/foldl1_f.html
It seems that, as the name clearly implies, the difference between
foldl and foldl1 is that the first one applies the function to the
second parameter and the first element of the list, then applies the
function to the result and the second element of the list, and so on,
while foldl1 there's only a function and a list and the first element
of the list is now in the role of the second parameter to foldl.
I think it would be perfectly possible to simulate the same thing in C++
with something like:
int result = foldl1(plus, intContainer);
with proper implementations of the 'foldl1' and 'plus' templates. The
foldl function would be equally easy:
int result = foldl(plus, 64, intContainer);
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> It was not a generic advice for making C++ programs. It was an advice
> for a *complete beginner* who is starting to learn the basics of the
> language.
Oh, I see. Well, the handling of assignment and copy constructors is the
only bit that is particularly confusing to me, language-wise. I may not
be an expert, but conceptually that's the only concepts I don't think I
have down.
>> fold (+) my_list
>> to something like
>> for (j = std::list.begin(); j != std::list.end(); j = j.next())
>> i += j.value;
>> or some such. :-)
>
> At least the latter states more explicitly what you are doing.
Ehn. The call to fold does too, if you know its definition. Just like if
you don't know what std::list.begin() does, you're not going to
understand the C++. Basically, the Haskell line is "put plus signs
between the elements of my_list and evaluate the result."
> In the haskell case, it's not at all clear what you are doing. In fact,
> I don't even think that your haskell example is correct. I can't find the
> function "fold", but the closest thing I can find is the (rather unclearly
> named): http://www.zvon.org/other/haskell/Outputprelude/foldl1_f.html
Well, "fold" is the mathematical term. If you worry about associativity
and such, then you need foldl and foldr, and if you worry about what the
identity for the operator is, then you use the foldl1 and such. I guess
Haskell, unlike (say) Erlang, doesn't alias "fold" to one of those.
> It seems that, as the name clearly implies, the difference between
> foldl and foldl1 is that the first one applies the function to the
> second parameter and the first element of the list, then applies the
> function to the result and the second element of the list, and so on,
> while foldl1 there's only a function and a list and the first element
> of the list is now in the role of the second parameter to foldl.
Yep! That's basically it. But in functional languages, things like fold
and map and apply and such are used so often, they're as much an idiom as
for (i = 0; i < j; i++)
is in C.
> I think it would be perfectly possible to simulate the same thing in C++
> with something like:
>
> int result = foldl1(plus, intContainer);
>
> with proper implementations of the 'foldl1' and 'plus' templates. The
> foldl function would be equally easy:
>
> int result = foldl(plus, 64, intContainer);
Yep. I think it could very easily be a function in C++, or even C if you
want to specialize it every time.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> In the haskell case, it's not at all clear what you are doing. In fact,
> I don't even think that your haskell example is correct. I can't find the
> function "fold", but the closest thing I can find is the (rather unclearly
> named): http://www.zvon.org/other/haskell/Outputprelude/foldl1_f.html
>
> It seems that, as the name clearly implies, the difference between
> foldl and foldl1 is that the first one applies the function to the
> second parameter and the first element of the list, then applies the
> function to the result and the second element of the list, and so on,
> while foldl1 there's only a function and a list and the first element
> of the list is now in the role of the second parameter to foldl.
printf() is short for "print formatted".
foldl is "fold-left", while foldr is "fold-right". Similarly foldl1 and
foldr1 only work for non-empty lists (and thus don't require a start
value; foldr returns the start value unaltered in the case of an empty
list). More subtle is foldl', which causes the subtotal to *actually
compute* at each step, rather than just building a giant expression
which is only evaluated right at the end.
The other name for a fold is "catamorphism", but we won't worry about
that. ;-)
You can use folds for all kinds of interesting things. E.g., "foldr1
max" finds the highest element of a list, and "foldr (++) []" takes a
list of lists and returns a flatterned list. A huge number of standard
list processing functions are or theoretically can be defined as folds.
For example:
filter p = foldr (++) [] . map (\x -> if p x then [x] else [])
That is, take a list. For every element where "p" returns true, make a
1-element list, otherwise make an empty list. Now just the list of lists
together...
Map itself can be similarly defined, as can reverse and lookup. In fact,
just about any process that involves linearly traversing a list (i.e.,
almost all list processing!) can be implemented as a fold. This is quite
possible not the most *efficient* implementation, but it demonstrates
the power of the abstraction.
Many other Haskell datatypes also have "fold" functions too. It's a
common idiom.
> I think it would be perfectly possible to simulate the same thing in C++
> with something like:
>
> int result = foldl1(plus, intContainer);
>
> with proper implementations of the 'foldl1' and 'plus' templates. The
> foldl function would be equally easy:
>
> int result = foldl(plus, 64, intContainer);
It sounds plausible to me...
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
>> Welll..... There's still a bunch of syntax. Template instantiation, <<
>> and >> for I/O, etc. I mean, compare Haskell add-up-a-list-of-numbers
>> to C++'s. Something like
>> fold (+) my_list
>> to something like
>> for (j = std::list.begin(); j != std::list.end(); j = j.next())
>> i += j.value;
>> or some such. :-)
>
> At least the latter states more explicitly what you are doing.
> (And by the way, you advance an iterator with the ++ operator.)
Yes, but imagine a conversation between two people. One of them says to
the other:
1. Take a list of numbers.
2. Take the first element.
3. Add it to the variable i.
4. If you're not at the end of the list, take the next number and repeat
step 3.
As opposed to what Darren said:
"Take all the numbers, put a plus sign between them, and give me the
result".
I think the name functional programming is very apt. The latter is
placing more emphasis on the "function" that we want (combining all the
numbers to get a result).
In Python, we have reduce, which is a simpler form of foldl (and
perhaps a better name). Guido hates map, filter and reduce, but I love
them. Sometimes I use a list comprehension or even an explicit for loop.
But if in my mind it's "obvious" that what I want to do is a map or a
reduce, then I write it that way.
The reality is we don't *always* think procedurally. I'm sure even you
in your head do occasionally think functionally. It's just that you're
so used to converting that into a procedure that you don't notice it.
--
He's got a magnet!!! Everybody BACKUP!!!!!!!!
/\ /\ /\ /
/ \/ \ u e e n / \/ a w a z
>>>>>>mue### [at] nawaz org<<<<<<
anl
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Mueen Nawaz wrote:
> In Python, we have reduce, which is a simpler form of foldl (and
> perhaps a better name).
...except that the result of a fold can be another list. (And possible
one that's larger than the original.)
> The reality is we don't *always* think procedurally. I'm sure even
> you in your head do occasionally think functionally.
Indeed.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On Tue, 23 Sep 2008 00:20:42 +0200, Darren New <dne### [at] san rr com> wrote:
> Welll..... There's still a bunch of syntax. Template instantiation, <<
> and >> for I/O, etc. I mean, compare Haskell add-up-a-list-of-numbers
> to C++'s. Something like
> fold (+) my_list
> to something like
> for (j = std::list.begin(); j != std::list.end(); j = j.next())
> i += j.value;
> or some such. :-)
'fold' (should probably be 'foldl1') is just a library function, not a
language primitive. The corresponding C++ library function is
'std::accumulate'.
std::accumulate( my_list.begin(), my_list.end(), 0 );
--
FE
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Fredrik Eriksson <fe79}--at--{yahoo}--dot--{com> wrote:
> 'fold' (should probably be 'foldl1') is just a library function, not a
> language primitive. The corresponding C++ library function is
> 'std::accumulate'.
> std::accumulate( my_list.begin(), my_list.end(), 0 );
Which got me thinking: Does foldl1 in haskell work for data containers
other than lists? What if you want to apply it to only a sub-range inside
the data container rather than to the entire container? (Can you do it
without copying the sub-range to a separate container?)
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 23-Sep-08 0:45, Darren New wrote:
> andrel wrote:
>> OK, so there is where we part. I don't want to think about hardware or
>> compiler issues when I am solving a problem.
>
> Me neither. But someone has to. If you're using a language with reserved
> words, you're going to have to avoid them.
>
>> Just as when I am doing math and I feel that I should need to
>> introduce a new syntax. Standard and new syntax will form my new
>> framework. Whatever someone else at any one time though were his
>> fundamental concepts is immaterial to me.
>
> Yep. Then you should be using FORTH or LISP or Tcl or something like
> that, right? What language *do* you prefer?
Ahumm, I mostly use Matlab ATM. That does not mean that I like its
syntax, it is just my swiss army knife. For things that should be
maintained we use our MWEB system (Knuth's WEB system modified for
Matlab). That gives a slighly more expressive system. If we want to we
can use greek variables with subscripts.
I did like FORTH, precisely for this reason. Did I ever bore you with
my multitasking FORTH on the C64? You can't use it for things that have
to be readable for others. Apparently Andy understands reversed polish,
but you can't count on that for the average modern student.
I liked Prolog and lambda calculus and functional languages. And
probably not as a surprise, I like Dijkstra's guarded commands, mostly
because I don't have a compiler for that*. Must check if there is one
perhaps.
*) Just kidding of course, it is because it is closer to predicate
calculus that any other imperative language.
> There aren't very many that let you introduce new syntax to the language.
Afraid so. Lots of stupid graphical interfaces but the use of Lexx and
Yacc seems to be yet another art lost in the mists of time :(
Just as that you have a number of symbolic formula manupilating programs
but non that I could find that let you apply predicate transformers on
complicated boolean expressions interactively.
I think I am somewhat out of touch with 'modern' computer science.
> In any case, you're still going to have to explain the new syntax, which
> means you're going to have to use a language comprehensible to your
> listeners. You can't just make up a new language from scratch and expect
> to be able to communicate with someone else, even if that other person's
> brain is flexible enough to learn it if taught it.
Sure.
>> Let me reiterate, I don't even care if Haskell allows it or not. The
>> discussion was if Haskell should visually make the distinction or not.
>> To which my answer is no, because what is and what isn't a reserved
>> word is too arbitrary. i.e. some are legitimate reserved (most of your
>> examples are from that stock), some are reserved but a case could be
>> made to relieve them of that status and some are not reserved, but
>> might be. So fuzziness all over, hence: do not treat them visually
>> different.
>
> OK. I'd say I'd want them treated differently if they actually are
> reserved. I.e., if I have a variable called "case", I'd want to see the
> statement starting
> case case of
> to have two different color words, for example.
>
> I take it you don't like syntax coloring in your editor?
>
Of course I do. I just want to be able to add a few things myself. My
point is still the other way around ;)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Which got me thinking: Does foldl1 in haskell work for data containers
> other than lists?
No. The function specified in the language standard works only for lists.
However, there *is* an alternate version in one of the libraries that
does work for any suitable container. (Confusingly, it has the same
name, even though it's a different function.) Almost all container
libraries supply their own home-grown fold function, which may or may
not have the same name and/or type signature.
This is something of a known problem. A container like lists can hold
*any* type of data, but the Set container can only hold "ordered" data.
We have yet to reach a concensus about how to encode this into the type
system. (Dependent types look like the future, but that's an
experimental new feature that isn't part of any official standard, etc.)
> What if you want to apply it to only a sub-range inside
> the data container rather than to the entire container? (Can you do it
> without copying the sub-range to a separate container?)
Notionally, no.
Physically? Ask the optimiser.
(Remember, your "container" might not actually *exist* in its entirity
until you try to "do" something with it...)
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Orchid XP v8 <voi### [at] dev null> wrote:
> > What if you want to apply it to only a sub-range inside
> > the data container rather than to the entire container? (Can you do it
> > without copying the sub-range to a separate container?)
> Notionally, no.
> Physically? Ask the optimiser.
One thing which bothers me with haskell is the amount of features which
rely solely on the compiler being able to optimize them. For example a
simple thing like "go through this subrange inside this list and calculate
the sum of the elements", which you usually do in-place, becomes something
which at the language level requires copying (needlessly) the subrange just
so that you can traverse it, and hoping that the compiler will be able to
figure out what is it that you are really doing and optimizing the copying
away.
In other words, you would have to know the internal workings and
capabilities of the specific compiler you are using in order to know
if you can do it or if you have to do it by other more difficult means
(if copying the range of elements would be way too heavy and memory
consuming to be feasible).
I don't know... This kind of defeats the whole purpose of a high-level
language where you are supposed to not to have to worry about internal
implementation details of the compiler.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> I don't know... This kind of defeats the whole purpose of a high-level
> language where you are supposed to not to have to worry about internal
> implementation details of the compiler.
I think you could do it the same way you do it in C, if you need to:
iterate over the list and add up only the elements you're interested in.
fold goes from yadda.begin() to yadda.end(). If you don't want to do
that, use a different function. Or write it more iteratively, so to
speak. Think about your C++ implementation of fold - you would have to
copy the list there too if you wanted to use that on a subset.
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
>> Notionally, no.
>
>> Physically? Ask the optimiser.
>
> One thing which bothers me with haskell is the amount of features which
> rely solely on the compiler being able to optimize them.
You're used to C++, where what you tell the computer to do is *exactly*
what the computer does. (Or very close to it.)
The C++ way means that if you're clever, you can write very performant
code. The Haskell way means that if the compiler suddenly gets smarter,
your existing code speeds up. (Although the fact that it's speeding up
implies that it was slower than necessary before.)
> For example a
> simple thing like "go through this subrange inside this list and calculate
> the sum of the elements", which you usually do in-place, becomes something
> which at the language level requires copying (needlessly) the subrange just
> so that you can traverse it, and hoping that the compiler will be able to
> figure out what is it that you are really doing and optimizing the copying
> away.
In this specific case, I'm particularly confident that the compiler can
work it out. (But - as you point out - that's because I have some idea
about how this is working internally.)
> I don't know... This kind of defeats the whole purpose of a high-level
> language where you are supposed to not to have to worry about internal
> implementation details of the compiler.
To an extent, yes.
I think of it as being somewhat like SQL. If you write some random chunk
of SQL, you can know what the result will be. But how will this result
be computed? Well, *that* would depend on exactly which database product
from which vendor with which configuration settings, what indexes have
been built, what the size of the tables is, how the optimiser has been
configured...
Now, if it's your job to make sure certain critical queries run fast,
you will know the particular database you're using and what to do to the
query to speed it up. You'll look at query plans to see what the
database is doing, and what might be worth changing. (GHC allows you to
do something conceptually similar.)
Does having to rephrase an SQL query to make it faster "defeat the
purpose of SQL"? Not so much. It's unfortunate that it's necessary, but
it doesn't stop SQL from being a hugely useful thing.
If you want to write performant code in C++, you had better go learn
about call-by-name vs call-by-reference and how virtual methods are
implemented and a whole bunch of stuff if you expect your code to be
performant. C++ doesn't pretend otherwise. In Haskell, you need to have
some idea how the abstractions you're playing with are implemented too.
Typically you don't need a really in-depth technical understanding, but
you do need to have some clue how the machine is doing this stuff.
I guess if you're writing something where efficiency is the absolute
maximum priority, Haskell perhaps isn't the best possible choice. But
for the kind of code *I* write, the program is so complicated
algorithmically that getting it to *work properly* is the top priority.
(Ever heard the phrase "implement first, optimise second"?) So maybe
it's just that you and I write different code. My code tends not to be
performance-critical.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Actually, I have a question...
The C++ tutorial I'm reading has a section on how to inline functions.
Now *clearly* the compiler cannot inline something that it can't *find*.
So all that stuff about putting code into header files if you want it
inlined across compilation units is a necessary part of the way a C++
compiler works. But does C++ ever inline functions within *the same*
compilation unit automatically? Or does it literally refuse to inline
every function that doesn't say "inline" on it?
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> fold goes from yadda.begin() to yadda.end(). If you don't want to do
> that, use a different function. Or write it more iteratively, so to
> speak. Think about your C++ implementation of fold - you would have to
> copy the list there too if you wanted to use that on a subset.
Or I could simply make an overloaded version of the fold function which
takes an iterator range.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Invisible <voi### [at] dev null> wrote:
> The C++ tutorial I'm reading has a section on how to inline functions.
> Now *clearly* the compiler cannot inline something that it can't *find*.
> So all that stuff about putting code into header files if you want it
> inlined across compilation units is a necessary part of the way a C++
> compiler works. But does C++ ever inline functions within *the same*
> compilation unit automatically? Or does it literally refuse to inline
> every function that doesn't say "inline" on it?
Whether the compiler will inline a function (from which it has the
implementation at that point) is unspecified and completely up to the
compiler. The 'inline' keyword doesn't really matter in this. The
compiler may well inline a function which does not have the 'inline'
keyword, and it may well not inline a function which has it. In a way,
the 'inline' keyword is a no-op, at least with respect to actual inlining.
It's not a no-op keyword overall, because it controls how the linker
behaves with that function. If the linker encounters the same 'inline'
function in more than one object file, it will use only one of them and
make all the code which calls that function to use that one.
If you put a function implementation in a header file, you must make
it 'inline' to avoid linker errors (if the header file is used in more
than one compilation unit). If you don't make it 'inline', you are
effectively instantiating the same function in more than one compilation
unit, and the linker considers this to be an error (name collision of
two different functions).
(Btw, template functions are implicitly 'inline', so the keyword doesn't
have to be used with them.)
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Whether the compiler will inline a function (from which it has the
> implementation at that point) is unspecified and completely up to the
> compiler. The 'inline' keyword doesn't really matter in this.
Right. So adding an inline function to a header file means that the
compiler *could* inline it across compilation units (which would
otherwise be impossible), but ultimately it's up to the compiler to
decide what it will or won't inline?
I can certainly see why the standard doesn't mandate anything either
way. Do you happen to know the "typical" behaviour of any well-known C++
compilers? (Presumably it varies depending on the optimisation level you
select...)
> (Btw, template functions are implicitly 'inline', so the keyword doesn't
> have to be used with them.)
That makes sense.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Or I could simply make an overloaded version of the fold function which
> takes an iterator range.
Now what happens if you want to only process elements having some
arbitrary property? (E.g., only the odd numbers.) Or what if the
function you're trying to fold is non-associative, and you want to
rearrange the list so the elements can be processed in a different order?
The idea behind Haskell's data processing function is that each function
does *one* job, which makes it possible to combine them in any possible
way. (This is not unlike the Unix idea of implementing complex
functionallity by "piping" data between multiple simple utility
programs, as seen in millions of shell scripts.) As soon as you start
trying to rely on particular pre-combined functions, you lose that
flexibility.
With lists in particular, the new stream-fusion library means that most
if not all common combinations of list processing functions get
automatically fused together by the compiler.
There used to be a function called "concatMap", which does the same
thing as "concat . map", but more efficiently. It's actually still there
for compatibility, but with stream fusion you don't actually need it any
more [because "concat . map" gets optimised into the same thing].
If you really need killer performance on a critical region of your
program... well actually, in that case you probably wouldn't be using
lists anyway, but still... if you really need killer performance
somewhere, you can manually fuse if the compiler somehow fails to do it
for you. But otherwise, the code is much more maintainable if you don't.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Invisible <voi### [at] dev null> wrote:
> Warp wrote:
> > Or I could simply make an overloaded version of the fold function which
> > takes an iterator range.
> Now what happens if you want to only process elements having some
> arbitrary property? (E.g., only the odd numbers.)
I make the function take a functor which tells which elements to process.
This is a standard technique in C++.
> Or what if the
> function you're trying to fold is non-associative, and you want to
> rearrange the list so the elements can be processed in a different order?
Then rearrange the list as you like and give it to the function?
> If you really need killer performance on a critical region of your
> program... well actually, in that case you probably wouldn't be using
> lists anyway, but still...
There are algorithms for which linked lists are the most efficient
data container. You still don't want to be needlessly copying the elements
around, though.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Invisible <voi### [at] dev null> wrote:
> I can certainly see why the standard doesn't mandate anything either
> way. Do you happen to know the "typical" behaviour of any well-known C++
> compilers? (Presumably it varies depending on the optimisation level you
> select...)
Compilers use some kind of heuristic to determine whether it's beneficial
to inline some function or not. What those heuristics are, I don't know.
From the programmer's point of view, it doesn't really matter. The program
will behave identically in either case (except maybe a small difference in
execution speed).
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
>> Now what happens if you want to only process elements having some
>> arbitrary property? (E.g., only the odd numbers.)
>
> I make the function take a functor which tells which elements to process.
> This is a standard technique in C++.
Right. And now what if the elements to process depends on the
surrounding context and you have to make several passes of the list to
identify them?
We're missing my point though. My point is that you can't write a
specialised implementation for every possible combination of list
processing operations. But with stream fusion, the compiler can generate
those for you - and yet the source code stays easily readable and
modifiably.
> There are algorithms for which linked lists are the most efficient
> data container. You still don't want to be needlessly copying the elements
> around, though.
Agreed.
This is why people have spent years doing research and coming up with
sophisticated optimisation techniques. :-)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Compilers use some kind of heuristic to determine whether it's beneficial
> to inline some function or not. What those heuristics are, I don't know.
>
> From the programmer's point of view, it doesn't really matter. The program
> will behave identically in either case (except maybe a small difference in
> execution speed).
Right. Well I guess that more or less answers my question. I was just a
little worried that maybe if you don't explicitly say "inline", the
compiler will never attempt to inline it and hence introduce way too
much overhead. It seems that's not the case...
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> fold goes from yadda.begin() to yadda.end(). If you don't want to do
>> that, use a different function. Or write it more iteratively, so to
>> speak. Think about your C++ implementation of fold - you would have to
>> copy the list there too if you wanted to use that on a subset.
>
> Or I could simply make an overloaded version of the fold function which
> takes an iterator range.
Which is what I was suggesting be done with Haskell, when I said "write
a more iterative version". Pass in a start index and a length, walk down
the list "start" elements, apply the operator to "length" elements,
return the result. If you don't want to copy the list, don't separate
out the function of copying the subset from the function of applying the
operator. (I don't know, Haskell might even be smart enough to lazily do
the Right Thing there even if you do it naively.)
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New wrote:
> Which is what I was suggesting be done with Haskell, when I said "write
> a more iterative version".
Yeah, if it bothers you that much, you could manually say something like
my_fold fn x0 i0 i1 list
| x0 > 0 = my_fold fn x0 (i0-1) (i1-1) (tail list)
| x1 > 0 = fn (head list) (my_fold fn x0 0 (i1-1) (tail list))
| otherwise = x0
while will skip over list elements until i0 counts down to 0, then it
starts folding until i1 counts down to zero, then it stops.
> If you don't want to copy the list, don't separate
> out the function of copying the subset from the function of applying the
> operator. (I don't know, Haskell might even be smart enough to lazily do
> the Right Thing there even if you do it naively.)
In Haskell, writing
list_function_1 . list_function_2 . list_function_3
is an extremely common idiom, and one which the compiler is designed to
handle well. GHC has had produce/consume fusion for years, but now
there's the stream-fusion library, which does even better:
http://www.cse.unsw.edu.au/~dons/papers/CLS07.html
The fundamental idea is to take list-processing functions, remove the
recursive part, let the compiler inline like hell, wrap the recursion
around it, and you end up with something like a single for-loop with all
the work inside it. No copying involved. (And if the thing that produces
the list can be fused, the list might not actually exist in memory at
all. It might be kept in CPU registers. Depending on type, obviously.)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Invisible wrote:
> Right. Well I guess that more or less answers my question.
http://yosefk.com/c++fqa/inline.html#fqa-9.1
(Warp will hate me for quoting that, but it's actually a pretty good
explanation. "inline" has nothing to do with inlining functions.)
--
Darren New / San Diego, CA, USA (PST)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Invisible <voi### [at] dev null> wrote:
>> Now what happens if you want to only process elements having some
>> arbitrary property? (E.g., only the odd numbers.)
>
> I make the function take a functor which tells which elements to
> process.
> This is a standard technique in C++.
You can even write a custom iterator that, when incremented, skips even
numbers. Then you can use it with the existing fold function.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Nicolas Alvarez <nic### [at] gmail com> wrote:
> Warp wrote:
> > Invisible <voi### [at] dev null> wrote:
> >> Now what happens if you want to only process elements having some
> >> arbitrary property? (E.g., only the odd numbers.)
> >
> > I make the function take a functor which tells which elements to
> > process.
> > This is a standard technique in C++.
> You can even write a custom iterator that, when incremented, skips even
> numbers. Then you can use it with the existing fold function.
Good point. In fact, you can create iterators which do a lot of fancy
things, like for example one which traverses the container backwards
(a standard such iterator exists), if you need to perform an operation
in reverse order, or whatever you wish. And all this without having to
modify nor copy the data container.
Well, I assume that when you say in Haskell "reverse the list and then
give it to this function" the compiler might be able to avoid copying the
list to another one with the elements in reverse order.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|
 |