[HN Gopher] What's functional programming all about? (2017)
       ___________________________________________________________________
        
       What's functional programming all about? (2017)
        
       Author : Ivoah
       Score  : 87 points
       Date   : 2024-09-04 18:04 UTC (4 hours ago)
        
 (HTM) web link (www.lihaoyi.com)
 (TXT) w3m dump (www.lihaoyi.com)
        
       | mgdev wrote:
       | I'm personally a fan of FP. It offers clear benefits: simplified
       | parallelization, improved testability, and reduced side effects.
       | These advantages often lead to more maintainable and robust code.
       | 
       | However, FP's benefits can be overstated, especially for complex
       | real-world systems. These systems frequently have non-
       | unidirectional dependencies that create challenges in FP. For
       | example, when component A depends on B, but B also depends on a
       | previous state of A, their interrelationship must be hoisted to
       | the edges of the program. This approach reduces races and
       | nondeterministic behavior, but it can make local code harder to
       | understand. As a result, FP's emphasis on granular
       | transformations can increase cognitive load, particularly when
       | dealing with these intricate dependencies.
       | 
       | Effective codebases often blend functional and imperative styles.
       | Imperative code can model circular dependencies and stateful
       | interactions more intuitively. Thus, selectively applying FP
       | techniques within an imperative framework may be more practical
       | than wholesale FP adoption, especially for systems with complex
       | interdependencies.
        
         | eyelidlessness wrote:
         | Also worth mentioning in terms of mixing functional/imperative
         | techniques: it can be very helpful to use languages (and
         | frameworks, libraries, interfaces) which are functional-
         | first/-by default, not necessarily pure but which provide
         | specific affordances for managing side effects. This can be
         | seen in languages like Clojure (with reference types distinct
         | from most of the rest of the language/stdlib). It's also a
         | hallmark of many projects with a reactive paradigm (which in
         | some form or another have dedicated APIs for isolating state
         | and/or effects).
         | 
         | These aren't strictly necessary for effectively using
         | functional techniques in an imperative environment, but they
         | can go a long way toward providing useful guardrails you don't
         | have to figure out yourself.
        
         | VirusNewbie wrote:
         | >d. As a result, FP's emphasis on granular transformations can
         | increase cognitive load, particularly when dealing with these
         | intricate dependencies.
         | 
         | Does it increase cognitive load, or is it just making the
         | cognitive load more _apparent_. Sure it 's easier to write
         | multithreaded code if you _assume_ race conditions can 't
         | happen, but that's not actually accurate to what would happen.
         | 
         | Perhaps FP just makes explicit in the typing/coding portion,
         | what would otherwise be uncovered hours/days/weeks later in a
         | bug?
        
       | lihaoyi wrote:
       | Author here. This blog post is actually kind of funny; I had a
       | flash of clarity one afternoon that wouldn't go away so I spent 8
       | hours in a manic frenzy banging it out in one pass with no
       | editing. Not how most of my writing happens (typically its a
       | tedious slog with multiple passes of editing and refinement)
       | 
       | Anyone who likes this article on my blog should check out this
       | presentation on applying this philosophy to build tools
       | 
       | https://youtu.be/j6uThGxx-18?si=ZF8yOEkd4wxlq84X
       | 
       | While the blog post is very abstract, the video demonstrates how
       | to take the abstract philosophy and use it to help solve a very
       | concrete, very common problem
        
         | carapace wrote:
         | Have you read Backus' original FP paper "Can programming be
         | liberated from the von Neumann style?: a functional style and
         | its algebra of programs"?
         | 
         | https://dl.acm.org/doi/10.1145/359576.359579
        
         | binary132 wrote:
         | Hi Li, appreciate your work. How do you feel the state of Scala
         | is these days? I took the EPFL intro on Coursera years ago, but
         | I was always disappointed by two things: the community feels
         | very fragmented outside of IDEA (RIP ENSIME -- oh, is it back
         | now?), and it seems like Spark completely overwhelms the rest
         | of the Scala ecosystem. I've mostly moved on these days but
         | still think fondly of it from time to time.
        
       | Barrin92 wrote:
       | >The core of Functional Programming is thinking about data-flow
       | rather than control-flow
       | 
       | That's not right. The difference between data and control flow
       | oriented programming is the difference between laziness and
       | eagerness. The difference between imperative and functional
       | programming is largely one of abstraction, not a feature in
       | itself.
       | 
       | The genuine core feature of FP is the _separation of data and
       | methods_ , that stands in contrast not to imperative programming
       | but object oriented programming, whose core feature is _fusion of
       | data and methods_. Functional programming tries to address
       | complexity by separation of concerns, pulling data and methods
       | apart, OO tries to address complexity by encapsulation, pulling
       | data and methods into purely local contexts.
       | 
       | This is also where the association between FP and static typing
       | comes from that the post briefly mentions. Pulling data and
       | functionality aside lends itself to programming in a global, sort
       | of pipe based way where types act like a contract between
       | different interacting parts, whereas the information hiding of OO
       | lends itself to late binding and dynamic programming, taken to
       | its most extreme version in say, Smalltalk.
        
         | keybored wrote:
         | Now you're just defining FP as a contrast to OO. That's wrong
         | and boring.
        
         | breadwinner wrote:
         | > _The genuine core feature of FP is the separation of data and
         | methods_
         | 
         | Couldn't disagree more. Based on this definition C language
         | would be the epitome of functional programming... but it is
         | not.
        
           | giovannibonetti wrote:
           | That's exactly what the parent comment said, since C is an
           | imperative programming language:
           | 
           | > The genuine core feature of FP is the separation of data
           | and methods, that stands in contrast not to imperative
           | programming but object oriented programming, whose core
           | feature is fusion of data and methods
        
         | wk_end wrote:
         | One of the foundational building blocks of FP is the closure,
         | the purpose of which is to couple together data and the
         | function operating on it.
         | 
         | ML, one of the standard-bearing functional programming
         | languages, is at least partially defined by its powerful module
         | system. And an ML module serves a similar sort of encapsulatory
         | purpose as the class does, often binding together a type and
         | functions that operate on it - the internals of the type sealed
         | away such that _only_ those functions can operate on that data.
        
           | Barrin92 wrote:
           | >One of the foundational building blocks of FP is the closure
           | 
           | Yes, because the closure solves a problem in functional
           | programming _by injecting a bit of OO_. Closures are just
           | stateful function objects with a single call method operator
           | where the local context serves the same purpose as private
           | variables in an object.
           | 
           | It's exactly because FP otherwise lacks the coupling of data
           | and methods that closures are so important, and it's why, the
           | other way around, in languages where functions are literally
           | first class objects, you achieve that through closures.
        
             | randomdata wrote:
             | _> by injecting a bit of OO._
             | 
             | OO is defined by message passing. What does a closure have
             | to do with message passing?
        
               | layer8 wrote:
               | In most OO languages, message passing is just a fancy
               | term for function calling. It's really not fundamentally
               | different from functional application on a closure value
               | in FP.
               | 
               | OOP is defined by encapsulation and subtyping
               | (polymorphism and inheritance). In fact, the one thing
               | that doesn't exist in standard FP is inheritance.
               | (Encapsulation sort-of exists with closures, and
               | polymorphism exists with function values.)
        
               | randomdata wrote:
               | _> In most OO languages, message passing is just a fancy
               | term for function calling._
               | 
               | Having objects does not make a language oriented to those
               | objects. By your definition C++ would be considered OO,
               | but we know it is not. Kay quite explicitly stated that
               | C++ is not OO. I expect you're thinking of Object-
               | _based_ programming.
               | 
               | If you look at the actual definition of OO, it basically
               | is just a laundry list of Smalltalk features. It is
               | probably not usable outside of Smalltalk for that reason,
               | despite Joe Armstrong's playful insistence that Erlang is
               | the only OO language in existence.
               | 
               | You might be able to include Ruby and Objective-C (and,
               | iff you're really stretching, Swift with @objc enabled),
               | but that's about the extent of it.
        
               | layer8 wrote:
               | I don't really care much about what Kay says. His view of
               | OO isn't what caught on. C++ as a descendant of Simula,
               | which is considered the first object-oriented programming
               | language, certainly supports OO. (C++ is really a multi-
               | paradigm language.)
               | 
               | Object-based means OO without inheritance or subtyping.
        
               | randomdata wrote:
               | Simula long predates OO. To call it an OO language is
               | laughable.
               | 
               | Simula has objects, but that does not imply that the
               | objects are oriented.
        
               | layer8 wrote:
               | The inventors of Simula disagree: http://kristennygaard.o
               | rg/FORSKNINGSDOK_MAPPE/F_OO_start.htm...
        
               | randomdata wrote:
               | Implying that the inventors of Simula had a time machine?
               | 
               | I am inclined to think the more logical explanation is
               | that they didn't actually take OO back in time and that
               | Simula isn't an OO language at all (perhaps confused by
               | the first OO language being created _with_ Simula?), but
               | admittedly I 'm rooting for the time machine! Look
               | forward to you telling us more about it.
        
             | wk_end wrote:
             | If paradigm A is defined, in part, by a particular concept
             | that was created wholly independently of paradigm B, even
             | if that concept has some analogs in paradigm B, it's a real
             | stretch to say that it's merely "injecting a bit of"
             | paradigm B into paradigm A.
             | 
             | A more parsimonious ontology is that there's an underlying
             | concept _shared_ by both paradigm A and paradigm B, and
             | that you 're making your cut in the wrong place - that this
             | concept is not where the split is.
        
               | Barrin92 wrote:
               | > it's a real stretch to say that it's merely "injecting
               | a bit of" paradigm B into paradigm A.
               | 
               | Not at all. Leibniz and Newton both invented the same
               | thing independently, calculus, even though each probably
               | called it something else. The reason why this whole
               | discussion is so confused is because people tend to do
               | what you proposed, which was argue from the top down by
               | association. We say ML is "functional", ML contains
               | closures, closures encapsulate state and behavior,
               | therefore that's "functional programming". But that's
               | like saying, Python is "object oriented", X is in Python,
               | therefore that's object oriented programming. Which is of
               | course not true, you can write functional code in Python
               | just fine.
               | 
               | If we want a real ontology we've got to look at what
               | structures in a program do, regardless of language.
               | Closures behave like objects, modules that you brought up
               | are basically just static classes. In fact the Ocaml
               | manual gives a good example of how classes, modules and
               | objects can be expressed in interchangeable ways
               | (https://ocaml.org/manual/5.2/advexamples.html). There's
               | a reason that Ocaml is a fairly logical extension of
               | Caml, it didn't suddenly become the ontological opposite
               | because you added the O to it.
               | 
               | Working up from clear conceptual differences I think also
               | leads to a much more sensible conclusion, which is that a
               | lot of languages combine both functional and object
               | oriented features and that labelling an entire language
               | as either one is just wrong.
        
         | oglop wrote:
         | There's always some rambling answer in every FP post about what
         | FP _actually is_ which then devolves into senseless drivel and
         | arguments.
         | 
         | If you wrote this in a book and gave it to me as a way to learn
         | what FP is, I'd be pretty pissed if I paid for that book is all
         | I'm saying.
         | 
         | Also, what in the actual fuck are you on about? FP is many
         | things and some of them are less enforced than others depending
         | on implementation, but I'm pretty sure in each FP book I've
         | looked at has mentioned exactly this idea of data-flow. So
         | either you are an amazing genius who sees what others can't, or
         | you are just generating nonsense. Either way I don't care, HN
         | is kind of a joke to me anymore.
        
       | gr4vityWall wrote:
       | The article itself was well written, although I'd appreciate if
       | the author was more "to the point" with the examples.
       | 
       | FP never resonated with me and never fit the mental model I have
       | for programming. I tried learning Prolog and Haskell a few times,
       | and I never felt like I could reason about the code. This line
       | from the article:
       | 
       | "[..] With functional programming, whether in a typed language or
       | not, it tends to be much more clear when you've made a trivial,
       | dumb error [..]"
       | 
       | wasn't my experience at all. In my experience, what made it clear
       | when I made a trivial/dumb error was either having good typing
       | present, or clear error messages.
       | 
       | I do always try to use the aspects from it that I find useful and
       | apply them when writing code. Using immutable data and avoiding
       | side effects when possible being the big ones.
       | 
       | I'm glad FP works for the blog author - I've met a few people
       | that say how FP makes it easier for them to reason about code,
       | which is great.
        
         | codr7 wrote:
         | FP doesn't have to be a religion; in Common Lisp it's just an
         | idea, an ideal to aim for perhaps.
        
           | iLemming wrote:
           | Same can be said about Clojure. Although Clojure usually
           | described as an FP-language, it's not "purely FP". In
           | general, I find that Lispers typically don't concern
           | themselves with the popularity of certain tools or
           | techniques; They don't care for things like MSFT marketing
           | shoveled in your mouth. Die-hard pragmatists, they'd use an
           | instrument if it makes sense. They don't give two shits about
           | OOP propaganda or extreme functional purity, or the notion
           | such as "not using static types is immoral" and shit like
           | that, they'd use object-orientation where it makes sense;
           | metaprogramming, where desired; constrains, if applicable;
           | concurrency when required, etc. All that without any zealotry
           | - just pragmatic use of the right tools for the job,
           | embracing Clojure's flexibility and power while staying true
           | to the only core "philosophy" - "simple made easy".
        
             | gleenn wrote:
             | As a dyed-in-the-wool Clojurist, I appreciate leveraging
             | the functional aspects as much as immutable by default data
             | structures. It takes a while to stand back and realize, but
             | once you have a large program that is mostly all passing
             | immutable hashmaps all around to static functions, testing
             | becomes soo much easier and reasoning about how all the
             | code is glued together becomes far easier. You know with
             | certainty that code doesn't have all the ordering problems
             | that come with Objects, whether you called some random
             | function on some object at aome time that cached some
             | instance variable that should have been shared with some
             | other object. Reasoning about that is nuts, and the status
             | quo for most OO code I've seen. If you know that most code
             | only is implemented with pure functions with immutable
             | data, then the ordering questions are nearly completely
             | gone. You can now refactor so much easier as well without
             | risk of subtle ordering related breakage. And then Clojure
             | has atoms and channels which are very nice, thread-safe
             | constructs that also are far easier to know your code won't
             | have memory safety issues. I have dabbled at learning Rust
             | or Haskell or Swift but Clojure gets so much so right.
        
               | Dansvidania wrote:
               | I have the same experience. I learned Haskell because of
               | the aura of prestige around it, and must admit I still
               | love the syntax, but moved over to Clojure as my daily-
               | driver as the practicality of "just use maps" is
               | incredible.
               | 
               | IMO the article misses the point there. Immutability is
               | the "thing" in FP.
        
               | iLemming wrote:
               | Yes! I spent months (maybe even years) trying to
               | understand Haskell, and for the love of god, I just
               | couldn't wrap my head around so many things. I just
               | wanted to build stuff with it, but it felt like becoming
               | a doctor - getting a bachelor's, passing MCAT, then four
               | years in medical school, then residency program, then
               | getting a license and a board certification. All that for
               | the sake of knowing how to properly apply a band-aid (or
               | something). Except, with Haskell, there's no rigid
               | structure, there's no single, proven, 100% guaranteed
               | path to glory - it's all blur. There's no "10 steps to
               | build a website with Haskell". And then I picked up
               | Clojure, and OMG! everything suddenly started making
               | so... much... sense... All the things in Haskell, for
               | which I had either no clue or had some shallow
               | understanding - that all became so much more intuitive.
        
               | Dansvidania wrote:
               | It definitely took me at least 3 years of reading and
               | practicing to be able to get to -at least think-
               | understand it, and I still think I would be considered
               | pretty incompetent by people that do Haskell daily.
               | 
               | There is no doubt in my mind that the Purity concept in
               | Haskell is taken to an impractical degree. I happen to
               | like it, but it does not make developing software with it
               | any less of a troubled experience. The defining concept
               | in comparing Haskell and Clojure for me is the pareto
               | principle :D Clojure gets 80+% of the way there, without
               | 80% of the self flagellation.
               | 
               | There is also a consideration of static typing vs dynamic
               | but that's a whole other can of worms.
               | 
               | Simply, in Haskell all mutability is delegated to the
               | runtime while in clojure one has the freedom and
               | responsibility to manage it directly, and boy does it
               | gain in simplicity for it!
               | 
               | I know I am preaching to the choir but it's pleasant to
               | find a similar experience and share thoughts!
        
               | iLemming wrote:
               | I know, right? It may feel weird at first - with
               | immutability, parentheses and prefix notation, but once
               | you grok REPL-driven interactivity and structural editing
               | - so much about programming becomes intuitively simpler.
        
         | marcosdumay wrote:
         | It's possible that what makes functional languages easier to
         | reason about and debug is the fact that they allow the types to
         | capture a lot more information than the imperative languages.
         | 
         | What would also explain why Prolog has none of those benefits.
         | If you don't use the extra information, it can't do any good.
         | 
         | But if it is really just that, it can be replicated over
         | imperative languages. Anyway, Rust is evidence that there is
         | something to that idea.
        
         | vundercind wrote:
         | I've wondered for some time how this breaks down along
         | preferences for math vs (human) language, or for proofs/formula
         | thinking vs algorithmic.
         | 
         | I find FP concepts easy enough to grasp (provided they're not
         | demonstrated in e.g. Haskell) and even adjacent stuff like
         | typeclasses or monads or what have you aren't a stumbling
         | block, and I'm plenty comfortable with stuff like recursion.
         | 
         | ... but I'm firmly on the language-is-more-natural-for-me side
         | of things, and find non-algorithmically-oriented math writing
         | incredibly difficult to follow--I have to translate it to
         | something more algorithmic and step-oriented to make any
         | headway, in fact. I find languages like Haskell nearly
         | illegible, and tend to hate reading code written by dedicated
         | FP fans in just about any language.
        
           | Dansvidania wrote:
           | the "FP is for math people" meme IMO is incorrect and comes
           | from FP mainly being used to refer to Pure FP, aka Haskell.
           | 
           | While Monads and co. are interesting constructs, I think the
           | main thing with FP (pure or not) is immutability by default.
           | 
           | That alone makes code so much easier to think about, in my
           | experience.
           | 
           | One can do FP in languages not commonly associated with FP by
           | just not (re)assigning variables. FP languages just make it
           | increasingly hard to do so.
        
           | agumonkey wrote:
           | To it wasn't really math, even though there was some of that,
           | it was about the amount of concepts and devices that have to
           | work together.
           | 
           | Ability to reason about expressions means everything can be
           | run and have a result without side effects (unless you allow,
           | say, lisp effectful builtins). The fact that everything is
           | built around function means you can always unplug or compose
           | them. All this with a very reduced set of ideas and syntax
           | (at least for the core)
           | 
           | On the other hand most imperative languages required you to
           | pay attention to state, which is rapidly a mental dead end,
           | with a lot more ceremony and syntax. At least before the
           | 2010s .. nowadays everybody has expression oriented traits
           | and lambdas.
           | 
           | After learning ml/haskell and doing interesting things with a
           | kind of clarity.. I tried going back to c/python and suddenly
           | a lot of errors, issues and roadblocks started to appear.
           | 
           | Then you have the parallel case.
        
         | kelnos wrote:
         | I think I find a lot of FP code harder to both write and read
         | (even code I've written myself), but when the FP code is
         | written and compiles, it is much more likely to be correct.
         | 
         | It's an annoying trade off, to be sure. With a language that
         | makes writing FP code ergonomic and idiomatic, though, I'm
         | usually going to choose to write that way.
         | 
         | But even in languages where writing in an FP style is a chore
         | (if it's even possible at all), you can take some lessons. The
         | C I write today is more "functional" than 25 years ago, when
         | I'd never even heard of functional programming. I try to avoid
         | global state and mutation and side effects, and write more pure
         | functions when I can. I think about my programs as
         | transformations of data, not as a series of instructions, when
         | I can. My C code today is not very FP at all when you'd compare
         | it to something written in Haskell or Scala or whatever, but
         | it's, IMO, "better" code than what I used to write.
         | 
         | In 2022 I went back to a C-based open source project that I
         | used to work on heavily in the mid-'00s. Reading a lot of that
         | old code makes me cringe sometimes, because the details at the
         | micro level make it really hard to reason about behavior at the
         | macro level. As I've been working on it and fixing things and
         | adding features again, I'm slowly morphing it into something
         | easier to reason about, and it turns out that using functional
         | concepts and idioms -- where possible in C without having to go
         | into macro hell -- is essentially what I'm doing.
        
       | leoff wrote:
       | Interesting how the post doesn't mention the word "side effect"
       | once.
       | 
       | To me, all of this could be summarized by "no side effect".
        
         | tromp wrote:
         | He didn't need to mention it because it's implicit in data
         | flow. Instead of the side effecting / state changing
         | whip(cream)
         | 
         | that needs to be under flow control, you have
         | whipped_cream = whip(cream)
         | 
         | describing the flow of data. Data flow describes the
         | relationship between non-changing entities and thus there are
         | no side effects.
         | 
         | While they could have mentioned it, it wouldn't really change
         | the message.
        
         | Feathercrown wrote:
         | I think the point of the article is to illustrate _why_ "no
         | side effect" is important.
        
           | magicalhippo wrote:
           | While I agree, you can get all of that FP example from the
           | article in say C++ by liberal _const_ -usage. So is "const-y"
           | C++ functional programming?
        
         | BoingBoomTschak wrote:
         | Similar reaction, I searched for "lambda calculus" first thing
         | first and was pretty weirded by the "0 results".
         | 
         | To me (someone who really doesn't like FP as a religion), FP is
         | about function application: everything should just be "pure"
         | function calls taking values (not references) and returning
         | values; immutability is indeed a corollary of that, static
         | typing really isn't (isn't Church's original LC untyped, btw?).
        
         | blueberry87 wrote:
         | this is wrong! ocaml is a functional programming language with
         | side effects.
        
       | dang wrote:
       | Related:
       | 
       |  _What 's Functional Programming All About?_ -
       | https://news.ycombinator.com/item?id=15138429 - Aug 2017 (139
       | comments)
       | 
       |  _What 's Functional Programming All About?_ -
       | https://news.ycombinator.com/item?id=13487366 - Jan 2017 (2
       | comments)
        
       | giovannibonetti wrote:
       | I wonder if there is a Python PEP for adding a pipe operator |>
       | to the language. This could be pretty handy, as described in the
       | article.
        
       | mbivert wrote:
       | I believe that a good definition of the "core" functional
       | programming is: it's a practical way of using the l-calculus.
       | Similarly, imperative programming is a practical way of using
       | Turing machines.
       | 
       | It's always somewhat possible to express what are typically
       | considered imperative features in a functional fashion (e.g.
       | monad), or bend imperative languages to behave somewhat like
       | functional ones: I think the differences become clearer once we
       | reach out for the underlying theoretical models.
        
       | yodsanklai wrote:
       | As a programmer, I don't know if it's still relevant to make a
       | strict separation between programming paradigms. You can use
       | immutable types, pure functions, closures and so on in most
       | languages. Conversely, you can define mutable types and
       | imperative code in most functional programming languages.
       | 
       | I'm always surprised reading comments on these topics, people
       | saying they don't grasp FP. But don't we use higher-order
       | functions, closures, combinators all the time in most mainstream
       | languages? How hard can it be to learn OCaml or Clojure for
       | someone who use closures all over the place in JS?
       | 
       | Monads have a steeper learning curve, but besides Haskell, they
       | aren't that pervasive. And there are constructs with similar
       | flavor in mainstream languages too (result types in Rust...)
        
         | acchow wrote:
         | Monads are pervasive: async-await.
         | 
         | We just don't call them monads.
        
           | bazoom42 wrote:
           | How is async-await monads? Isn't it just syntax sugar over
           | callbacks?
        
             | ryandv wrote:
             | Consider async-await a syntactic sugar over Promises (from
             | JavaScript). Then, Promises constitute an instance of the
             | Monad typeclass where monadic `bind` or (>>=) is
             | `Promise.then()`, and `return` is `Promise::resolve()`.
             | 
             | Here is a translation of a modification of the example
             | given in [1]:                   const promise1 =
             | Promise.resolve(123);              promise1.then(v => v *
             | 2).then((value) => {           console.log(value);
             | // Expected output: 246         });
             | 
             | into Haskell:                   ghci> let promise1 :: IO
             | Int = return 123         ghci> promise1 >>= (return . (*
             | 2)) >>= print         246
             | 
             | One key discrepancy worth pointing out is that in the
             | `Promise.then()` API of JavaScript, the function provided
             | to `then` (e.g. `v => v * 2` above) is implicitly composed
             | with a call to `::resolve` in order to turn that function's
             | pure return value, the `Number` 246, into a Promise
             | resolving into the `Number` 246; in Haskell, this operation
             | must be made explicit (hence the composed function `return
             | . (* 2)` passed to the first application of (>>=) or
             | `bind`).
             | 
             | You could say that the instance method `Promise.then()`
             | expects an argument of type (a -> b), with the return value
             | of type `b` being implicitly and automatically wrapped into
             | a Monad `m b`, whereas Haskell's bind expects an argument
             | of type (a -> m b) on that operator's right-hand side, with
             | the return value explicitly wrapped into a Monad `m b` by
             | the provided function argument itself.
             | 
             | [0] https://wiki.haskell.org/Monad
             | 
             | [1] https://developer.mozilla.org/en-
             | US/docs/Web/JavaScript/Refe...
        
         | fire_lake wrote:
         | Mainstream languages are not expression orientated like true FP
         | languages are. Most people working in mainstream languages
         | aren't aware of the significance of this and wonder why FP
         | seems awkward in their language, despite it having closures,
         | some immutable types, etc.
        
         | SatvikBeri wrote:
         | I wouldn't think of paradigms as strictly separate, but there
         | are definitely clusters of related techniques that go well
         | together. Higher order functions work best with immutability
         | and pure functions, and struggle without them. Currying is
         | somewhat useful by itself, but much more valuable with function
         | composition or HOFs.
         | 
         | It's also important to teach the techniques together because
         | functional programming tools are typically (deliberately) less
         | powerful, which makes them easier to analyze, but means you
         | need more tools.
        
         | smrtinsert wrote:
         | Most js code bases I see (frontends typically in react) have
         | only a basic sense of functional programming/closures. It is a
         | massive paradigm shift to move to clojure from where modern js
         | is today. It was probably less so in the jquery days funny
         | enough
        
         | kelnos wrote:
         | I don't think we should think of things as having a strict
         | separation, but certainly some languages push you harder than
         | others toward certain programming paradigms, and some make
         | other paradigms difficult or awkward to use.
         | 
         | For example, while I can do FP in Rust, I would not really call
         | Rust a FP language. It does have some features that make doing
         | FP possible, but a lot of the code I see (and write) is a mix
         | of imperative and FP.
         | 
         | But if I'm writing Scala, I'm going to be mostly writing FP
         | code, and the language helps me there. Recent versions of Java
         | make it easier to write FP code if you so choose, though you'll
         | still see a lot of people write more imperative code in Java.
         | 
         | (I think if Rust had Scala's for-comprehensions, I'd write more
         | FP-ish Rust. I know there are crates that emulate them, but
         | they're not the most ergonomic, and I don't want to write what
         | ends up being unidiomatic Rust code.)
        
       | ryandv wrote:
       | The classic dichotomy drawn between functional programming (FP)
       | and object-oriented programming is the "Expression Problem" [0];
       | in the former approach you optimize for extensibility of the
       | number of _operations_ in your model, at the cost of reduced
       | developer ergonomics when attempting to extend the number of
       | _types_ you can operate upon. In the latter, object-oriented
       | approach, you have the inverse tradeoff.
       | 
       | In FP the fundamental unit of composition is the function; your
       | solution is expressed as a composition of functions, which are
       | treated as first-class objects (first-class meaning, functions
       | can be manipulated via higher-order functions, or "functions of
       | functions"), a feature not always seen in object-oriented or
       | multi-paradigm languages. Polymorphism is most frequently
       | achieved through parametric polymorphism, or "generics," and ad-
       | hoc polymorphism, or "trait bounds"/"typeclass constraints".
       | 
       | In OOP the fundamental unit of composition is the object; your
       | solution is expressed as a composition of objects, whether
       | through actual composition/aggregation (objects containing and
       | possibly delegating to other objects [1]), or subtype
       | polymorphism, also known as "inheritance." Parametric and ad-hoc
       | polymorphism can often feature in OOP languages as well, but
       | subtype polymorphism is a distinguishing characteristic of OOP.
       | 
       | Functions, particularly pure functions without side effects in
       | the "real world" such as I/O or hardware access, are akin to
       | equations in which an expression can be replaced by its value -
       | the "left-hand side" equals the "right-hand side." Mutable state
       | often does not enter into the picture, especially when
       | programming in this "pure" (side-effect free) style. This makes
       | functional programs easier to reason about equationally, as one
       | can determine the value of an expression simply by inspection of
       | whatever variables are in the function's scope, without having to
       | keep track of the state of the entire program.
       | 
       | Objects are distinguished by their often stateful nature as they
       | bundle together data/internal state, and operations over that
       | internal state. Often such internal state is hidden or
       | "encapsulated" from the client, and the internal state is only
       | modifiable (if at all) via the object's class' set of public
       | methods/operations. Objects with immutable internal state are
       | more akin to closures from functional programming - that is,
       | functions with access to a "parent lexical scope" or
       | "environment."
       | 
       | Between the two extremes exists an entire spectrum of mixed-
       | paradigm languages that incorporate features of both approaches
       | to structuring and modelling a software solution.
       | 
       | [0] https://wiki.c2.com/?ExpressionProblem
       | 
       | [1] https://en.wikipedia.org/wiki/Composition_over_inheritance
        
       | yCombLinks wrote:
       | My problem usually appears when rather than clearly laying out
       | the types of each returned value like in the article, all the FP
       | guys I've worked with want to build giant chains that look like :
       | return beat(whip(mix....(eggs)))
        
         | myth2018 wrote:
         | Mine too. I've worked for a shop adopting this practice some
         | time ago. A very common pattern was to declare a relatively
         | large Python dictionary with function calls and list
         | comprehensions nested deep in the dict [sub]properties. Nice to
         | glance, terrible to debug and reason about.
        
       | ninetyninenine wrote:
       | It's hard to characterize what fp is. A lot of people think fp is
       | a bunch of techniques like map, reduce, immutability or high
       | level functions or monads.
       | 
       | None of those things are exclusive to fp. They are just tricks
       | developed by fp.
       | 
       | Here's how to get a high level characterization of what fp
       | actually is:
       | 
       | You know how in math and physics they have formulas? Formulas for
       | motion, for area, etc.
       | 
       | Functional programming is about finding the formula for a
       | particular program.
       | 
       | Typically when you code you write an algorithm for your program.
       | In functional programming you are writing a formula. Think about
       | what that means.
       | 
       | The formula has side effect benefits that make it easier to
       | manage complexity and correctness. That's why people like it.
       | These side effects (pun) are not evident until you programmed
       | with fp a certain amount.
       | 
       | Obviously though people naturally reason about things in the form
       | of procedures rather then formulas so fp tends to be harder then
       | regular programming.
        
       | kh9sd wrote:
       | Very nice article, I liked it a lot! It personally resonated with
       | me and my own conclusion that the core "benefit" of FP is (for
       | lack of a better work, stupid Bitcoin) "proof of work".
       | 
       | Writing functions FP is essentially all about returning results
       | from a function, which is proof that that a computation has
       | occurred. If you don't have that return value, that proof, then
       | obviously the rest of your code can't and shouldn't continue, and
       | FP makes it obvious compared to more traditional imperative
       | approaches.
       | 
       | And this idea extends into so many other things that people
       | consider core, or at least originating from FP. Result/Option
       | types come to mind, making the possible failure of "proof of
       | work" explicit in the type signature, so people are forced to
       | consciously handle it. It also leads into the whole idea of type
       | driven design, one of my favorite articles, "Parse, don't
       | validate"[1], describes this as well, making types that clearly
       | restrict and set the expectations needed for the "proof of work"
       | to always be returned from a function.
       | 
       | [1] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-
       | va...
        
         | ryandv wrote:
         | This is borderline nonsense.
        
       | Terr_ wrote:
       | > Anything vertically separated can be done in parallel.
       | 
       | This assumes no contention on a limited number of bowls or having
       | only one kitchen tool for beating or whisking etc. :P
       | 
       | I point that out not to demand that the metaphor be bulletproof,
       | but because I think it may help explore something about state-
       | handling and the FP/imperative split.
       | 
       | How might this tiramisu-tree model change if we were limited to N
       | bowls and 1 beater and 1 whisk, unable to treat them as
       | statelessly-shareable or endlessly-duplicable?
        
       | deepsun wrote:
       | The article is not about FP.
       | 
       | > Languages like Java encourage patterns where you instantiate a
       | half-baked object and then set the fields later.
       | 
       | Maybe it did before 2010. For many years everyone prefers
       | immutable objects (so that object and its builder have different
       | types, or in simpler case -- no setters, only constructor
       | initialization). You can see it in pretty much all frameworks.
       | 
       | I'm ok with both functional and procedural languages, I just
       | think this article is not about functionality. Come on, FP is all
       | about monads!
       | 
       | Moreover, the "imperative" code example would be impossible
       | anyway with immutable objects without side effects. So what I
       | think the article is about is immutable data types. Everyone
       | agrees that immutable are better, we have to do mutables only
       | when we're optimizing for CPU/RAM.
       | 
       | And BTW concurrency is typically easily achieved if variables
       | were not plain objects, but Future/Observable/Flow/Stream -- pick
       | your poison. They all have passed the hype hill already, and
       | became "just a tool" I think.
        
         | dianeb wrote:
         | Define what you mean by "everyone" -- there are times where the
         | cost of immutability can be overwhelming, such as in high
         | traffic systems with overly complex data structures which you
         | are required to use because someone who should have known
         | better insisted upon writing.
         | 
         | (sorry, bitter personal experience) And yes, that is explicitly
         | "modern" Java code written by a lead engineer and "java
         | champion" in 2023.
        
       ___________________________________________________________________
       (page generated 2024-09-04 23:01 UTC)