February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright wrote:
> X Bunny wrote:
>> (defun ack (x y)
>> (declare (fixnum x y))
>> (the fixnum
>> (if (zerop x)
>> (1+ y)
>> (if (zerop y)
>> (ack (1- x) 1)
>> (ack (1- x) (ack x (1- y)))))))
>>
>> The structure is no less obvious to me then the C. I can see the input and output types are clearly fixnums. The branches of the ifs are obvious.
>
> I see:
> 1- x
> in the Lisp code, and have to mentally translate it to:
> x - 1
> and not:
> 1 - x
>
> This just hurts my brain.
The first atom is the function to operate on the rest of the args.
(defun (subtract-one-from-it x)
(- x 1))
(subtract-one-from-it 43) -> 42
(defun (1- x)
(- x 1))
(1- 43) -> 42
same thing, first one is more verbose. Maybe this was a bad example?
(+ 2 3 1) -> 6
(defun (who-is-the-bomb boorad kris sean_k)
boorad)
BA
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to kris | kris wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> kris wrote:
>>
>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>
>>>> Bill Baxter wrote:
>>>>
>>>>> kris wrote:
>>>>>
>>>>>> kris wrote:
>>>>>>
>>>>>>> Bill Baxter wrote:
>>>>>>>
>>>>>>>> Frits van Bommel wrote:
>>>>>>>>
>>>>>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Whoa! I certainly hope so. It hadn't even occurred to me that Andrei might mean this syntax can only be used for just two collections. If that's the case then ... ick.
>>>>>>>>
>>>>>>>> --bb
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> InterleavedIterator does multiple collections via using multiple instances of InterleavedIterator. It's simple to use, and only needs to be written once. Would be better to implement some basic iterator needs than to introduce some tricky new syntax?
>>>>>>>
>>>>>>> - Kris
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Should have given an example. Simple case with 2 entities:
>>>>>>
>>>>>> auto two = InterleavedInterator (x, y);
>>>>>> foreach (x; two) ...
>>>>>>
>>>>>> more than 2:
>>>>>>
>>>>>> auto three = InterleavedInterator (two, z);
>>>>>> foreach (x; three) ...
>>>>>
>>>>>
>>>>>
>>>>> Should have also mentioned where one can find this mythical InterleavedIterator.
>>>>
>>>>
>>>>
>>>> The issue with such a multi-iterator is that it makes it easier to make errors, and harder to write efficient and correct code that's statically verifiable.
>>>>
>>>> I'm not sure when the interleaved iterator stops iterating, but there are two possibilities, none of which is satisfactory:
>>>>
>>>> 1. Stop after the shortest of the two collections is done. Then user code must query the state of the iterator after the loop to figure what extra work is to be done:
>>>>
>>>> auto two = InterleavedInterator (x, y);
>>>> foreach (x; two) { ... }
>>>> if (two.MoreData(0)) {
>>>> auto back2one = two.Project(0); // fetch the first iterator
>>>> foreach (x ; back2one) { ... }
>>>> } else if (two.moreData(1)) {
>>>> ... same (or)deal ...
>>>> }
>>>>
>>>> This is way more work than there should.
>>>>
>>>> 2. Stop after the longest of the two collections is done. Then user code must ensure _at each step_ that both iterators have meaningful data:
>>>>
>>>> auto two = InterleavedInterator (x, y);
>>>> foreach (x; two) {
>>>> if (two.HasData(0)) { ... }
>>>> else { ... only the second iter has data ... }
>>>> }
>>>>
>>>> This is unclear, verbose, and probably suboptimal.
>>>>
>>>> The scoping of foreach links the scope of the variables with their validity range, which rules out a class of possible errors entirely:
>>>>
>>>> foreach (x ; c1) (y ; c2) (z ; c3) {
>>>> ... x, y, z syntactically accessible _and_ valid ...
>>>> }
>>>> continue foreach (x, z) {
>>>> ... x is both invalid _and_ syntactically inaccessible ...
>>>> }
>>>>
>>>> As I mentioned in a different post, the fact that there are combinatorial potential sub-foreach statements is a non-issue.
>>>>
>>>>
>>>> Andrei
>>>
>>>
>>> If x, y, & z are of differing type, then I'd agree.
>>
>>
>> If they are of the same type and in an arbitrarily large numbers (x1, x2, x3...), we start talking about cutting through some sort of a matrix or manifold, which is an entirely different business.
>
> So if we did have a language-based iterator (which was a hot topic recently), it might take the form of a generator?
They all have their place in the language/stdlib ecosystem.
a) foreach does great when the iteration policy *and* range are both
fixed. I see this limitation as a big advantage. At least one study has
shown that most bugs occur in loops, and it's kind of annoying that C
turned the clock of progress back when it just provided the most general
and the least safe way of iteration to replace all others. Ever since
then, industrial languages followed suit, and as often happens, people
have done it that way for so long, that it took a few geniuses to figure out what makes sense to people on the street. So only since recently limited iteration started again to receive proper support.
b) iterators and generators have both the ability of stopping and resuming iteration. Generators are more natural when there's no real container (do you "iterate" random numbers? does it make sense to compare the input iterator to the "end of input" special iterator?) but have the disadvantage of fixing the iteration policy. Iterators are more flexible in that they offer the user the ability to devise their own iteration policies within a well-defined framework.
So there's a place for everyone.
Andrei
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | Sean Kelly wrote:
>Walter Bright wrote:
>>I see:
>> 1- x
>>in the Lisp code, and have to mentally translate it to:
>> x - 1
>>and not:
>> 1 - x
>>
>>This just hurts my brain.
>
>I thought Lisp used prefix notation, but the above syntax looks like element composition.
>
>
>Sean
nah. reading s-expressions needs some getting used to. you're reading serialized tree structure there after all.
the "1-" is the name of a function. instead of writing
(1- x)
you could of couse define a function "subtract-one" that does the same and then write
(subtract-one x)
but that's not much more readable. you can of course write
(- x 1)
and any lisp compiler optimizes it.
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kevin Bealer | Kevin Bealer wrote: > X Bunny wrote: >> Kevin Bealer wrote: >>> X Bunny wrote: >>>> Kevin Bealer wrote: >>>>> >>>>> 2. The syntax doesn't provide visual hints that let you read a program. >>> What I mean, is this (from the Computer Language Shootout): >>> >>> (defun ack (x y) >>> (declare (fixnum x y)) >>> (the fixnum >>> (if (zerop x) >>> (1+ y) >>> (if (zerop y) >>> (ack (1- x) 1) >>> (ack (1- x) (ack x (1- y))))))) >>> >>> as compared to this: >>> >>> int Ack(int x, int y) >>> { >>> if (x == 0) { >>> return y + 1; >>> } else if (y == 0) { >>> return Ack(x-1, 1); >>> } else { >>> return Ack(x-1, Ack(x, y-1)); >>> } >>> } >>> >>> These two things do the same thing in the same way, but the structure and syntax make the C example much more readable. If I want to know input and output types, I can find them. When I see the top of the if/elseif/else structure, I can find each part. It's layed out like a table, rather than like a ball of yarn. >> >> If the C was indented like this would it be as unreadable as the Lisp? >> >> int ack(int x, int y) >> { >> if(x == 0) >> { return y + 1; } >> else if(y == 0) >> { return ack(x-1, 1); } >> else { >> return ack(x-1, ack(x, y-1)); >> } >> } > > Yes - indenting badly makes it less readable in either case; I'm not sure if I was indenting the LISP example in a reasonable way. (I took it from someone else's page.) > >> (I cant even match up all the brackets with that one!) >> My editor indents the Lisp like this: >> >> (defun ack (x y) >> (declare (fixnum x y)) >> (the fixnum >> (if (zerop x) >> (1+ y) >> (if (zerop y) >> (ack (1- x) 1) >> (ack (1- x) (ack x (1- y))))))) >> >> The structure is no less obvious to me then the C. I can see the input and output types are clearly fixnums. The branches of the ifs are obvious. > > Maybe one day it will be for me if I keep trying to do things in LISP, but I can't shake the feeling that I'm learning to shoe horses -- a skill that had its place and time. > >>> More importantly, I can understand *parts* of this code without understanding the whole thing. >> >> Mmmm I dont what to say about that, for me with the Lisp I can do the same. >> >> Bunny > > Actually, I can understand the structure in both languages for this simple example without too much trouble. But the LISP one is a lot 'slower' for me to scan with my eyes. Some of that is a learned reflex, of course, but personally I don't think all of it is. > > My point is less about indentation than the other aspects of syntax. For one thing, the '(){};,' punctuation, which for me, is a win over the LISP way (again, assuming both examples are indented). If the LISP syntax is readable for you throughout, then that's okay. For me it definitely isn't -- I think most people would agree. > > Someone I know at work told me today that the LISP notation is very much like mathematical formulas. I couldn't help remembering math classes in college, when (at the time) I was always thinking, 'why can't they break this up into simple steps like a computer program would?' (Of course, I learned a lot about programming before I took algebra, so maybe that's part of the problem.) Probably this might be helpful. I think I should give it a shot, too: http://srfi.schemers.org/srfi-49/srfi-49.html A big-time Scheme champion (wrote an entire Scheme system and two dozens of great papers) admitted to me that syntax is a big hurdle for acceptance and that he is looking into offering alternatives. The advantage is that they come from the "right" place. Unix started as a system for professionals with security, flexibility, etc. in place, and it's much harder to make Windows, which started as a consumer product, get to the same level. Andrei |
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | Andrei Alexandrescu (See Website For Email) wrote:
> renoX wrote:
>> Andrei Alexandrescu (See Website For Email) a écrit :
>>> James Dennett wrote:
>>>> C++, of course, has std::for_each(e.begin(), e.end(), do_x);
>>>> in its library (though that's weaker than it could be because
>>>> of lack of support for convenient anonymous functions/lambdas).
>>>>
>>>> C++0x is very likely to have for(v: e). It's implemented
>>>> in ConceptGCC already. Java already has essentially that,
>>>> as does C#. This really doesn't set D apart (but at least
>>>> D isn't falling behind here).
>>>
>>> BTW, D might soon have simultaneous iteration that will blow away all conventional languages:
>>>
>>> foreach (i ; coll1) (j ; coll2)
>>> {
>>> ... use i and j ...
>>> }
>>> continue foreach (i)
>>> {
>>> ... coll2 finished; use i ...
>>> }
>>> continue foreach (j)
>>> {
>>> ... coll1 finished; use j ...
>>> }
>>>
>>> Best languages out there are at best ho-hum when it comes about iterating through simultaneous streams. Most lose their elegant iteration statement entirely and come with something that looks like an old hooker early in the morning.
>>
>> At first, I really didn't like the 'continue foreach', then afterwards I got used to it, I wonder if this is really such a requested feature though, what's wrong with the good old 'for' or 'while' for the complex case?
>
> Absolutely nothing's wrong. The same argument, however, could be formulated to render foreach redundant. We have for, don't we.
>
> The thing is foreach is terse and elegant and has a functional flavor that gives it safety and power that for doesn't have. It's only natural to ask oneself why all of these advantages must go away in a blink just because you want to iterate two things simultaneously.
>
>
> Andrei
I think its about how much this feature will be used. This one seems like it could be useful but its pretty close to borderline-"feature for feature sake" for me. There are probably a lot of other features that could be more useful then this one.
-Joel
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lutger | Lutger a écrit :
> renoX wrote:
>>> Recursion just has a nerdy aura around it. It probably doesn't help that
>>> most programmers are learned to think iteratively too.
>> And it doesn't help recursion that when you transform a recursive
>> function to a tail recursive function so that it's not too slow, the
>> result is ugly!
>
> How is that so? Could you give an example? I'm not very familiar with
> recursive programming actually...
A "natural" factorial:
fact(n):
if (n <= 1)
return 1;
else
return n * fact(n - 1);
Here fact is not tail recursive because the last operation is * not f.
So if you want an efficient implementation you have to rewrite it as:
fact(n) = fact2(1,n);
fact2(acc,n):
if (n == 1)
return acc
else
return fact2(acc*n, n-1);
Bleach. That's fugly.
renoX
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to janderson | janderson wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> renoX wrote:
>>> Andrei Alexandrescu (See Website For Email) a écrit :
>>>> James Dennett wrote:
>>>>> C++, of course, has std::for_each(e.begin(), e.end(), do_x);
>>>>> in its library (though that's weaker than it could be because
>>>>> of lack of support for convenient anonymous functions/lambdas).
>>>>>
>>>>> C++0x is very likely to have for(v: e). It's implemented
>>>>> in ConceptGCC already. Java already has essentially that,
>>>>> as does C#. This really doesn't set D apart (but at least
>>>>> D isn't falling behind here).
>>>>
>>>> BTW, D might soon have simultaneous iteration that will blow away all conventional languages:
>>>>
>>>> foreach (i ; coll1) (j ; coll2)
>>>> {
>>>> ... use i and j ...
>>>> }
>>>> continue foreach (i)
>>>> {
>>>> ... coll2 finished; use i ...
>>>> }
>>>> continue foreach (j)
>>>> {
>>>> ... coll1 finished; use j ...
>>>> }
>>>>
>>>> Best languages out there are at best ho-hum when it comes about iterating through simultaneous streams. Most lose their elegant iteration statement entirely and come with something that looks like an old hooker early in the morning.
>>>
>>> At first, I really didn't like the 'continue foreach', then afterwards I got used to it, I wonder if this is really such a requested feature though, what's wrong with the good old 'for' or 'while' for the complex case?
>>
>> Absolutely nothing's wrong. The same argument, however, could be formulated to render foreach redundant. We have for, don't we.
>>
>> The thing is foreach is terse and elegant and has a functional flavor that gives it safety and power that for doesn't have. It's only natural to ask oneself why all of these advantages must go away in a blink just because you want to iterate two things simultaneously.
>>
>>
>> Andrei
>
> I think its about how much this feature will be used. This one seems like it could be useful but its pretty close to borderline-"feature for feature sake" for me. There are probably a lot of other features that could be more useful then this one.
No doubt, but there are many factors to take into account (among which implementation difficulty). In Perl it's a constant source of friction for me. If I want to iterate through one thing (array, file, hash...), it's all dandy. As soon as I need to iterate over two things I need to import and use an arcane library, or fall back and use while(1) and do it all with the axe (which is what I end up doing most of the time).
I'm actually mildly surprised. Lately there was some talk around here about supporting the day-to-day programmers and so on. I find looping a very day-to-day thing, and looping over 2+ things at least a few-days-to-few-days thing. There is a need for parallel iteration, if nothing else shown by the existence of a library that addresses exactly that - to the extent possible in a library that's not in the position to control syntax, scoping, and visibility. I was sure people will be on this one like white on rice. But Bjarne Stroustrup was right: nobody knows what most programmers do :o).
Andrei
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | Andrei Alexandrescu (See Website For Email) a écrit : > renoX wrote: >> Andrei Alexandrescu (See Website For Email) a écrit : >>> BTW, D might soon have simultaneous iteration that will blow away all conventional languages: >>> >>> foreach (i ; coll1) (j ; coll2) >>> { >>> ... use i and j ... >>> } >>> continue foreach (i) >>> { >>> ... coll2 finished; use i ... >>> } >>> continue foreach (j) >>> { >>> ... coll1 finished; use j ... >>> } >>> >>> Best languages out there are at best ho-hum when it comes about iterating through simultaneous streams. Most lose their elegant iteration statement entirely and come with something that looks like an old hooker early in the morning. >> >> At first, I really didn't like the 'continue foreach', then afterwards I got used to it, I wonder if this is really such a requested feature though, what's wrong with the good old 'for' or 'while' for the complex case? > > Absolutely nothing's wrong. The same argument, however, could be formulated to render foreach redundant. We have for, don't we. > > The thing is foreach is terse and elegant and has a functional flavor that gives it safety and power that for doesn't have. I wouldn't call 'functional flavored' something with such an 'hidden state' stored in i, but that's just me. And I have a question for the safety: what is supposed to happen if the programmer modifies coll1 between the foreach(i ; coll1) and continue foreach? Adding or removing value in the collection before the continue foreach? Just being curious, I would imagine that this is just forbidden. renoX > It's only natural to ask oneself why all of these advantages must go away in a blink just because you want to iterate two things simultaneously. > > > Andrei |
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | Andrei Alexandrescu (See Website For Email) wrote:
> I'm actually mildly surprised. Lately there was some talk around here about supporting the day-to-day programmers and so on. I find looping a very day-to-day thing, and looping over 2+ things at least a few-days-to-few-days thing. There is a need for parallel iteration, if nothing else shown by the existence of a library that addresses exactly that - to the extent possible in a library that's not in the position to control syntax, scoping, and visibility. I was sure people will be on this one like white on rice. But Bjarne Stroustrup was right: nobody knows what most programmers do :o).
Python and Ruby are hardly considered to be obtuse languages, or unfriendly to Joe coder, but both get by just fine without special case syntax for iterating over multiple collections, or for iterating in reverse.
for x,y izip(foo,bar):
do stuff
for x reversed(foo):
do stuff
for x,y izip(reversed(foo),bar):
do that with your proposal!
That said, I understand that Python and Ruby have a little more freedom to pile up the abstractions because both of them are so friggin slow that a few more layers won't hurt anything. D can't be quite so cavalier about tossing performance for elegance.
Still, I'm remain unconvinced that D can't have both performance and elegance.
--bb
|
February 14, 2007 Re: Super-dee-duper D features | ||||
---|---|---|---|---|
| ||||
Posted in reply to renoX | renoX wrote: > Andrei Alexandrescu (See Website For Email) a écrit : >> renoX wrote: >>> Andrei Alexandrescu (See Website For Email) a écrit : >>>> BTW, D might soon have simultaneous iteration that will blow away all conventional languages: >>>> >>>> foreach (i ; coll1) (j ; coll2) >>>> { >>>> ... use i and j ... >>>> } >>>> continue foreach (i) >>>> { >>>> ... coll2 finished; use i ... >>>> } >>>> continue foreach (j) >>>> { >>>> ... coll1 finished; use j ... >>>> } >>>> >>>> Best languages out there are at best ho-hum when it comes about iterating through simultaneous streams. Most lose their elegant iteration statement entirely and come with something that looks like an old hooker early in the morning. >>> >>> At first, I really didn't like the 'continue foreach', then afterwards I got used to it, I wonder if this is really such a requested feature though, what's wrong with the good old 'for' or 'while' for the complex case? >> >> Absolutely nothing's wrong. The same argument, however, could be formulated to render foreach redundant. We have for, don't we. >> >> The thing is foreach is terse and elegant and has a functional flavor that gives it safety and power that for doesn't have. > > I wouldn't call 'functional flavored' something with such an 'hidden state' stored in i, but that's just me. My definition of "foreach (i ; c) S" is: bind i in turn to each element of c, and evaluate S. That's very functional. The definition really reminds much more of map and fold, than of "for (s; e1; e2) S". And let's not forget that all functional programs sneak state in their arguments - or a monad :o). > And I have a question for the safety: what is supposed to happen if the programmer modifies coll1 between the foreach(i ; coll1) and continue foreach? That's a good question. Currently the behavior is undefined. The behavior that Walter is thinking of implementing is to render it implementation-defined, but never undefined (as in thrashing random memory). All that needs to be done is disallow in-place shrinking of containers. The garbage collector will take care of the rest. > Adding or removing value in the collection before the continue foreach? See above. > Just being curious, I would imagine that this is just forbidden. That's what it is today - it's formally forbidden. After Walter eliminates in-place shrinking, it will be safe, and interestingly, there will never be a need for bounds checking. Andrei |
Copyright © 1999-2021 by the D Language Foundation