In this programming-puzzle, 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 theFunction
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.
3 Answers 3
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([,])
(削除) 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
.
-
3\$\begingroup\$
[,]
saves 3 bytes. \$\endgroup\$Neil– Neil2023年12月10日 00:27:54 +00:00Commented 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– emanresu A2023年12月10日 00:30:44 +00:00Commented Dec 10, 2023 at 0:30
-
2\$\begingroup\$ Fortunately
find
loops from0
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– Neil2023年12月10日 00:50:54 +00:00Commented Dec 10, 2023 at 0:50 -
\$\begingroup\$ This does call
f
with 3 arguments though \$\endgroup\$Bergi– Bergi2023年12月10日 23:08:45 +00:00Commented 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– emanresu A2023年12月11日 00:03:00 +00:00Commented Dec 11, 2023 at 0:03
JavaScript (Node.js), (削除) 74 (削除ここまで) 65 bytes
[].forEach.bind([{[Symbol.split]:(c=Map.call).bind(c)}],''.split)
-
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– Bergi2023年12月16日 01:05:50 +00:00Commented Dec 16, 2023 at 1:05 -
-
\$\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– TwiN2023年12月16日 06:24:55 +00:00Commented Dec 16, 2023 at 6:24 -
1\$\begingroup\$ So, calling
run(f)
runsforEach
on that inner object, passing it as the argument toString.prototype.split
, withf
as thethisArg
.split
then calls theSymbol.split
function of that inner object, passingf
and0
as arguments. That's equivalent to callingFunction.prototype.call.call(f, 0)
which is equivalent to callingf.call(0)
, which is equivalent to settingf
as a property onNumber.prototype
and invoking that on0
. \$\endgroup\$Neil– Neil2023年12月16日 11:52:32 +00:00Commented Dec 16, 2023 at 11:52 -
1\$\begingroup\$ @Neil See also my explanation I had tried to edit into the answer \$\endgroup\$Bergi– Bergi2023年12月20日 07:55:31 +00:00Commented Dec 20, 2023 at 7:55
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
Explore related questions
See similar questions with these tags.
()=>{}
orfunction*(){}
? \$\endgroup\$[].find
like emanresu uses) \$\endgroup\$.name
and.length
don't matter, nor does thethis
valuef
sees. As for the "returningundefined
" 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\$