Go's Declaration Syntax (2010)
blog.golang.org
blog.golang.org
So the last example of function pointer taking a function pointer parameter and returning a function pointer could look something like:
typedef int (*return_fp_t)(int, int);
typedef int (*param_fp_t) (int, int);
return_fp_t do_stuff(param_fp_t a, int b) {
/* ... */
}
It requires coming up with new type names but it remains pretty readable in my opinion. Also if the prototype of one of the function changes (which is pretty common in my experience) it's easier to modify it since it's only declared once.Early C didn't have typedef, just struct. The grammar was LALR(1), and context-independent - knowing the syntax and the reserved words, you could parse C.
Then came typedef. Now, to parse C/C++, you must know the names of types. So, to parse a C/C++ file, you must read and interpret all the #include files first. This makes it hard to write tools that work on C/C++ source.
This isn't a problem with the declaration syntax of the Pascal/Modula/Ada/Go line of languages, where declarations have the order "x int" instead of "int x". So "go fmt" doesn't have to have all the dependencies just to clean up the indentation.
#define IF(x) if (x) {
#define ELSE } else {
#define ENDIF }Also worth mentioning that the "clockwise/spiral rule" that this article links to for reading C declarations is actually wrong, as Linus Torvalds helpfully points out here: https://plus.google.com/111049168280159033135/posts/1ZhdNwbj...
This is probably a better article for understanding them: http://www.unixwiz.net/techtips/reading-cdecl.html
You know what instigated it? A precedence error.
Working alone in C++ is one thing, but having to read other people's code seems to turn into a mess if you have a mix of C++98, C++11 and C++14 code bases.
Merely reshuffling the syntax/grammar but not going deeper is not going to change anything.
C++ is, let's say, interesting. But it's always going to be a "bolt-on" addition to C, and keeping the redundancy (should I use open, fopen or a C++ object? when opening devices, open is usually used)
In C, of the 3 type operators you compose to declare the type (arrays, functions, pointers), 2 are postfix (arrays and functions) and one is prefix (pointers). This causes a mixture of directions when you read the type. Here in go they fixed this by making arrays and functions prefix like pointers, so you get a unidirectional read and you don't need parenthesis for precedence (except maybe for associativity).
int (int argc, char*[] argv)main
prefix is: main(argc int, argv []*char) int
The C version: int main(int argc, char *argv[])
Is a horrible jumbling of both styles, with some confusion around type and name thrown in for fun.The main benefit of the Go approach, beyond whether prefix or postfix works better, is consistency. Compare to Go's:
main(argc int, argv []*char) int
Once you learn to read it, it's much better, though perhaps not like English. In English, we preface the noun with the adjective, "the red car," whereas other languages say the equivalent of, "the car red." When people object to the Go syntax, they're almost always native English speakers. Ptr(Type) -- deref type
Array(Count, Type) -- elem type
Func(ParamList, Type) -- return type
Since the "Type" param is always there. C declaration syntax is like a linear composition of operators where the Type flows through all the operators to form the final type. The base type on the left of the declaration is the initial parameter that goes through the composition.So C type operator grammars (in the right-hand side of the declaration, excluding the base type) are actually:
Ptr *rest_of_type
Array rest_of_type[count]
Func rest_of_type(params)
That is why I say ptrs are prefix and array/func are postfix.All 3 have the extra implicit type argument that comes from the wrapping type-expression and the base type - so I wouldn't include it and say they're both prefix and postfix.
Edit: Of course I must add that it is a breath of rather old fresh air - Pascal and SML have had it for decades.
char buffer[10];
the name "buffer" is a pointer to the first element of the buffer. This always feels like a hack, because "buffer" is an array, not a pointer to an element. I'd rather write &buffer[0]. This also feels like a hack when the buffer is coincidentally of size 0, and buffer[0] does not exist. So I'm just left with the feeling that something is missing in the language.P.S.: how do you input a literal asterisk on HN? Escaping with a backslash doesn't work, had to find a fancy unicode asterisk.
int p; type nameOfInstanceOfType;
int *p; notType notNameOfInstanceOfType;
How about something sane like: "pointer p = pointer(int)" or even Scalalike "pointer[int] p"? Pointers aren't so bad really. I think a lot of the hate for them is because the syntax begs you to fuck up. And inevitably you do. I mean, I just type random stuff until I get no warnings and things look like they work. Literally. That's how I program. How many asterisks here? I dunno, I'll just try zero through three and see if any of those work...
int* p;
Follows the Type NameOfVar pattern, though of course it breaks for multiple simultaneous declarations.As to your other point: it's legal for a pointer to an object to point one item past the end of the object. You may not read/write to this of course, but if you allocate 100 bytes of memory you may have char ∗start, and char ∗end = start+100. That's a nice concept taken to its limit in STL where all algorithms operate on abstract start; end+1 iterator pairs. So an empty sequence has start == end.
(Thanks to a sibling poster for the usage of ∗).
http://eli.thegreenplace.net/2009/10/21/are-pointers-and-arr...
The short version is that in the early days, pointers where declared like "char p[]" and for arrays declared like "char buf[10]" a pointer was created, like this (in modern C):
char buf_storage[10]; /* this is not actually visible to the programmer */
char *buf = &buf_storage[0];
So pointers and arrays really were the same thing. Of course, this didn't fly anymore when structs were introduced (or when embedding arrays in structs was allowed, at least) so that's when arrays were introduced as a first-class type, with weird semantics so that existing code that used array variables as if they were pointer variables wouldn't break.http://confreaks.com/videos/3419-gophercon2014-opening-day-k...
It makes sense, but at the same time it reminds me of something from the esoteric top list at 99-bottle-of-beer.net.
C++ compilation is not slow because of the grammar, but because of things like multiple includes
Compilers like GCC are "slow" because they do a lot of work. To see a good demonstration of how fast C compilation can be, consider Bellards TCCBOOT [1] which boots into a version of TCC that then compiles a Linux kernel and executes it - the compilation took a reported 10 seconds on a 2.4GHz Pentium 4 in 2004. When testing against the then-current gcc 3.2 with -O0, TinyCC was about 9 times faster than GCC.
There are certainly things about C that makes fast compilation tricky, but it's not the syntax (abusing a text based pre-processor instead of having a proper module system being suspect #1)
Even so, last time I looked, GCC stood up fairly well agains the Go.