12
\$\begingroup\$

In this , your goal is to write the following function in JavaScript, using (essentially) point-free (tacit) programming:

(f) => void(f())

Your submission should be an expression which returns a function which behaves like the above (taking a function as an argument and calling it, and returning undefined), without writing a function definition (defined objectively below). You must instead achieve the behavior by combining existing functions (e.g., Function.prototype.apply).

Any JS is valid, with the exception of the following:

  • Function definitions using the function keyword, arrow function syntax, or object/class method syntax
  • Function definitions using new Function or the Function constructor
  • Evaluation of code using eval (directly or indirectly)
  • Relying on browser-only functionality or import/filesystem/OS/FFI libraries in Node.js

If this is not possible, a proof of its impossibility is also a valid submission. Solutions will be scored by byte count; smallest solution is best.

\$\endgroup\$
12
  • \$\begingroup\$ May a submission use a function that has no body like ()=>{} or function*(){}? \$\endgroup\$ Commented Dec 10, 2023 at 14:02
  • \$\begingroup\$ @noodleman No, but most things you could do with that, you could probably do with an existing function in the language (e.g., [].find like emanresu uses) \$\endgroup\$
    – rydwolf
    Commented Dec 10, 2023 at 18:09
  • 1
    \$\begingroup\$ Must the submission pass no argument to the function? \$\endgroup\$ Commented Dec 10, 2023 at 20:16
  • 1
    \$\begingroup\$ @noodleman Yes, but if you have an interesting solution that does pass an argument, maybe post it in an answer alongside a more valid (if ungolfy) variant \$\endgroup\$
    – rydwolf
    Commented Dec 10, 2023 at 22:49
  • 1
    \$\begingroup\$ @Bergi The solution's .name and .length don't matter, nor does the this value f sees. As for the "returning undefined" part, it just happened to be part of the problem I was idly attempting while bored at work; it's not for any clever reason. \$\endgroup\$
    – rydwolf
    Commented Dec 11, 2023 at 4:20

3 Answers 3

12
\$\begingroup\$

Why I believe this is impossible (as stated)

There aren't very many JS builtins that call a function, and even fewer that call one with arbitrary arguments. The ones that I've found are the string methods replace and replaceAll, the array methods every, filter, find, findIndex, findLast, findLastIndex, flatMap, forEach, map, reduce, reduceRight, and some; the the function methods apply and call; the global functions Reflect.apply, setTimeout, setInterval, the Promise constructor and (browser only) requestAnimationFrame and addEventListener, and also some weird stuff like RegExp[@@replace].

All the array methods are useless, as they all call functions with the signature f(item, index, array).

There are a few that call the function but can't return undefined. String#replace (along with String#replaceAll and RegExp[@@replace]) almost work with ''.replace.bind('', /(?:)/g), but returns the function's result stringified. setTimeout ignores the function's return value, but returns a uid for that particular call (ditto with setInterval).

Likewise, the function methods apply and call return the return value of whatever function they're called with. Reflect.construct can be used to trigger the Promise constructor, but requires the arguments passed to the constructor function to be placed in an array.

Event listeners are somewhat promising, taking a function and returning undefined, but there's no way to create an object which has an event occur on it every time the function is called. window.addEventListener.bind(window, 'click') (browser only) does work to call a function every time a click is called though.

As calling the expression with the input function has to be the last operation performed on the expression, I don't think there's any way to transform whatever non-undefined value is returned into undefined. And since there's no built-in function capable of both instantly calling the function a single time without arguments and returning undefined, I don't think this challenge is possible.


(old answer, calls with nonempty args) 17 bytes

[].find.bind([,])

Try it online!

I'm pretty sure this is optimal -3 bytes thanks to Neil.

Calls the function on an array [,], passing undefined to it. If the function's return value is truthy, the undefined is returned; if it's falsy the find call fails and returns undefined.

\$\endgroup\$
6
  • 3
    \$\begingroup\$ [,] saves 3 bytes. \$\endgroup\$
    – Neil
    Commented Dec 10, 2023 at 0:27
  • 1
    \$\begingroup\$ @Neil I'm surprised that works, it feels like it should be either 0 calls or 2 calls. Sparse arrays are weird. Thanks! \$\endgroup\$
    – emanresu A
    Commented Dec 10, 2023 at 0:30
  • 2
    \$\begingroup\$ Fortunately find loops from 0 to the length (the object you bind it to doesn't even need to be an array as long as it has an integer length). \$\endgroup\$
    – Neil
    Commented Dec 10, 2023 at 0:50
  • \$\begingroup\$ This does call f with 3 arguments though \$\endgroup\$
    – Bergi
    Commented Dec 10, 2023 at 23:08
  • \$\begingroup\$ @Bergi I forgot find works like that, and you're right that calling it with undefined isn't quite the same as calling it with no arguments. I guess we'll wait for OP's clarifications. \$\endgroup\$
    – emanresu A
    Commented Dec 11, 2023 at 0:03
10
+100
\$\begingroup\$

JavaScript (Node.js), 74 65 bytes

[].forEach.bind([{[Symbol.split]:(c=Map.call).bind(c)}],''.split)

Attempt This Online!

\$\endgroup\$
5
  • 2
    \$\begingroup\$ This is absolutely awesome! Calling String.prototype.split with a function as the receiver, because that'll be forwarded to the [Symbol.split] method, is just crazy… \$\endgroup\$
    – Bergi
    Commented Dec 16, 2023 at 1:05
  • \$\begingroup\$ -9: you don't actually need a length on the splittee for anything (demo) \$\endgroup\$
    – Bergi
    Commented Dec 16, 2023 at 1:07
  • \$\begingroup\$ @Bergi Oh true. I added that because I was planning to use Function#apply to remove any additional args but did not check again whether that is necessary after I finished. \$\endgroup\$
    – TwiN
    Commented Dec 16, 2023 at 6:24
  • 1
    \$\begingroup\$ So, calling run(f) runs forEach on that inner object, passing it as the argument to String.prototype.split, with f as the thisArg. split then calls the Symbol.split function of that inner object, passing f and 0 as arguments. That's equivalent to calling Function.prototype.call.call(f, 0) which is equivalent to calling f.call(0), which is equivalent to setting f as a property on Number.prototype and invoking that on 0. \$\endgroup\$
    – Neil
    Commented Dec 16, 2023 at 11:52
  • 1
    \$\begingroup\$ @Neil See also my explanation I had tried to edit into the answer \$\endgroup\$
    – Bergi
    Commented Dec 20, 2023 at 7:55
4
\$\begingroup\$

It looks like a loophole in the function definition restriction, but you can still define a setter:

({set s(f){f()}}).__lookupSetter__('s')

__lookupSetter__ is non-standard and deprecated, but shorter than

Object.getOwnPropertyDescriptor({set s(f){f()}},'s').set
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.