That's fine with me. The people who will remain are the people who
will remain. It's similar to the discussion over in comp.arch, that
those who come to Jesus do so because they have a calling on the
inside.
Those who will use CAlive will see the value I see in the offering,
and come to it for similar reasons.
adhoc touched
|params char* p
{
printf("Touched %s\n", p);
}
if ( x == 2 (|touched|"x"||) <||meta|iftrue|x||> <||mefa|iffalse|x||>
|| y == 1 (|touched|"y"||)
|| z == 3 (|touched|"z"||) <||meta|iftrue|z||> <||mefa|iffalse|z||>)
{
// Code goes here
} else {
// Code goes here
}
In each test, because the casks are inserted after the test, the
execution of the (|touched|) cask indicates it made it that far
through the || expression, and the result of each logic state
will have triggered the appropriate iftrue() and iffalse() calls.
And casks can be inserted anywhere employing similar abilities.
> > switch (x)
> > {
> > case 2 <||meta|iftrue|> <||mefa|iffalse|>:
> > // Code goes here
> > break;
> > }
>
> The "iftrue" part can go directly in the case, just before "Code goes
> here". Again, the standard C is clearer and simpler than the casks.
>
> The "iffalse" part is harder - it is far from clear how it should
> interact with other code. Should it be run for all cases other than 2?
It would be fired in all cases top-down in the case statements until
one which matches is found.
CAlive also allows for non-const values in switch statements, as well
as ranges of values (which I believe are also GCC extensions).
> Should it be run before or after "iftrue" parts for other cases?
> Should that depend on whether those cases are before or after case 2?
>
> Even if you have a single answer to that question, it is not going to be
> obvious when used - and it will be a total mess if there are many such
> clauses. It means that the actions taken in one case depend on the code
> written in a different case - making the code confusing, and losing all
> track of code flow. And as well as being extremely difficult for the
> programmer to comprehend, it will hinder the compiler's abilities to
> generate good object code.
>
> I would need to see a /very/ convincing real-life example before this
> would make sense to me.
There will be one at some point. CAlive is still a ways away from
me bringing it into a tool you can download and test. I'm doing all
of this development in my after-work hours, on weekends, and on
holidays.
> > Bear in mind that in the GUI editor, these expressions would be
> > reduced down to visual forms which look like this until expanded:
> >
> > if (x == 2 <|iftrue|> <|iffalse|>)
> > {
> > // Code goes here
> > }
> >
> > switch (x)
> > {
> > case 2 <|iftrue|> <|iffalse|>:
> > // Code goes here
> > break;
> > }
> >
> > And only when some GUI interaction takes place would they be expanded.
> > It makes them easy to see, easy to manage. They can be grabbed hold
> > of with the mouse as a single unit and dragged around, etc.
>
> Judging from your screenshots, all I see is that these casks will have
> syntax highlighting to make them a little more obvious. That does not
> change how unnecessary and confusing they are.
I have offline drawings I've made over time as I've created little
examples to introduce things.
Casks can also have what I call pips:
(|o|name|o|)
And they can have more than one pip:
(|ooo|name|oo|)
And pips can be opened or closed:
(|Ooo|name|OO")
// "O" means "open", and "o" means closed
// Why "open" and "closed"? Read on...
The original purpose I came up with back in the mid-1990s was to employ
pips to indicate things by context. The open and closed nature
determined how the cask was rendered graphically. If open, the return
parameters, for example, would be shown like this:
// It's actual form: (|O||return|name|)
// How it's rendered on-screen:
+--------+
| return |
|+-------+
||
(|O|name|)
It has a popup which extends up out of the side. When it is closed,
it would be rendered like this:
(o||name|)
The double-|| on the left side indicates it has content there, and the
closed "o" would be used to access it. Similarly, the content on the
right-side would go down:
// It's actual form: (|name|a,b,c||O|)
// How it's rendered on-screen:
(|name|O|)
||
|+------+
| a,b,c |
+-------+
In addition, utility casks operate the same way. And I use the tag
".meta(name)" to give them a display name, as in:
~|lots of code goes here|~.name(fixup)
Then it's rendered on-screen as this when closed, allowing it to be
dragged around and dropped where needed:
~|o|fixup|~
Which allows it to be like this when opened:
+---------------+
| abc = NULL; |
| other_code(); |
| more_code(); |
|+--------------+
||
~|O|fixup|~
The GUI editor handles the re-display, while the hard-and-fast syntax
form exists in text form. But, since computers are our tools and
helpers, there's no reason why the two should not be explicitly tied
together in this supercomputer-on-a-graphics-card era.
> > -----
> > CAlive also introduces the "always_before" and "always_after" code blocks
> > for various things:
> >
> > // Note: the [once] syntax below indicates "once" is a keyword that
> > // is optional, same for [per_call].
> > while (some_condition)
> > {
> > // Code goes here
> >
> > } always_before [once] [per_call] {
> > // Code executes before the while loop begins
> > // If once is applied, then it only executes the first time
> > // the while loop is traversed throughout the lifetime of
> > // the program. If per_call is applied, then it will use
> > // a local flag to determine if it should be called.
> >
> > } always_after [once] [per_call] {
> > // Code executes after the while loop exits
> > // If once is applied, then it only executes the first time
> > // the while loop is traversed throughout the lifetime of
> > // the program. If per_call is applied, then it will use
> > // a local flag to determine if it should be called.
> > }
>
> Why not just write the code before the while loop, after it, or inside
> it as appropriate?
Encapsulation.
It's all about removing as much as is required from the author having
to figure things out all of hte time. That requirement will still be
there, but by visually putting the code related to the unless clauses
inside the loop, it's immediately obvious to even non-programmers
what it means.
BTW, I've used my wife and son as guinea pigs for some of this, so I
do have corroborating evidence to which I speak. :-)
> Again, it's going to take a /serious/ example to show any point here.
>
> And then you have to convince us that it is better than normal C or C++,
> possibly using exceptions or goto.
>
>
> > -----
> > CAlive also allows do loops to have their test clause at the top:
> >
> > do while {
> > // Code goes here
> >
> > // } unless (test) { blocks can go here
> >
> > }; // semicolon here is optional, and ignored
> >
> >
>
> In C, you have:
>
> while (x) {...};
>
> and
>
> do {...} while (x);
>
> Put the condition where it makes sense.
That's why I allow "do while" and "do { } while" ... they're both do
loops, and it makes more sense to keep them the same than to just use
"while" in one case, and "do" in another.
There are also some x-extensions I'll get into at a later point. I'm
sure you'll love those. :-)
Here's some previews in VXB++ syntax (XBASE-like) in case you can't wait:
http://www.visual-freepro.org/wiki/index.php/Flow_Control#FORX..ENDFORX
http://www.visual-freepro.org/wiki/index.php/Flow_Control#XIF..XELSEIF.2C_XFALLTHROUGH.2C_XGOTO.2C_XEXIT.2C_and_XRETRY
http://www.visual-freepro.org/wiki/index.php/Flow_Control#XELSE.2C_XOVERRIDE_and_XCANCEL
-----
I also introduce a line if statement:
lif (something) code1; code2; code3;
lelse code4; code5; code6;
It differs from the standard syntax:
if (something { code1; code2; code3; }
else { code4; code5; code6; }
...and for exactly the reasons you'd expect, lif and lelse are
fundamental things and they should be easier to read and understand.
-----
I also introduce a full-if statement which tests every condition
all the way through:
fif (x == 4 && y == 3)
{
// Both values were tested, and their results are in implicit
// structure of boolean variables called _fif.l1, _fif.l2, and
// so on.
}
The affixed names can also be changed with override definition casks
(shown here with extra spaces to break it out):
fif ( [|xtest|] x == 4 && [|ytest|] y == 3)
{
// The results are available through _fif.xtest, and _fif.ytest
// as well as _fif.l1 and _fif.l2, as they are created as a
// union.
}
And there's more. But in all honesty, I don't want to spend the day
defending myself. I'm 20+ years in considering these ideas, and I've
really spent the bulk of the last few years considering the finer
details and points as I've become a better C/C++ developer. They are
the things I want to see in a language, and I'm intent on adding them.