[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)