577 karma · joined May 5, 2021
"Union" does not imply disjointness (which is the source of many problems), whereas all those other terms do.
Jesus Christ, I am so sick of you and all the people like you who repeat this lie. I've invested a lot of time learning about category theory, domain theory, type theory, etc. so I could become the best version of myself as a programmer, and I have seen very real benefit from this investment. Only to have you and these other people in HN tell me I'm just trying to "feel smart". Your arrogance is so off-putting to me.
When did HN become so anti-intellectual? If you don't understand something, then either learn it or don't—but you don't need to bash other people for their own efforts.
No they don't. Every Haskeller I know (myself included) acknowledges that sometimes point free style is better, and sometimes it isn't. None of them would unilaterally declare it better, and most generally avoid it except in very simple situations.
> The Haskell community is full of people who are here to geek out rather than produce software.
The Haskell community is full of people who are here to geek out about the best ways to produce software. This means they embrace tools like mathematics, and are generally extra thoughtful about structuring programs in principled ways. It's a wonderful community of people who care about the details of their craft and treat it like a skill to be honed over time. I have a lot of respect for that.
If I could rant for a moment: I'm really sick of spending many years of my life investing in myself and my ability to produce software with the best tools humanity has to offer, only to have these people in Hacker News tell me I'm just doing mental masturbation when it seems like they don't even understand what they're criticizing.
Why are you so fixated on algorithms research in your attempts to dismiss the usefulness of functors and monads?
You don't know what you're talking about. In the context of programming language theory, monads were first used by Eugenio Moggi precisely for the purpose of specifying the semantics of imperative programming languages. Only later did Wadler realize that they could be useful as user-defined constructs within a programming language.
The "very specific properties" are actually just the identity and associativity laws of a certain monoid. These are trivially satisfied by imperative-style code including the "algorithms research and papers are expressed in imperative pseudo-code" you mentioned: you can write an imperative program that does nothing when sequenced with other programs (identity), and A; B; C does not require explicit grouping (associativity).
> It's only Haskell's laziness that makes it require monads for i/o, by the way. Other pure functional languages don't use them. For example, in Idris, IO and state are not monads, they are effects - tracked at the type system level, but without all of the commodity of monads. For example, you can have a single do-block in Idris that does IO and state operations without needing any monad transformers or lifting.
This is an incoherent train of thought. Haskell's laziness does not require the explicit use of monads, nor are monads only useful in the context of laziness. Haskell could have used Idris's IO system instead—algebraic effects work just fine in lazy languages. But also it is not true that Idris programs do not use monads just because you don't see the word "monad" in Idris programs. Effects give rise to a monad, mathematically; namely, the free monad for the algebraic theory. Read the seminal work by Plotkin and Pretnar, for example.
We're not talking about any complex category theoretical concepts here. Functors, for example, are one of the first ideas one would learn in a category theory course. Likely even in the first lecture.
Even the most advanced functional programs use only the most basic categorical constructs.
And when you say "imperative pseudo-code", you've already conjured an implicit monad involving state, I/O, etc. Functional programming just teaches us that it's not the only one—and there are simpler ones that may be more appropriate for the given situation.
First of all, I completely understand where you're coming from.
But the fact that Haskell embraces computer science, unlike most other programming language communities, is what attracts me to it the most. I can geek out about the mathematics that leads to simpler, safer software with like-minded people. I am always learning from Haskellers more than from any other programmers.
But it does take a lot of learning and unlearning to become a fluent functional programmer. This isn't because functional programming is more complicated than other paradigms (au contraire), it's because schools and universities have been mostly teaching OOP for the past 2-3 decades—unfortunately. As a result, most programmers have such a big gap in their knowledge that it can feel too daunting to dive in.
We could definitely do a better job teaching the theory and explaining how it's useful. That would be better for everyone: better for curious minds who want to expand their programming skills, and better for me because I'd have more company to discuss it with.
Divergence and partiality can easily be modeled by the category of complete partial orders and Scott-continuous maps. This is what you learn in, e.g., a course on domain theory and/or denotational semantics.
Andrej Bauer is mainly arguing that Hask isn't a category because no one has formally specified it. For example, we would need to come up with a policy on when two arrows are considered equal, and it's not clear what that notion of equality should be.
When using static types, it is best to make as few nonsensical/illegal/invalid states representable as possible, so that you can rely on the compiler enforcing that they won't happen. Functional programmers have been saying this for decades, and these days Rust programmers are generally onboard with this too.
Sum types are the categorical dual of product types, so it always seems unnatural to me when a language only has the latter and not the former. I think part of the problem is we don't teach category theory to programmers, so they never stop to consider the duals of things.
What makes you think Swift only supports functional programming?
> I would have used Haskell doc reference
And if you would have, you would have disproved your own point. In Haskell, closures cannot mutate state, because mutable state is not part of the language semantics. It's modeled explicitly via monads.
> There is a well-established theory of functions, the λ-calculus, ... However, our theory of objects is self-contained; it is the first that does not require explicit reference to functions or procedures.
So that answers your question in the negative and proves my point: no, not every language is based on lambda calculus.
Not really. FP has an obvious connection to lambda calculus. Many OOP languages don't even have a straightforward notion of "function", which is what lambda calculus is all about.
It's a programming paradigm based on simple, well-understood mathematical framework. It's also the starting point for most research in programming language theory.
> In your opinion.
Correct. But my opinion is informed by years of experience with functional programming, procedural programming, and OOP (each).
> Please provide any evidence that this is true, especially the ‘safe’ part. Any study I’ve read says there’s almost no effect on language choice at all on bug rate.
Sure, here is a paper which provides the evidence you're looking for: "A Large Scale Study of Programming Languages and Code Quality in Github" (https://dl.acm.org/doi/10.1145/2635868.2635922). That paper concludes:
> The data indicates functional languages are better than procedural languages; it suggests that strong typing is better than weak typing; that static typing is better than dynamic; and that managed memory usage is better than unmanaged.
I personally don't trust academic studies on programming language effectiveness, since they often contradict each other (e.g., https://arxiv.org/abs/1901.10220 contradicts the paper I cited above). But if you're looking for a peer-reviewed paper, there you go.
Choosing the right paradigm absolutely has an effect on the bug rate. If you have to write more code, repeat code, or deal with low-level details irrelevant to the problem at hand, you are going to make more mistakes. On top of that, functional languages often have better type systems than procedural/OOP ones, which helps with bug catching that much more. Taking this to extreme, with dependent type systems you can actually verify arbitrary mathematical properties of your programs.
How much experience do you have with functional programming? I have professional and academic experience with both functional programming and OOP, and everyone I've met with that experience agrees about the trade-offs I've been discussing in my comments.
But I would argue that the majority of Go programs should not require the programmer to follow the intimate details of a program's execution when trying to understand the business logic. The fact that the language has a garbage collector would suggest that it was designed for programs where some details can be hidden away.
So I agree with your general point, but I am somewhat skeptical about its application to Go.
1. Both examples contain a bunch of extra logic that has nothing to do with the difference between the two styles. For example, the code that converts users to that `UserLevelPoints` struct takes up a lot of space in both examples and is essentially the same in both. I think it would have been better (for pedagogical purposes) to just return the users (or perhaps a simpler struct containing a level and a user).
2. Both examples still have all the verbosity that is typical of imperative code, since it's still Go after all.
If one were to write the same function in a syntax which was designed for functional programming, the result might look something like this:
topUser = head . sortBy points
topUserPerLevel = topUser . values . groupBy level
I find this vastly quicker to read and understand than either of the examples in the article. But that relies on me knowing the functional-optimized syntax (e.g., that the `.` operator is just function composition), and for most imperative programmers that is the main hurdle. Since I am familiar with both styles, I can tell you that this would take me about 6 seconds to understand, whereas understanding each of the examples in the article took me at least a minute to even read the code, let alone understand it—and if there were bugs I wouldn't have noticed.In contrast, with the simple functional version I provided, I can read it in seconds and be reasonably confident that it is correct (assuming all the pieces fit together in a valid way, which a type checker can check for me).
If the unfamiliar syntax prevents you from grokking the example I provided, it's reasonable to assume it's just some clever code golf that should be discouraged in production code. But please resist that temptation; this is actually what good code looks like. It's simple, not repetitive, and there aren't many places for bugs to hide.
Algebraic data types are table stakes for any programming language in my book. I don't know why it's taken OOP programmers so long to come around to them (and most still haven't). It bothers me that Rust calls them "enums", even though "enum" already means something different to most programmers and we already have several terms that describe the feature that Rust has (coproducts, variants, sum types, discriminated unions, tagged unions, and, more generally, algebraic data types). This makes talking about programming languages more difficult than it needs to be, since when someone says "enums" I now have to ask what programming language they are referring to, since "enum" now means different things in different languages.