Yet another way to invoke functions in JavaScript without parentheses

5 days ago (blog.ikaes.de)

This reminds me of "Creators admit Unix, C hoax" (e.g. https://www.gnu.org/fun/jokes/unix-hoax.html)

> We stopped when we got a clean compile on the following syntax:

    for(;P("\n"),R--;P("|"))for(e=C;e--;P("_"+(*u++/8)%2))P("| "+(*u/4)%2);

I love this. This is a fun hack, but the article also hearkens back to pre-LLM days when people were still interested in _code_.

I want to write a super pedantic book showing every way I can think of to move data from here, to there. In the same program, or between programs.

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?

  • You might be interested in the book "Data Flow Analysis" by Khedker et al.

    It's very compiler-focused, but it's one piece of the puzzle.

  • Implement your own delay-line memory with the speaker, microphone, and echo of the room.

    • 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...

      The list goes on and on...

      "The Exfiltration Manual"...?

  • 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 want to put the called function after the arguments:

    (((x)f)g)h

So I can see exactly the order in which things happen, from left to right. As opposed to:

    f(g(h(x)))

where I have to read backwards.

  • 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.

    • There's also function composition

         (h . g . f) x
      

      Or for LtR, with Control.Category

         (f >>> g >>> h) x
      

      Could combine with the forward pipe for

         x |> (f >>> g >>> h)

  • Do you mean (((x)h)g)f ?

    since the order in which things happen is "h first, then g, then f"

  • 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.

      2 replies →

  • 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.

  • Assuming you mean h)g)f, you can do it in D. D allows you to write this:

    f(g(h(x))) as x.h().g().f() or even x.h.g.f (parenthesis are optional).

  • That actually makes so much sense...

    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.

      1 reply →

  • 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 want to put the function below the arguments, so that the call tree actually looks like a tree

        u v x y
         f   g
           h
    

    This is h(f(u,v), g(x,y)).

    • 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.

        │u│v│x│y
        ├─┴─├─┴─
        │f  │g
        ├───┴─
        │h
        └─
      

      With >2 inputs and longer names.

        │uh│v  │xizzy│yeez
        ├──┴─  ├─────┴────
        │fooey │gar
        ├──────┴───
        │hello
        └─────
      

      The associative property.

        (a+b)+c=a+(b+c)=a+b+c
      
        │a│b    │b│c
        ├─┴─    ├─┴─
        │+  │c│a│+  │a│b│c
        ├───┴─├─┴─  ├─┴─┴─
        │+    │+    │+
        ├─────┴─────┴─
        │=
        └─
      

      The quadratic formula.

        (-b±sqrt(b^2-4*a*c))/(2*a)
        
          │b│2│4│a│c
          ├─┴─├─┴─┴─
          │^  │*
          ├───┴─
        │b│-    
        ├─├─    
        │-│sqrt│2│a
        ├─┴────├─┴─
        │±     │*
        ├──────┴─
        │/
        └─
      

      Obviously, it takes way more space and time to draw. Tell me if you like it!

      1 reply →

  • 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.

  • And that's why "purely procedural" languages don't make much sense to me

    It's more natural to write

        x.h().g().f()

  • If `f` is an expression, that would require it to be evaluated last as well.

  • you can start with

        var chain = x=>f=>f?chain(f(x)):x;
        chain(x)(h)(g)(f)();
    

    if you need to partly curry to free first argument

        var _ = (f,...y)=>x=>f(x,...y);
        var sum = (x,y)=>x+y;
        chain(1)(_(sum,1))()
    

    if you want whole pipeline

        var $ = (x,...f)=>f.reduce((x,f)=>f(x),x);
        $(1,_(sum,1));
    

    which gives fun ability to extract function without input param

        var sum1 = _($,_(sum,1));
        sum1(1);
    

    ... which in turn is just having fun with point-free JS

  • 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:

      foo()(bar())

Not gonna work on Firefox, since according to the doc, Error.prepareStackTrace is an optional API and only implemented by V8.

  • > optional API and only implemented by V8

    aka "Chrome implemented this and wanted everyone else to do so as well"

  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.

    • Pretty interesting that you can carry comments over.

          (x=>/*this is a comment*/1) + 1
          "x=>/*this is a comment*/11"
      

      I wonder if anything cool can be done with it today. It's probably useless.

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

> without parentheses

> \x28

> \x29

does this not count as using parentheses?

  • Not when it comes to bad input sanitizers that only look for literal "()".

It is called ... Ruby.

(Though, parentheses are sometimes needed due to ambiguity. But by and large, the lispy legacy has been overcome already.)

Could this be used in obfuscation? We should try to apply it and see if LLM can deobfuscate it.

[flagged]