from Hacker News

Programming with Less Than Nothing

by signa11 on 10/23/25, 5:42 AM with 151 comments

  • by JonChesterfield on 10/23/25, 11:28 AM

    Since people are a bit WTF is this, here's a point to combinators.

    A combinator is a function that doesn't mutate global state and doesn't close over variables. It's the base case in software. Pass it the same argument, get the same result, doesn't change anything about the rest of the system.

    If you combine some of these combinators, you get another combinator - because when you put pure functions together, what you get is a pure function.

    These are also the functions that are easy to write in assembly. Or C. Because the don't do very much. So if you write S and K in x64, and then compile common lisp to combinators written in terms of combinators written in terms of S and K, what you've got is common lisp running on those two hand written assembly functions.

    That's not a great idea for performance, but if you go with a less spartan set of maybe a few hundred of the most commonly occurring patterns inlined into each other and given names, you've got a viable way "machine" to compile functional programs into.

    This looks a bit like a forth that fears mutation. Or a bytecode vm. Or a CPU, where the combinators are called "instructions".

    So what combinators are, broadly, is an expression of applied computer science with implementation details ignored as much as possible. That's the sense in which it's simpler than the lambda calculus.

    Equally, if you implement the lambda calculus on a handful of combinators, then implement lisp on the lambda calculus, then write stuff in that lisp, you've really cut down how much machine specific work needs to be done at the bottom of the stack.

  • by tromp on 10/23/25, 8:41 AM

        let A = (x) => (y) => (z) => x(z)(y((w) => z))
    
    Just need to combine this a few times.

        let K = A(A(A))(A(A(A))(A)(A)(A)(A)(A); // (x) => (y) => x
        let S = A(A(A(A)(A(A)(A(A)))(A(A(A(A)((A(A)))))))(A)(A); // (x) => (y) => (z) => x(z)(y(z))
    
    > “I would never be caught dead using Lambda calculus. It’s a bloated language.”

    Actually, combinatory logic is more bloated than lambda calculus, generally needing more bits to express the same program [1]. One can argue that lambda calculus is one of the most concise languages ever [2].

    > Dana smirks. “Well, yeah. JavaScript is an eager language. Can’t use the Y combinator.”

    Eager languages can be made lazy by wrapping arguments in lambdas with dummy arguments, as done in the Javascript BLC interpreter [3].

    [1] https://tromp.github.io/cl/LC.pdf

    [2] https://www.ioccc.org/2012/tromp/

    [3] https://github.com/tromp/AIT/blob/master/uni.js#L56

  • by egypturnash on 10/23/25, 2:32 PM

    “Are you familiar with fizzbuzz?”

    “Yes. Do you want the ten-character code golf version, the twelve-line eminently readable version that the junior devs can understand, or the one that’s an excuse to show off some extremely esoteric knowledge and do it in a completely sideways fashion?”

  • by andersmurphy on 10/23/25, 7:37 AM

    Awesome explainer of combinator logic, the writing style reminded me of this series of posts (which is always a good thing in my opinion):

    https://aphyr.com/posts/353-rewriting-the-technical-intervie...

  • by RickJWagner on 10/23/25, 12:20 PM

    I’ve retired after a 35 year career in programming.

    I knew programmers that could write code I wouldn’t understand. Some of them wrote large, valuable systems. I drew the conclusion that some people think in different ways, some had cognitive gifts different from my own. Some were just better at some things.

    But the very best programmers wrote code I could follow. It was dirt simple and well documented. Their code made me feel smart as I maintained it. I could understand what the other person was thinking, how the next section would probably go. We were sharing ideas and architectures, but may have never met!

    I really liked working with that second type.

  • by rgovostes on 10/23/25, 7:48 AM

    The last code block scrolls, by the way. It's 166 kB.

    Note that S and K are curried functions which take one argument at a time. Further reading: https://stackoverflow.com/a/36321/

  • by aleph_minus_one on 10/23/25, 11:09 AM

    While the constructions are for sure interesting for people who love thinking about different programming models, I opine that the story framing with the programming interview is "wrong":

    Programming interviews serve two purposes to find out:

    1. Is the candidate knowledgeable in programming?

    2. Does the programming style (somewhat) fit the one that is desired by the company?

    1 should be clear, and the author clearly passes this part.

    For 2: In particular since programming is separated into so many "cultures" of which many of them take deep effort to get more than a superficial knowledge (that's why you talk of "Java shops", "Microsoft/C# shops", ...), in a programming interview, you also want to see whether the candidate will likely be a fit for the prevalent programming culture in the respective company. If you advertise a JavaScript position, it is highly unlikely that if you are that deeply into combinatory logic, you will be a good fit for this position - and thus he would very likely (for good reasons) be rejected if he came up with such a solution.

  • by vorticalbox on 10/23/25, 4:29 PM

    This reminds me of one I read a few years ago “fizzbuzz in tensorflow”

    https://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/

  • by hanslub42 on 10/23/25, 8:13 PM

    This would be a good place to remember Moses Schönfinkel, who invented Combinatory Logic as a student of David Hilbert at Göttingen in 1920. His life was tragic: after his return to his native Russia he became mentally ill, and he died in Moscow in poverty. According to Wikipedia, "His papers were burned by his neighbors for heating".
  • by TYPE_FASTER on 10/23/25, 1:18 PM

    > Bluebird, cardinal, warbler, thrush. Avian friends you know well.

    And I have a new favorite naming convention.

    > “Barendregt. Church is too mainstream.” > “It won’t be JavaScript for much longer.”

    Douglas Adams teaches SICP.

  • by dvt on 10/23/25, 12:54 PM

    > “Is that the… Y combinator?” Dana asks.

    > “Can’t recurse without it.”

    Fun fact: there are an infinite number of fixed-point combinators (and therefore infinite ways you can recurse without the Y combinator).

  • by fschuett on 10/23/25, 12:04 PM

    What inane, impure nonsense. You are still using "execution" and "output", these filthy, contingent effects of reality. Why would you use THREE entire combinators, when you could've just defined ιx = xSK. Pathetic. A true, pure program never needs to run. It just exists as a proof, and by its existence as such it simply reconfigures the consciousness of the user to align with its obvious, Manichaean-esque purity. "Running" the "program", please. What are we, barbarians?
  • by pbohun on 10/23/25, 2:42 PM

    Haha! You know, I think this is a perfect illustration between something being mathematically beautiful verses pragmatically beautiful. The beauty of one often looks ugly by the standards of the other.
  • by mrkeen on 10/23/25, 10:49 AM

    > Dana smirks. “Well, yeah. JavaScript is an eager language. Can’t use the Y combinator.”

    Does anyone know off the top of their head if the Z combinator would just work here? (Instead of switching to Skoobert)

  • by underdeserver on 10/23/25, 5:27 PM

    If you're interested, the variable-less version takes up 166,664 characters, out of 178,071 characters in the page (counted by Cmd+A, so including newlines and tabs, but no markup), or 93.6% of the output.

    Since Astro throws in a lot of inline styles (though it gave up halfway through, which is what prompted me to do this math), the line in the HTML that produces that textarea is 558,580 bytes long (or about 3.3 times as big).

    It all adds up to a 700k page.

  • by mgh95 on 10/23/25, 7:28 AM

    ** SPOILER **

    > You lean back, exhausted but triumphant.

    > Dana is dead.

    Thank you for a good laugh.

  • by pjmlp on 10/23/25, 8:27 AM

    And after all of this, all that one gets to do is using AI prompts to configure integrations on a MACH architecture cloud product, fetching data from a content cloud into a CMS.

    Great article by the way.

  • by tripzilch on 10/24/25, 10:20 AM

    BTW there's some variants on the Y or Z combinators that do allow for recursion with plain non-lazy JS. It's been a few months since I did this, and the details evaporate from the mind rather quickly when you don't use the knowledge :) Afaik the idea was to put some kind of indirection/evaluation "speed bump" type of thing around the recursive part.

    That said, I don't think you actually _need_ recursion for a finite problem like FizzBuzz? The Church Numeral for 100 is literally the function `f f f f f f f.. f x` nested 100 times. So that's your loop.

  • by bryanrasmussen on 10/23/25, 9:40 AM

    I wonder if this guy has a problem getting hired.
  • by andreybaskov on 10/23/25, 5:06 PM

    Surprised no one mentioned Tree Calculus - https://treecalcul.us/. It uses binary trees to represent functions and data. K=ΔΔ, S=Δ(Δx), and so on. Though I won't pretend I fully understand it.
  • by amelius on 10/23/25, 11:30 AM

    To make it closer to less than nothing I suggest to use this programming language:

    https://en.wikipedia.org/wiki/Whitespace_(programming_langua...

  • by kragen on 10/23/25, 5:31 PM

    This is a fanfic of aphyr's "Foobaring the Technical Interview" series, though with less literary flair: https://aphyr.com/tags/interviews

    I hadn't heard of Barendregt numerals before; the reference seems to be to Barendregt, H.P. (1976). A global representation of the recursive functions in the lambda calculus, Theoretical Computer Science 3, pp. 225–242. https://repository.ubn.ru.nl/bitstream/handle/2066/17239/132.... The bit about numerals starts in §4.1 on p. 238 (p. 15/19), so you can just skip there if you don't need an introduction to the λ-calculus and Schönfinkel/Curry combinatory logic.

    I wonder if the academic literature would be more enjoyable to read if it were still structured as dialogues, the way it originally was—Hofstadter and Galileo were just calling back to Plato. I think aphyr and Moody fall somewhat short of the standards they set, since the antagonists of their dialogues never raise any real objections, serving only as flimsy, symbolic opposition to the Invincible Hero Mary Sue protagonists, who never make any mistakes or change their minds about anything. As I wrote yesterday in https://news.ycombinator.com/item?id=45669385, narratives are central to how the humans understand anything; even adept, experienced programmers often anthropomorphize parts of their programs, indulging in the "this guy talks to that guy" metaphor that Dijkstra so famously deplored, on the perhaps reasonable grounds that it led to illogical thinking.

  • by latexr on 10/23/25, 9:23 AM

    You explained the thing, gave references to learn more about the thing, and even listed one con of the thing:

    > It is also extremely difficult to understand.

    But nowhere do I see a reason why we should learn the thing. Is It useful in any way? Is it just a curiosity? Does it develop your thinking? Any reason is fine, but if you don’t give one we’re just left looking at something which looks both complex and useless, so why would we learn further?

    To really drive the point home, I have no doubt this would be fun to learn and even useful for a certain kind of people. But because you don’t say, we don’t know if we fit the bill.

  • by 0xCE0 on 10/24/25, 10:16 AM

    If someone wants to see SKs in their dreams, read following :)

    https://writings.stephenwolfram.com/2020/12/combinators-a-ce...

  • by abstractspoon on 10/23/25, 6:51 AM

    Read all the way to the end!
  • by mikewarot on 10/23/25, 5:21 PM

    It seems to me that we just need to update the standard FizzBuzz question to count upward from 1 Billion to eliminate shenanigans like those in the article.
  • by TedHerman on 10/23/25, 11:44 AM

    Shout out to those hardcores using De Bruijn notation. And bonus points if you're using just S, K, and I. Points off if you're using Y or some other fancy combinators.
  • by JadeNB on 10/23/25, 9:42 AM

    Lots of fun, though I think "less than nothing" oversells (undersells?) it. I appreciate the author's taking time to explain somewhat at the end, rather than leaving the reader just to feel stupid if they miss, for example, the reference to Smullyan. I also can't help wondering if Dana is just a choice of name, or a cheeky reference to Dana Scott.
  • by gradschool on 10/23/25, 3:16 PM

    I know everyone says SK combinators can express any computable function, but I don't get it. How do we write this function foo in terms of SK combinators alone? Is there some obvious programming trick I'm missing that makes it trivial? (It wouldn't be the first time.)

    foo(x) = if (x == K) return S else return K

  • by andyferris on 10/23/25, 1:09 PM

    Sadly, it doesn't actually run in NodeJS :(

        Uncaught RangeError: Maximum call stack size exceeded
            at REPL2:1:16
            at REPL1:1:30
            at REPL1:1:34
            at REPL1:1:30
            at REPL1:1:30
            at REPL1:1:30
            at REPL1:1:35
            at REPL1:1:34
            at REPL1:1:34
  • by tonyedgecombe on 10/23/25, 9:41 AM

    I did something similar by abusing Func and dynamic in C#: https://github.com/tonyedgecombe/functionalfizzbuzz/blob/mas...
  • by beepbooptheory on 10/23/25, 6:18 PM

    Title on here makes it look like its referring to some proper noun "Less Than Nothing". Rather than the op which also capitalizes "With".

    Was absolutely shocked for second seeing Žižek on the hn frontpage.

  • by xplt on 10/24/25, 9:14 PM

    > You open your laptop and begin.

    FizzBuzz in an on-site interview on your personal laptop?

  • by tdeck on 10/24/25, 6:03 AM

    This is a great example of how pure FP folks tend to communicate and why their languages aren't more popular.
  • by 334f905d22bc19 on 10/23/25, 7:43 AM

    nice 9.5/10 (minus half a point because my brain got hurt a little)
  • by jackdoe on 10/23/25, 8:05 AM

    haha I was not expecting this journey :)

    brilliant work!

  • by TZubiri on 10/24/25, 3:16 AM

    By Vidrun! This sounds like a ripoff from (inspired by) "Hexxing the technical interview"!
  • by wrs on 10/23/25, 3:50 PM

    In case you’re trying to follow the code: The letters definition is slightly too late. It needs to be before extractString.
  • by senfiaj on 10/23/25, 9:15 PM

    Unfortunately couldn't run that long code VM90:2 Uncaught RangeError: Maximum call stack size exceeded
  • by dc10tonite on 10/24/25, 2:49 AM

    This is the hardest I've laughed this year. Dear lord i'm dying - I'm Dana.
  • by NoSalt on 10/23/25, 3:02 PM

    Sometimes ... I can overcome impostor syndrome. I feel "good" about being a "successful" coder. Then I [attempt to] read an article like this, and it's right back into the "I'm just a pathetic code monkey." state of mind. I just can't win.

    :-(

  • by xcf_seetan on 10/23/25, 9:53 AM

    I'm on Firefox and all i can see is boxes with code... Don't understand what this site is about.
  • by drob518 on 10/23/25, 5:06 PM

    Well done. Kingsbury’s series was equally fun but didn’t use combinators.
  • by the_af on 10/23/25, 2:23 PM

    I was so sure I was reading a post by Aphyr, I had to do a double take.
  • by davedx on 10/23/25, 12:29 PM

  • by singpolyma3 on 10/23/25, 5:38 PM

    two combinators? So bloated
  • by temperceve on 10/23/25, 12:24 PM

    > It is also extremely difficult to understand.

    haha

  • by 1dolinski on 10/23/25, 4:40 PM

    grok =>

    make fizzbuzz up to 20 only starting with these combinatory functions

    let S = (x) => (y) => (z) => x(z)(y(z)); let K = (x) => (y) => x;

    10 seconds later https://gist.github.com/1dolinski/93c2e193e5bc6fad9acfc33d71...

  • by 01HNNWZ0MV43FF on 10/23/25, 4:46 PM

    Absolute Urbit
  • by alienbaby on 10/23/25, 2:26 PM

    ok, but if you did this in an inteview I was giving I would say "clever, but you didn't get the job". Mainly because you missed the point of the interview.

    We have a guy who joined recently who constantly tries to be as clever as possible when writing his code. He ignores established patterns and conventions because he knows better. He does some cool stuff. Really cool stuff.

    He's so far bounced thorugh 3 different teams, I don't think he'll be with us long.

  • by caporaltito on 10/23/25, 9:42 AM

    Let me put that in my list of articles to make you feel dumb
  • by jaeyson on 10/23/25, 7:49 AM

    sheeeshhh, that's harsh
  • by p0w3n3d on 10/23/25, 2:43 PM

    Congratulations, you've created another Brainfuck
  • by Atomic_Torrfisk on 10/23/25, 1:27 PM

    Im sorry, this is just bad. There are some neat tricks here, don't get me wrong, but the story is irrelevant/distracting and just feels like a "and everyone clapped" type post on Reddit.