I wrote the least-C C program I could
briancallahan.net
briancallahan.net
IF (n=to-from)<=1 THEN return FI
All modern languages that try and do a refresh on the C style (Rust and Swift notably) do this, it's clearly the right idea. They should just update the C and C++ syntax to make those parentheses optional at this point.(PS. some people would also recoil at the assignment-as-expression used in that line, but that's just good clean fun!)
if (a)
doThis();
andThat();
is interpreted as if(a){
doThis();
}
andThat();
Some compilers are nice enough to throw around misleading indentation warnings, but without an explicit block termination like FI this just causes issues all over the place.Most native speakers aren't aware of the terms. They'll call anything by any name.
There is a significant constituency for "curly brackets {}" and "square brackets []".
Anyway, there are mistakes you could make that would give you away as a nonnative speaker, but that wasn't one of them.
Yes, but there is still basically no constituency for “parentheses” (“{}”) or “parentheses” (“[]”), except in some narrow specific contexts; in programming, one example would be discussion of certain Lisp dialects where either all or certain uses of “()” in classic Lisp can (or, in the “certain” case, sometimes must) use the others, in which context “parentheses” are sometimes used generically for all three.
So, while there is a variety of ways paired delimiters are described by native speakers, the particular use here was still outside of the normal range of that variation.
Many (most?) C style guides make the braces mandatory, but it’d be a big step to actually change the language syntax that way. Tons of existing code would need to be updated. .. although I guess that could be automated!
Basically, C had a choice of eliding the parentheses around the condition or the braces around a single-statement block, and they chose the braces. With a half-century of hindsight, that was probably the wrong choice.
Syntactically it seems unambiguous because after an 'if' you just need to check for an '(', and if you don't find it you're looking for a '{' after an expression.
You'd lose forward compatibility, but I think there's precedent for that in C language revisions.
In everyday usage in the UK, I’d wager that most refer to () as brackets, [] as square brackets and {} as curly brackets.
Fun fact: parenthesis / parenthetical can mean 'a word or phrase inserted as an explanation or afterthought into a passage which is grammatically complete without it, in writing usually marked off by brackets, dashes, or commas.' (Merriam-Webster) - so you can have a parenthetical statement without using actual parentheses.
https://augustss.blogspot.com/2009/02/regression-they-say-th...
And to answer the question in the top comment:
https://hackage.haskell.org/package/BASIC-0.1.5.0/docs/Langu...
#include<curses.h>
#include<stdio.h>
#define ;
#define do
#define ʌ *
#define N -1
#define call
#define main.
#define , ];
#define ꞉= =
#define = ==
#define ; ];
#define not !
#define I int
#define M 256
#define or ||
#define end ;}
#define CALL }
#define S case
#define X x<<8
#define size [
#define <> !=
#define var int
#define Y y<<16
#define begin {
#define F FILE*f
#define ꞉integer
#define POOL ;}}
#define W p[y*q+x]
#define Z (W&(M N))
#define packed char
#define OK break;case
#define procedure int
#define fill꞉= return
#define close fclose(f)
#define readln(a) c=fgetc(f)
#define H(a,b) mvaddch(a,b,' ')
#define writeChar(a) fputc((a),f)
#define open(a,b) F=fopen((a),(b))
#define A(a) attron(COLOR_PAIR(a))
#define B(a) attroff(COLOR_PAIR(a))
#define read(a) switch(getch()){case
#define draw(a) {W=Y;W|=X;W|=((a)&0xff);}
#define LOOP for(y=0;y<w;y++){for(x=0;x<q;x++){
#define check if(fgetc(f)!=83){fclose(f);return N;};w=fgetc(f);q=fgetc(f)
#define start initscr();clear();keypad(stdscr,TRUE);cbreak();noecho();curs_set(0)https://www.reddit.com/r/rust/comments/5penft/parallelizing_...
> If you look closely, those aren't angle brackets, they're characters from the Canadian Aboriginal Syllabics block, which are allowed in Go identifiers. From Go's perspective, that's just one long identifier.
The thread goes downhill fast from there to the point where
> I once wrote a short Swift program with every identifier a different length chain of 0-width spaces.
In the above thread, user "yiyus" actually went through the macro code line-by-line and made explanatory notes; unbelievable! - https://docs.google.com/document/d/1W83ME5JecI2hd5hAUqQ1BVF3...
If we're going to use crazy header files, I want to see someone get the linux kernel to build and boot while including this: https://gist.github.com/aras-p/6224951
A long time ago, a friend who was studying mathematics at the time, approached me laughing hysterically and showed me a page in an "intro to C"-style book. It showed an example of how one would write a "get circumference of a circle" function. At the top of the code, there was a #define for the value of pi.
The text describing the code said something like this about why pi is #defined and not included directly in the expression:
"We define pi as a constant for two reasons: 1) it makes the expressions using it more readable 2) should the value of pi ever change, we will only have to change it in one place in the code"
Most remember 3.14159 which is way better than 22/7, but pi has an even better unusual rational approximation, 355/113 gets you two extra digits, it is accurate to one part in 12 million, or 0.000008% . So you have to go to 3.1415927 to beat it.
> For JPL's highest accuracy calculations, which are for interplanetary navigation, we use 3.141592653589793.
https://www.jpl.nasa.gov/edu/news/2016/3/16/how-many-decimal...
"It's easier to type $pi than 𝜋, especially if you don't have Unicode. And it will be easy to maintain the program in case the value of ever changes."
There is also a comment by Randal Schwartz in the comments section where he credits Tom Phoenix with that particular bit of humour.
https://www.forbes.com/sites/kionasmith/2018/02/05/indianas-...
#define ;
#define do
I was under the impression it was "#define CNAME value" – what does it mean when there is no value? A trip to Google didn't turn up anything for me, so I'm wondering if a C master can weigh in. Thanks!The most common place where this sort of thing occurs is the common idiom
#ifdef DEBUG
#define debug(...) realdebug(__VA_ARGS__)
#else
#define debug(...)
#endif
which allows you to sprinkle debug() calls through your code and have them disappear if you compile without defining the macro DEBUG. #define debug(...) ((void)0)
This forces you to use debug() as a statement. If no value is defined you could omit the semicolon on a debug() statement and it still compiles."When I use a word," Humpty Dumpty said, in a rather scornful tone, "it means just what I choose it to mean - neither more nor less."
Very ugly, ill-defined, uncodified democracy. No one tells you when you vote, no one counts up the votes, there are no official results.
When you speak or communicate with someone else, you are voting with them. "I vote this word means X, for a poor/crude/coarse agreement of what X is the first place."
And this is propaganda at its finest. Evil doublespeak: say one thing, and it actually is another. Snuck in.
Fantastic.
Clever, I was wondering how the : was done, but it's an abomination :-/
With some simple improvements to the language, about 99% of the C preprocessor use can be abandoned and deprecated.
Of course, I've also opined that Unicode supporting multiple encodings for the same glyph is also madness. The Unicode people veered off the tracks and sank into a swamp when they decided that semantic information should be encoded into Unicode characters.
As if that weren't enough, they also decided to cram half-assed formatting into it. You got bold letters, italics, various fancy-style letters, superscripts and subscripts for this and that.. all for the sake of leagacy compatibility. Unicode was legacy right from the beginning.
Unicode chose to support lossless conversion to and from other encodings it replaces (I presume it was important for adoption), so unfortunately it inherited the sum of everyone else's tech debt.
Note that nobody needs these code points to reverse text. I did it above without gnisu those code points.
[Purely-Latin borderline cases: umlaut (is not two dots in Fraktur) vs diaeresis (languages that use it are not written in Fraktur), acute (non-Polish, points past the letter) vs kreska (Polish, points at the letter). On the other hand, the mathematical “element of” sign was still occasionally typeset as an epsilon well into the 1960s.]
Unicode decides most of these based on the requirement to roundtrip legacy encodings (“have these been ever encoded differently in the same encoding?”), which seems reasonable, yet results in homograph problems and at the same time the Turkish case conversion botch. In any case, once (sane) legacy encodings run out but you still want to be consistent, what do you base the encoding decisions on but semantics? (On the other hand, once you start encoding semantic differences, where do you stop?..) You could do some sort of glyph-equivalence-class thing, but that would still give you no way to avoid unifying a and а—everyone who writes both writes them the same.
None of this touches on Unicode “canonical equivalence”, but your claim (“Unicode supporting multiple encodings for the same glyph is [...] madness”) covers more than just that if I understood it correctly. And while I am attacking it in a sense, it’s only because I genuinely don’t see how this part could have been done differently in a major way.
As a way to avoid going ASCII-only, though, excluding only things that look like syntax might be simultaneously not going far enough (how are homograph collisions between user-defined identifiers any better?) and too far (reliably transplating identifiers between languages that use different sets of punctuation seems like it’d be torturously difficult).
The idea of a letter in an alphabet and a printable glyph for that letter are two different ideas. Unicode could have and probably should have had a two-layer encoding where the letters are all different but an extra step resolves letters to glyphs. Where one glyph can represent more than one letter, a modifier can be attached to represent the parent alphabet so no semantic information is lost. Comparison for "same character" would be at the glyph level without modifiers, and we could have avoided a bunch of different Unicode equivalence testing libraries that have to be individually written, maintained, and debugged. Use in something like a spell checker, conversion to other character sets, or stylization like cursive could have used the glyph and source-language modifier both.
First off, Unicode cursive (bold, Fraktur, monospace, etc.) Latin letters are not meant to be styles, they are mathematical symbols. Of course, that doesn’t mean people aren’t going to use them for that[1], and I’m not convinced Unicode should have gotten into that particular can of worms, but I think you can consistently say that the difference between, for example, an italic X for the length of a vector and a bold X for the vector itself (as you could encounter in a mechanics text) is not (just) one of style. Similarly for the superscripts and modifier letters—a [ph] and a [pʰ] or a [kj] and a [kʲ] in an IPA transcription (for which the modifiers are intended) denote very different sounds (granted, ones that are unlikely to be used at the same time by a single speaker in a single language, but IPA is meant to be more general than that).
(Or wait, was this a reply to my point about Russian vs Bulgarian d? The Bulgarian one is not a cursive variant, it’s derived from a cursive one but is a perfectly normal upright letter in both serif and sans-serif, that looks exactly the same as a Latin “single-storey” g as in most sans-serif fonts but never a Latin “double-storey” g as in most serif fonts, and printed Bulgarian only uses that form—barring font problems—while printed Russian never does. I guess you could declare all of those to be variants of one another, even if it’s wrong etymologically, but even to a Cyrillic user who has never been to Bulgaria that would be quite baffling.)
As to your actual point, I don’t think the comparison you describe could be made language-independent enough that you wouldn’t still end up needing to use a language-specific collation equivalence at the same time (which seems to be your implication IIUC). E.g. a French speaker would usually want oe and œ to compare the same but different from o-diaeresis, but a German speaker might (or might not) want oe and o-umlaut to compare the same, while every font renders o-diaeresis and o-umlaut exactly the same. French speakers (but possibly not in every country?) will almost always drop diacritics over capital letters, and Russian speakers frequently turn ё (/jo/, /o/) into е (/je/, /e/) except in a small set of words where there’s a possibility of confusion (the surnames Chebyshev and Gorbachev, which end in -ёв /-of/, are well-known victims of this confusion). Å is a stylistic varisnt of aa in Norwegian, but a speaker of Finnish (which doesn’t use å) would probably be surprised if forced to treat them the same.
And that’s only in Europe—what about Arabic, where positional variants can make (what speakers think of) a single letter look very different. Even in Europe, should σ and ς be “the same glyph”? They certainly have the same phonetic value, and you always have to use one or the other...
Of course, we already have a (font-dependent) codepoint-to-glyph translation in the guise of OpenType shaping, but it’s not particularly useful for anything but display (and even there it’s non-ideal).
[1] https://utcc.utoronto.ca/~cks/space/blog/tech/PeopleAlwaysEx...
This is a total pedantitangent but I don't think that's actually true. These wikipedia pages don't talk about it directly but I think give a bit of the flavour/related info that suggest it's not nearly that set in stone:
https://bg.wikipedia.org/wiki/%D0%91%D1%8A%D0%BB%D0%B3%D0%B0...
https://bg.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D0%B6%D0%B4...
The second one, in particular, says early versions of Peter I's Civil Script had the g-looking small д, so these variants have been used concurrently for some time.
By the context!
How would a book distinguish `a` as in `apple` from `a` as in `a+b`? (Unicode has a separate letter a from a math a.)
By the context!
This is what I meant by Unicode has no business adding semantic content. Semantics come from context, not from glyph. After all, what if I decided to write:
(a) first bullet point
(b) second bullet point
Now what? Is that letter a or math symbol a? There's no end to semantic content. It's impossible to put this into Unicode in any kind of reasonable manner. Trying to do it leads one into a swamp of hopelessness.
BTW, the attached article is precisely about deliberately misusing identical glyphs in order to confuse the reader because the C compiler treats them differently. What better case for semantic content for glyphs being a hopelessly wrongheaded idea.
In fact, having a font that makes l I and 1 indistinguishable is plenty of good reason to NOT make this a requirement.
Arguably the C feature most used in other languages is the C preprocessor's conditional compilation for e.g. different OSes. Used by languages from Fortran (yes, there exists FPP now - for a suitable definition of 'now') to Haskell (yes, `{-# LANGUAGE CPP #-}`).
1. static if conditionals
2. version conditionals
3. assert
4. manifest constants
5. modules
I occasionally find macro usages that would require templates, but these are rare.
typedef enum { ONE, TWO, THREE } my_enum;
const char* getEnumName(my_enum val);
you can use various preprocessor tricks to implement getEnumName such that you don't have to change it when adding more cases to the enum. This would be much better implemented with some compiler intrinsic/operator like `nameof(val)` that returned a string. C# does something similar with its `nameof`.For those who don’t know: the X Macro (https://en.wikipedia.org/wiki/X_Macro, https://digitalmars.com/articles/b51.html)
This is slightly different than the form I've seen it, but same idea: in the version I've seen, you have a special file that's like "enums.txt" with contents like (warning, not tested):
X(red)
X(green)
X(blue)
and then you write: typedef enum {
#define X(x) x
#include "enums.txt"
#undef X
} color;
const char* getColorName(color c) {
switch (c) {
#define X(x) case x: return #x;
#include "enums.txt"
#undef X
}
}
Same idea, just using an #include instead of listing them in a macro. Thinking about it, it's sort-of a compile time "visitor pattern".A better preprocessor (a C code generator, effectively) would be a simple program that would interpret the <% and %> brackets or similar (by “inverting” them). It is very powerful paradigm.
If you want to use metaprogramming, you've outgrown C and should consider a more powerful language. There are plenty to pick from. DasBetterC, for example.
I'll repeat that if metaprogramming is important to you, you need a more advanced language. Why are you using C if you want advanced features?
> Is anyone using Cello?
> People have experimented with it, but there is no high profile project I know of that uses it. Cello is too big and scary a dependency for new C projects if they want to be portable and easy to maintain.
void get_order_token()
BeginDeclarations
EndDeclarations
BeginCode
While token_break_char Is ' '
BeginWhile
order_token_get_char();
EndWhile;
copy_string(token, null_string);
If IsIdentifier(token_break_char)
Then
While IsIdentifier(token_break_char)
BeginWhile
concat_char_to_string(token, token_break_char);
order_token_get_char();
EndWhile;
lowercase_string(token);
Else
If token_break_char Is '['
Then
While token_break_char IsNot ']'
BeginWhile
concat_char_to_string(token, token_break_char);
order_token_get_char();
EndWhile;
order_token_get_char();
If case_ignore.val
Then
lowercase_string(token);
EndIf;
Else
concat_char_to_string(token, token_break_char);
order_token_get_char();
EndIf;
EndIf;
return;
EndCode
*1 https://ftp.sunet.se/mirror/archive/ftp.sunet.se/pub/simteln...Whether they’d ever go back and finish the migration is not known.
Stephen Bourne did this in the sources of the Bourne shell, around 1977, to make C look like Algol.
Here it is in V6 Unix, 1979:
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...
1) Write CPP macro language to emulate Oberon syntax.
2) Write program in the above macro language.
3) This program looks like Oberon but we now have two ways of "compiling" it; a) Feeding it to the CPP/C Compiler or b) Feeding it to the Oberon compiler directly with a little bit of tweaking.
Have i understood it correctly?
(https://github.com/ibara/cpaint/blob/21c70acba373df932920d5f...)
In the 80's I worked for a guy who insisted that we wrote all our C using macros that made it look like FORTRAN, amongst much other nonsense. How fondly I remember the many hilarious hours spent trying to pin down the cause of unexpected results.
I don't remember any specific examples, but consider:
#define SQ(v) v*v
int sq = SQ(++v);
In similar vein there is also the “do {} while 0” trick to allow macros to be appear like normal functions and end with semicolon.
Don’t even want to imagine how many more hacks would be needed to transform into another syntax using macros only.
Yeah that sounds like the easiest way to make your colleagues hate you
I "love" how we had more languages in the 70s (usually created as a one-off project for people with not so much user friendliness in mind) think m4, awk, tcl, etc
Some absolute lunatic solved this year's Advent of Code in m4; it was impressive.
(It was a long time ago, and there was no C compiler on our IBM/370...)
Except C.
For short scripts, awk is nice, but most people would use Python nowadays, and die hard Unix greybeards will use Perl or TCL depending on the mood.
Well I'm not Whitney's colleague but I really like his code.
I like it because it makes it easier for me to see the big picture. The forest and the mountain. It doesn't over-emphasize the bark on the trees; it doesn't drag on and make me scroll & jump through a maze of boring minute detail. At the same time, it doesn't actually bury and hide whatever detail there is; it's all there for when you need it. Whitney also generally simplifies things a lot and avoids tedious contortions others would make for portability or some theoretical conception of maintainability, readability, or vague "best practices". It's very straightforward and -- once you get past the Whitney vocabulary and general style -- there are no mountains of abstractions and layers you need to grok before you can work on the code.
The biggest problem is that after getting used to that style, "normal" code starts to feel like kindergarten books with very simple sentences written in horse sized capital letters, a handful per page. Except that those letters are not used to write a short children's story, but a complex labyrinthine machine, and the over-emphasis on minute detail just obscures the complexity and you end up with cross cutting concerns spread over thousands of lines of code and many many files. It might look clean and readable on the surface, yet: there be dragons. And nobody wants to tame those dragons because there's so much code. And then I find myself sighing and asking: why on earth do we need an entire page for what would really amount to a line of code if we didn't insist on spelling everything out like it's babby's third program after hello world? It's just tedious.
Whitney certainly writes his own sort of dragons, but it's easier to keep them all in your head. For example, the b compiler won't work out of the box on multiple platforms. I'm fairly confident I could port it without much effort, as long as the platform meets some basic requirements.
> Learn to read and it turns out not to be unreadable?
There's the thing, if someone learns Russian they can converse with 140 million people in Russia, similarly Ukrainians and Belarusians will be fine and they could probably make themselves understood through the Caucasus and Central Asia. If you learn to read C written in the Arthur Whitney style you can "converse" with a fairly small number of people who like the Whitney style[1]. So taking the example a bit further, I learned the Cyrillic alphabet in an afternoon and through knowing another Slavic language I can roughly parse the meaning of many Russian things I read (audio is another thing entirely, I can only pick out a handful of Czech/Russian homophones). If I had gotten up to speed with the ngn/k codebase would I be productive on one of the projects you wrote in a similar style, or is there a similar productivity wall where I'd have to first learn some idioms local to your codebase?
Sorry for the questions, I know people who like this style probably have to answer these questions fairly frequently. I am genuinely just quite curious though.
[0] = https://codeberg.org/ngn/k/src/branch/master/h.c
[1] = is there a proper name for this or is it ok to refer to it as "Whitney style"?
For me, the end goal isn't Whitney style, but I've been pursuing effective programming all my life. When I learned to code, I wasn't talking to anyone except my computer, and that alone was exciting enough to make it a life-long hobby and profession for me.
Do you know what brought me to Hacker News? Arc. And Paul Graham's writings about Lisp. The message was never about a popular language that everyone speaks. If anything, it was rather the opposite: pg saw in lisp a powerful and expressive (if niche) language that makes you competitive against larger players who stick to boring mainstream languages. I wasn't interested in competition or startups, but merely in powerful ways to make the computer do what you want. I don't particularly care if I'm the only person on planet earth willing to wield that power; it's for my own enjoyment. Programming for me isn't about "product" so "productivity wall" isn't something I think about, and complaining about productivity wall would be a bit like complaining that getting fit for Tour de France is time consuming, why don't they just drive it by car?
That said, I think there are people who find K, APL, and related languages very productive in their niche. I'm definitely not speaking for everyone.
Anyway, it is the curiosity and desire to discover a powerful way to command the computer that has driven me to study Haskell, APL, PostScript, forth, TLA+, lisps, BQN, K, Erlang, and more. "Whitney C" is just one milestone along the journey, and I don't know where the journey will eventually lead; I'm just not happy with any existing language right now.
So the answer is no, learning Whitney C will not make someone immediately productive with my code, just as learning Java does not make you immediately productive C++. They are different languages. However, anything you learn can shrink the productivity wall; knowing APL or BQN, K, and Whitney C might make it easier to grasp whatever I come up with next. That applies to all of programming in general though; the more you know, the more you know, and some of that knowledge will almost always transfer. There will be familiar patterns and ideas.
I also think people seriously overestimate the productivity wall. As you say, one can learn to read cyrillic in an afternoon. Kana in a weekend. But learning Russian or Japanese is significantly more work than learning the script. In terms of scope, I'd say learning APL or Whitney C is closer to learning kana than it is to learning Japanese.
(EDIT: I also find it ironic that programmers are ostensibly excited about learning new things, yet at the same time programmers really love to complain about languages that look alien won't give a few weekends to learn them)
So I find APL, J, K and friends quite fascinating (and J is on my list to try) but I haven't seen much hostility to them. People understandably get a bit intimidated by how different it is but they usually still seem curious. The real hostility is reserved for Whitney C. In this case I don't think it's like - if you'll forgive me for abusing that human language metaphor a bit - an English speaker learning Russian, more like
ifAnEnglishSpeakerEncountered
aLocaleWhereEnglishIsWrittenA
ndStructuredDifferentlyToWhat
theyWereUsedToSomethingLikeTh
is.
I can understand why their instinct is to recoil in horror and think "I already know a more standard English, I'm currently learning Russian and Japanese ... I have little patience for trying out this alternate form of English". It's obviously an exaggerated/contrived example, but this is genuinely how that C code appears to an outsider at first blush (or at least it did to me and a couple of my friends).That said your replies have piqued my interest, I'm gonna have to properly dig through that ngn/k repo some day. If I turn into a Whitney C guy then I'm holding you responsible :D
After accustomization with a style that doesn't force explicit declarations of identifiers and their types, verbose type conversions, line breaks and indentation after every statement and brace, etcetra., one could definitely make a different (and similarly exaggerated) human language metaphor. For example, take some English text and feed it through a parser. Feels good to read?
(S (NP Parsing)
(VP refers
(PP to
(NP (NP the activity)
(PP of
(S (VP analysing
(NP a sentence)
(PP into
(NP its component categories and functions))))))))
.)
That's a bit how mainstream languages feel after using something that hasn't been forced into such an artificial form :) If you're willing to let go of that, you can write sentences and clauses on the same line, almost like prose!