I know very little about functional programming (so I should probably just stop and wait for a more knowledgable commenter, but here I go...) but even I know that functional programming doesn't mean "programming that involves functions".
It's a totally different programming paradigm that involves specifying a collection of facts relating function inputs to their outputs and letting the compiler/interpreter figure out how to turn that into a program. There are lots of ways that these sorts of program are different from the type of programming you're used to, including pattern matching, lazy evaluation, and, yes, passing around functions as values. But a particularly surprising one is that often the order of statements don't make any difference because the program is not just executed sequentially starting at the top and working its way down. OOP is subtype of imperative programming, which is the usual programming you're used to where you just write a series of instructions that are executed top-to-bottom (except for function calls and flow control like "if" and "for" but even there you're explicitly specifying what should be executed next).
Apologies if you knew all of that, but it seemed you like didn't because surely no one that really understands functional programming would confuse it with imperative programming involving some callbacks.
---
I think the parent commenter called you out on this even though it's not the main thrust of your argument because it's a really significant misuse of terminology and shows a lack of understanding of fundamental programming concepts. Pedantically it's a bit of an ad hominem attack, but you seem to be coming down hard on Cap'n Proto especially because of its misuse of terminology and it's ironic that you're the one misusing it.
(Another example of this is where you object to "streaming", which means what the document say it does, which you call "polling" but that really means having to proactively check every so often whether something is done rather than getting a callback. [Edit: these are actually orthogonal concepts because even with a non-streaming request you could either be notified or have to poll for the single response. I think you have just totally missed what "streaming" means here.])
I am having to fight really hard the urge to dig into more detail of your comment. But I will leave it at one more thing that you seem to have missed: You seem to be talking as if this page is a first introduction to Cap'n Proto, when it's not, it's just a changelog entry intended for people that already know what the library is. Of course features are mentioned without a proper introduction, changelogs are typically of the form "add cancellation parameter to the floog() function" without explaining what "floog()" is. Adding all that detail would actually make them less useful, because it's just noise to the target audience that buries the real content, which is what's actually changed.
I do really appreciate that you try to explain this from first principles. First, to explain myself, I'm primarily programming in F#, which is a hybrid functional AND object oritented lang (also use: TS, Elm, Haskell, C#, Python, Ruby etc). I prefer to write the software in a functional style though, so I rather use closures and self-recusive functions acting as message loops over the dynamic dispatch of "telling" objects to do things.
> and, yes, passing around functions as values
If we go back to the foundations of what I would call functional programming, Lambda Calculus as defined by Church, I like to give the evaluation of booleans as a prime example of how functional programming computes by passing callbacks around: https://en.wikipedia.org/wiki/Lambda_calculus#Logic_and_pred...
Since this was arguably the very first functional programming "language" that existed in the world (even: one of the very ways to do structured computation!), and is almost exclusively uses callbacks, I'm very confident in saying that callbacks is more functional programming than anything else.
Functional programming still computes heavily via function composition; the principle of passing callbacks around is at the very center of functional programming. To name a few instances of callbacks being passed around; via currying "context" values, by passing a function to "act in a context" (hole-in-the-middle/delegator) or closing (closures) over mutable state to enable runtime re-configuration.
What most people don't realise about functional programming (until they've done it a long time) is that it's not in the syntactic features, nor in the type system, that functional programming really shines, it's in the compositionality of it.
> But a particularly surprising one is that often the order of statements don't make any difference
This is generally a false statement. You can mean many things here: dataflow programming (https://en.wikipedia.org/wiki/Dataflow_programming) or the ability of monadic computation expression to choose when in time it executes its built-up construction, or how lazy evaluation can share chunks (bits of compute) that are beta-(normalised)-normal to each other.
What is true, however, is that callbacks is not ONLY a core concept in functional programming (even if it's arguably MORE core in FP); even PL/1 and Cobol have address pointers that gets passed as callbacks, but there it's more used for cross-system interop than for composition. Assembly and even C also heavily use callbacks, but those are not OOP. The dynamic dispatch you have in object hierarchies in OOP, is not even such a close concept to callbacks (https://en.wikipedia.org/wiki/Dynamic_dispatch).
> write a series of instructions that are executed top-to-bottom
> I think the parent commenter called you out on this even though it's not the main thrust of your argument because it's a really significant misuse of terminology and shows a lack of understanding of fundamental programming concepts. Pedantically it's a bit of an ad hominem attack, but you seem to be coming down hard on Cap'n Proto especially because of its misuse of terminology and it's ironic that you're the one misusing it.
Obviously I disagree with this (see above, with references!). The way he answered is unconstructive and only leads to hard feelings. I'm coming down hard on the way the Cap'n Proto article is written, and I can back it up and argue my point. I'm in no way attacking the author, or objectively saying that Cap'n Proto is bad — only that I think the way it's making its case is.
> Another example of this is where you object to "streaming"
I'm objecting not to "streaming" but to how the code makes it look. It's really unclear to someone who doesn't know the Cap'n Proto syntax. I think you're missing the point that my review is from someone who really understands these concepts, reading the article and forming an opinion of whether to give Cap'n Proto a chance based on it. Also, the article only says streaming means "returning multiple responses", which is an API-level concern, not a protocol concern (* * not a protocol concern _ necessarily _ * * : the protocol is what should be explained; for example gRPC uses HTTP2 push to do streaming responses)
> You seem to be talking as if this page is a first introduction to Cap'n Proto, when it's not, it's just a changelog entry intended for people that already know what the library is
It is the first introduction I had to Cap'n Proto, and I also after reading the article read the main site's introduction. I'm giving comments on how I perceive Cap'n Proto based on that.
> ... foundations of what I would call functional programming, Lambda Calculus as defined by Church ...
Ah! Now we’re getting to the nub of it. Yes, the lambda calculus, absolutely correct.
> ... almost exclusively uses callbacks ...
Whoops! This is absolutely wrong. The functions in the lambda calculus cannot have side effects (like saving a file or displaying a message to the user) and they can only depend on their inputs (e.g. they cannot return a number entered by a user, data from a socket, the current time, etc.). In the software and computer science world they are called "pure functions". Mathematicians (including Church) would simply call them "functions" because that, by definition, is what a mathematical function is (it is a subset of the Cartesian product of the domain and codomain, very much like a lookup table in computer programming except it can be infinite or even uncountably infinite).
These are very different from the callbacks being discussed in the original post. A callback - as the name suggests - is a function passed from a higher-level abstraction to a lower level abstraction to be notified when something happen. Those exist purely for their side effects - that's the whole point of a callback - and usually have no return value at all.
> Functional programming still computes heavily via function composition; the principle of passing callbacks around ... [e.g.] via currying "context" values, by passing a function to "act in a context" (hole-in-the-middle/delegator)
Sure, except you're passing (pure) functions around, not "callbacks" (at least for it to count as true functional programming).
> closing (closures) over mutable state
As you said, F# is a hybrid language, and mutable state is on the imperative side (admittedly a concession made even by stricter functional languages like LISP).
> This is generally a false statement. ... how lazy evaluation can share chunks (bits of compute)
It would be true in a pure functional language, and is only false to the extent that a real-world "functional" languages actually have some imperative features.
> > write a series of instructions that are executed top-to-bottom
> That's procedural programming, not OOP:
No, it's imperative programming, as I said in my previous comment. Procedural programming and OOP are both subtypes of imperative programming. On the Wikipedia page you linked to, look at the box on the right hand side, and note how imperative says "(contrast declarative)" next to it, and vice-versa. The box shows that declarative includes functional and dataflow programming.
> Assembly and even C also heavily use callbacks
Agreed, and they're a lot more similar to the type of callbacks being described in the original post than pure functions: they're totally imperative and allowed side effects. But they don't normally have included state - you usually get a void* for your extra data but no functions to copy and destroy that, unlike C++ lambdas and std::function (Python uses garbage collection to avoid needing those for state attached to function objects). So using them ends up with a slightly different feeling to the developer than callbacks in OOP languages, which is why the article specified callbacks in OOP languages rather than callbacks in general.
----
There are other details I could pick apart in your original comment or other replies. But there is a broader point. I know this isn't terribly constructive, but I think it's important to address the elephant in the room: frankly, none of your comments make any sense. You misuse terminology all over the place, but then pedantically pick apart individual technical terms in other people’s comments, mostly because they've used the terms with their real meanings rather than your imagined meanings. The commenter _pmf_ chose to highlight your incorrect criticism of "callbacks" and I followed up on it, but almost everything else in your original post deserves the same treatment; that was just one example. I really think people downvoted your original comment rather than replying because they took a look at it and thought "this is a lost cause".
In the comment I'm replying to, you say "I'm very confident in saying that" and complete the sentence with something that is wrong, and several people have already told you so. But this attitude comes across implicitly elsewhere. I implore you, please stop trying to just push your understanding of things, and instead lean back and consider that the comments contradicting you could actually be right.
This especially applies to looking back at Kenton's replies, even if you ignore what I'm saying (fair enough). I realise he gave up replying to you eventually but his replies were very reasonable and clear explanations and you responded with just an explosion of more confusion that would take more and more effort to clear up - I think he has the patience of a saint for addressing as much of your comments as he did.
Bear in mind that no one else in the comments had trouble with either the original article or the introduction to Cap'n Proto in general. No one came along and agreed with anything you said, which does happen when bad articles are posted here. In general (of course there are exceptions), Hacker News commenters are clever and reasonable. If you find yourself surrounded by clever reasonable people and they're all wrong and you alone are right, it is time to consider if it is really the other way round.
I ended up putting the wrong reply here to this frament, sorry about that:
> > This is generally a false statement. ... how lazy evaluation can share chunks (bits of compute)
> It would be true in a pure functional language, and is only false to the extent that a real-world "functional" languages actually have some imperative features.
What I had intended to say was that, yes, this is the sort of feature of functional languages I was talking about. In a true (perhaps theoretical) functional programming language, all functions are pure, so you can arbitrarily reorder function calls (relative to other calls of the same function and to calls to other functions) and this cannot possibly have an effect on the meaning of the program. You can even coalesce multiple calls to the same function with the same parameters.
As I'm sure you realise, this can't work for functions that have side effects e.g. reading bytes from a socket. Again, these are things that can happen for functions, including callbacks, in imperative programming languages (or the imperative bit of mixed or mostly-functional programming langauges).
It's a totally different programming paradigm that involves specifying a collection of facts relating function inputs to their outputs and letting the compiler/interpreter figure out how to turn that into a program. There are lots of ways that these sorts of program are different from the type of programming you're used to, including pattern matching, lazy evaluation, and, yes, passing around functions as values. But a particularly surprising one is that often the order of statements don't make any difference because the program is not just executed sequentially starting at the top and working its way down. OOP is subtype of imperative programming, which is the usual programming you're used to where you just write a series of instructions that are executed top-to-bottom (except for function calls and flow control like "if" and "for" but even there you're explicitly specifying what should be executed next).
Apologies if you knew all of that, but it seemed you like didn't because surely no one that really understands functional programming would confuse it with imperative programming involving some callbacks.
---
I think the parent commenter called you out on this even though it's not the main thrust of your argument because it's a really significant misuse of terminology and shows a lack of understanding of fundamental programming concepts. Pedantically it's a bit of an ad hominem attack, but you seem to be coming down hard on Cap'n Proto especially because of its misuse of terminology and it's ironic that you're the one misusing it.
(Another example of this is where you object to "streaming", which means what the document say it does, which you call "polling" but that really means having to proactively check every so often whether something is done rather than getting a callback. [Edit: these are actually orthogonal concepts because even with a non-streaming request you could either be notified or have to poll for the single response. I think you have just totally missed what "streaming" means here.])
I am having to fight really hard the urge to dig into more detail of your comment. But I will leave it at one more thing that you seem to have missed: You seem to be talking as if this page is a first introduction to Cap'n Proto, when it's not, it's just a changelog entry intended for people that already know what the library is. Of course features are mentioned without a proper introduction, changelogs are typically of the form "add cancellation parameter to the floog() function" without explaining what "floog()" is. Adding all that detail would actually make them less useful, because it's just noise to the target audience that buries the real content, which is what's actually changed.