In the late 90s, I interviewed for a job as a Technical Product Manager with a Toronto startup. The CTO/Co-founder, was an obfuscated C contest winner: https://www.ioccc.org/1985/lycklama/index.html
Great guy to work with. I think he runs a craft brewery now.
TC39 member here; There are a lot more ways in JS to call a function without parenthesis. The first thing to come to my mind is Symbol.isConcatSpreadable or Symbol.iterator which works in multiple places
I wonder if there was a period in the 90s where the chess community was more interested in talking about IBM’s stock price than in actually playing chess.
It feels like it's from back when we unironically thought memorizing quirks like this makes us better programmers, or is somehow important for making computers more secure, aka middle of the 2010s tech bubble.
One of the things I love about programming in dialects of ML like ocaml and f# is the forward pipe operator:
x
|> f
|> g
|> h
It's nice because it just puts whatever's on the left of the operator in as the last argument. So it also works for functions with an arity of more than one.
So if it were something like
h(z, g(y, f(x))
you can write it as
x
|> f
|> g y
|> h z
It works really nicely for data munging code:
my_data
|> filter (x => x > 37)
|> select (x => x * 2)
|> sum
but unlike doing it with method calls in object oriented code, inserting a new function into the chain doesn't involve modifying a class or or mucking around with more complex language features like extension methods and traits.
In ML family languages you also don't have to change the language for this, as function application is naturally "argument-by-argument" (the term is "curried"): the syntax is normally f x y z but (f x y) z and (((f)) (x)) y z are just as valid, and indeed f x y will be a function taking one argument. Thus |> can just take a function of one argument and be done.
Maybe I've just been doing too much Racket lately and that's affecting how I view things, but it's real hard for me to not think of lisp macros as a mechanism for changing the language itself. Especially if it's something like a reader macro.
A lot of Common Lisp is like this. They took things that were used to implement standard parts of the language, and went "you know, users might also want to have these", and exposed them as part of the standard. If I were to extend Common Lisp I'd standardize and expose more things like this.
So would you like to write shell commands connected with pipes like that too?
So is this better too
$ less | find / -type f -perm /6000 -ls
than ?
$ find / -type f -perm /6000 -ls | less
Just because shell actually starts executables right to left when using pipes.
Reason to this behaviour is because it can fail early if there is problem constructing pipe first and not spend lot of time something finding later that some pipe components can't start and receive data properly.
ps. I'm fine with current notation and would not like change it.
I think he actually wants pipes. Instead of having to write:
f3(f2(f1(some data)))
it would be nice to write
some data |> f1 |> f2 |> f3
javascript has proposals for pipe operators because as soon as the simple .map /.filter/.reduce APIs don't suffice (ie. as soon as Promise.all is needed) the f3(f2(f1(data))) mess starts.
In a lazy language like Haskell, the order in which things are actually evaluated is f -> g -> h -> x, because arguments to a function are only evaluated when the are needed, rather than before they are called. But even so, I still prefer writing it as x |> h |> g |> f.
When you're calling a function, you're putting some things into the function... You have to get the things first before you can put them in...
Why didn't it evolve like that in the first place? Is it a byproduct of mathematical notation? If so, I wonder why mathematical notation evolved that way in the first place...
Forth (or Postscript by the way) works exactly like that, but writing the function first also makes sense in functional programming, specifically when you care about currying functions: writing "f(g(h(x)))" (or "f g h x" or whetever) makes more sense, since you are not supposed to put all expected arguments at once.
Do you have to write a function first before it is referenced in PostScript? (I did a lot of PostScript programming in NeWS once - but it was ~35 years ago...)
Did you even have an RPN-based calculator at school?
As to more serious programming languages, Erlang, F#, Haskell have forms of a "pipe" operator, roughly
[1 2 3] |> sum |> print
For it to work properly, functions should be either functions of one argument, or support currying.
But the original algebra functions, as introduced by Leibniz in 17th century, were, of course, pure functions, more like properties than procedures. So cos(x), aka cos x, is just a formalized shorthand for "cosine of x", so it preserved the order of words typical for Germanic and Romance languages. Maybe if functions were introduced by the Japanese, we would have the reverse order, kind of x の cos.
Stack based languages and the APL tradition all follow this pattern, which I agree can be more terse and nice too. I've also been experimenting with right-associative calls which does the same thing but in reverse. For a data pipeline you're more likely to transform from A->B, so I think the post-fix syntax makes sense.
But you can also define a "thread" operator easily for doing subsequent function calls. ala `threaded x { f; g; h }`
I've thought about this before. It gets harder when dealing with >2 inputs and names longer than 1 letter, so I organized it with lines to make it look more tree-like.
The function reference is actually evaluated first so your preferred order is actually backwards! Consider this:
foo.bar(x)
The `foo.bar` field has to be retrieved before the function can be called. It does not evaluate `x` first. That is observable with JS proxies, or you can test with code like this:
For some reason this made me think about all of the potential complexities that can be involved in CLOS in calling a generic function and some method(s) actually being invoked...
I honestly thought of typing that one out here, lol.
You have the camera... I have the monitor... How about I flash QR codes? Oh, the camera is on top of the monitor, facing the room? Well, at night, I could FLASH the monitor full white in binary, and you can see the light on the chair and wall, right?
I'll leave the disc head on the sector number. You read the disc head position.
I'll exit my function on a time modulo a number to encode the remainder. You see what time you were called, and can decode the remainder.
I'll warm the CPU to a temperature... you read the temperature...
Object.prototype.message='alert\x281337\x29';Array.prototype.toString=Error.prototype.toString;Error.prepareStackTrace = Function;const x = new Error;x.valueOf=x.stack;+x;
Well, that's a mouthful! So many years and JavaScript still can do the most amazing, weird, awesome, scary, obscure, and sexy things in the world of coding.
The prototype thing is quite unique among mainstream languages. It is kind of simple in principle and works well for what it intends to do, but god it causes so much confusion and is frequently a source of CVEs. valueOf combined with implicit conversion is a nightmare for production code.
Yesterday I was re-using some shaders I had written a long time ago, and here's what I used to do multiline strings before the glorious backtick arrived on the scene...
(function () {/*
my awesome shader code
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]
... I can't call it sexy, but I also can't deny I felt a mixture of fear and arousal when I saw that.
I believe this is the regular syntax for a set of characters (between "[" and "]"), but the caret symbol ("^") is negation when used inside the set. Therefore, it is simply the negation of an empty set of characters.
If I were to write a book on programming in JS, it would be a black cover with the JS Holy Trinity equality diagram in red, to emphasize its Satanic nature.
The reason is that the language was designed in a week with a "product manager" pushing the developer's design into something that only has "is a programming language" in common with it during that week.
Doesn’t feel LLM-y to me. If it is LLM-generated, the LLM must have been explicitly instructed to ignore sentence capitalization and invent a persona (‘i chose...’).
I'm not pro-AI (at least not the way the SOTA model companies operate) but I'm tired of seeing "written by AI" on every single thing on HN.
I get "wRiTteN bY aI!" comments on my open source projects when, in fact, I didn't use AI at all. I've considered sharing some of my recent reverse engineering work on HN but am confident that it would devolve into "it must have been written by AI" bs.
Is it important to know if something was created with AI? Yes. But spewing "written by AI!" without reading/evaluating something yourself is just as lazy.
The new generation of engineers will never know a world where all software was written by humans. As someone who spent 22 years writing assembly and low-level C/C++, I think that's a shame. But that is the new reality.
One of the reasons the world is so fucked up is because people have lost respect for expertise. I was on the original Visual Studio team at Microsoft and helped rebuild Xcode from scratch at Apple. I have 30 years worth of experience building low-level software and that experience matters.
If we were talking about cancer treatments and someone said, "Hey, I've been an oncologist for three decades" it would be ridiculous to not at least listen to what what they had to say.
Experience and expertise matters; now more than ever.
Lastly, why create a new account just to make that comment?
> We stopped when we got a clean compile on the following syntax:
‘Larry, throw the cat at the keyboard!’
MEEEOW!
‘What did it type?’
‘&}Ak'>v=4%Qe}9;’
‘OK, so let’s make this a syntax for hello world.’
I haven't seen it in many many years, but at the time it struck my giggle button every time.
https://www.mcmillen.dev/sigbovik/2019.pdf
here's an actual one: print'(%,,/@(/),$!'^'`@@@@`_@[@@+'
another one
Bareword found where operator expected (Missing operator before "Ak"?) at -e line 1, near "&}Ak" Bad name after Ak' at -e line 1.
https://www.ioccc.org
Great guy to work with. I think he runs a craft brewery now.
https://donhopkins.com/home/catalog/unix-haters/etc/unix-is-...
That will also serves as a litmus test to check how many of my coworkers are reading the code they push.
“Back in my days…”
“No true Scotsman…”
“I remember when…”
“Kids these days…”
Gotta love this self-aggrandising humble-brag nonsensical stuff.
So if it were something like
you can write it as It works really nicely for data munging code: but unlike doing it with method calls in object oriented code, inserting a new function into the chain doesn't involve modifying a class or or mucking around with more complex language features like extension methods and traits.since the order in which things happen is "h first, then g, then f"
So is this better too
than ? Just because shell actually starts executables right to left when using pipes. Reason to this behaviour is because it can fail early if there is problem constructing pipe first and not spend lot of time something finding later that some pipe components can't start and receive data properly.ps. I'm fine with current notation and would not like change it.
When you're calling a function, you're putting some things into the function... You have to get the things first before you can put them in...
Why didn't it evolve like that in the first place? Is it a byproduct of mathematical notation? If so, I wonder why mathematical notation evolved that way in the first place...
It did, just in Arabic.
As to more serious programming languages, Erlang, F#, Haskell have forms of a "pipe" operator, roughly
For it to work properly, functions should be either functions of one argument, or support currying.But the original algebra functions, as introduced by Leibniz in 17th century, were, of course, pure functions, more like properties than procedures. So cos(x), aka cos x, is just a formalized shorthand for "cosine of x", so it preserved the order of words typical for Germanic and Romance languages. Maybe if functions were introduced by the Japanese, we would have the reverse order, kind of x の cos.
But you can also define a "thread" operator easily for doing subsequent function calls. ala `threaded x { f; g; h }`
f(g(h(x))) as x.h().g().f() or even x.h.g.f (parenthesis are optional).
It's more natural to write
But my point is that the reference to the function you are calling is actually its own term that needs to be considered in the order of evaluation.
You can also use the IIFE (immediately invoked function expression) pattern that is/was common in JS as an example:
You can use any valid expression in place of a function name, including anything that causes observable side effects.Global variable.
Parameter to a function.
Pass a functor/lambda, get the value.
Environment variable.
Registry.
File.
Store it in a ping response.
VRAM texture that you write and then read.
Database.
REST server.
I'm pretty sure I could come up with a thousand ways.
But... why? Why do I want to write this book? Why has it been on my mind?
And something my friend wrote a few years ago: https://github.com/radian628/tram
Data storage and transmission has so much innovation going on!
It's very compiler-focused, but it's one piece of the puzzle.
You have the camera... I have the monitor... How about I flash QR codes? Oh, the camera is on top of the monitor, facing the room? Well, at night, I could FLASH the monitor full white in binary, and you can see the light on the chair and wall, right?
I'll leave the disc head on the sector number. You read the disc head position.
I'll exit my function on a time modulo a number to encode the remainder. You see what time you were called, and can decode the remainder.
I'll warm the CPU to a temperature... you read the temperature...
The list goes on and on...
"The Exfiltration Manual"...?
aka "Chrome implemented this and wanted everyone else to do so as well"
> \x28
> \x29
does this not count as using parentheses?
The sooner that abomination is killed, the best.
(Though, parentheses are sometimes needed due to ambiguity. But by and large, the lispy legacy has been overcome already.)
Regardless, I don't think it's correct on this one.
I get "wRiTteN bY aI!" comments on my open source projects when, in fact, I didn't use AI at all. I've considered sharing some of my recent reverse engineering work on HN but am confident that it would devolve into "it must have been written by AI" bs.
Is it important to know if something was created with AI? Yes. But spewing "written by AI!" without reading/evaluating something yourself is just as lazy.
The new generation of engineers will never know a world where all software was written by humans. As someone who spent 22 years writing assembly and low-level C/C++, I think that's a shame. But that is the new reality.
One of the reasons the world is so fucked up is because people have lost respect for expertise. I was on the original Visual Studio team at Microsoft and helped rebuild Xcode from scratch at Apple. I have 30 years worth of experience building low-level software and that experience matters.
If we were talking about cancer treatments and someone said, "Hey, I've been an oncologist for three decades" it would be ridiculous to not at least listen to what what they had to say.
Experience and expertise matters; now more than ever.
Lastly, why create a new account just to make that comment?