[HN Gopher] At the boundaries, applications aren't functional
       ___________________________________________________________________
        
       At the boundaries, applications aren't functional
        
       Author : s3cur3
       Score  : 25 points
       Date   : 2022-05-02 11:00 UTC (2 days ago)
        
 (HTM) web link (blog.ploeh.dk)
 (TXT) w3m dump (blog.ploeh.dk)
        
       | dagss wrote:
       | At the boundaries are events. "User wanted to change time of
       | meeting".
       | 
       | Once you have recorded events, figuring out what side-effects
       | (often causing new events in other systems) should be triggered
       | from the set of all events input into the system can be coded
       | using whatever flavor of functional/relational/reactive
       | programming.
       | 
       | I think the combination of event sourcing and functional
       | programming, and databases that support this way of working
       | better than today and doesn't have OOP as their main target
       | audience, is the future. (And I absolutely don't mean Kafka. SQL
       | comes at least closer; to efficiently work with events and
       | implement functional business logic on top of events relations
       | and structure is important.)
        
       | jerf wrote:
       | I think you can say that at the boundaries, applications aren't
       | _anything_.
       | 
       | I tend to think of code in a cellular sense, as in, biology.
       | Outside the cell is a big scary world that you don't control.
       | There's a cell wall where you only permit certain things in.
       | Inside the cell, the cell trusts that only good stuff has been
       | let in. It may also have to package certain things on the way out
       | to the next cell.
       | 
       | In this case, the observation I'd make is about that big scary
       | external world. You don't get to impose anything on it, or at
       | least, your control is a great deal less rigid than our code
       | would like. Even if you think you control your internal network,
       | hackers might explain otherwise to you in the worst possible way.
       | You can't impose compliance with functional paradigms, imperative
       | paradigms, security levels, soundness of data, whether or not a
       | bit was flipped during transit, that the metadata matches what
       | was sent, or anything.
       | 
       | Obviously you can't fully write your code that way (even real
       | cells get it wrong sometimes too), but that's the principle I try
       | to view the world through. Even within an application where every
       | individual component is, say, compliant with functional
       | programming, the interactions still can't be counted on to have
       | any particular properties that you don't check somehow.
       | 
       | FP, OO, data-driven design, all that sort of stuff, that's for
       | what you do inside the cells, and maybe how you choose to
       | structure the code implementing your cell wall. But you almost
       | always end up forced to treat the outside world as bereft of any
       | structure you don't rigidly check for and enforce yourself, if
       | not outright hostile (anything with security implications).
        
         | ezy wrote:
         | Yea, I think a lot of programmers confuse the map for the
         | territory. It's not only the data, but the program itself.
         | 
         | Almost no one actually cares how a particular program was
         | written or how it understands it's input and output -- we care
         | that it works with some level of quality. How one gets that
         | result is irrelevant in the end. It could be written directly
         | in twisty assembly code. Do not care[1]
         | 
         | Parts of these paradigms have useful tools for building working
         | programs, but a great majority of the contents of these
         | paradigms are simply about organization. This shows up most
         | clearly in OO, and of course, functions are a way to organize
         | code. This isn't a bad thing -- certainly helpful for humans
         | working on a code base -- but it isn't actually relevant to the
         | task the program itself performs or how fast or well it
         | performs it.
         | 
         | So, of course, the input and output of a program aren't really
         | conformant to any paradigm, because the paradigms are about
         | organizing programs, not about performing a particular task.
         | 
         | [1] (it might even be more reliable, in some cases, because you
         | would be forced to be careful and pay attention and all those
         | little details you want to ignore are right there in your face
         | (see: async) :-))
        
           | mb20281 wrote:
           | I think you might miss the audience here...this is talking
           | /to/ programmers, after all - who decidedly _do_ care about
           | how the program is written, organized, etc.
           | 
           | I don't think anyone is making a "more correct" or even "more
           | performant" argument here; maybe, a "more reliable" argument
           | - but only by extension of "better organized, so less likely
           | to include certain classes of bugs".
        
         | cryptonector wrote:
         | > I think you can say that at the boundaries, applications
         | aren't anything.
         | 
         | I think you very much can say that "at the boundaries,
         | applications are procedural" (i.e., they do side-effecting
         | things sequentially).
         | 
         | That's _not_ incompatible with FP. On the contrary, FP used
         | properly lets you push all that procedural code to smallish
         | kernels at the  "boundaries" so that all the rest of the code
         | can be pure (thus easily tested).
        
           | jerf wrote:
           | "I think you very much can say that "at the boundaries,
           | applications are procedural" (i.e., they do side-effecting
           | things sequentially)."
           | 
           | You may be able to; I can't. I have a number of incoming
           | event streams that are not necessarily ordered.
           | 
           | Now, like I said, you don't implement all code everywhere for
           | all possible missteps, so you may have specific apps that get
           | away with assuming orderedness. But it is not a general thing
           | you can rely on.
        
             | cryptonector wrote:
             | > I have a number of incoming event streams that are not
             | necessarily ordered.
             | 
             | That doesn't make the application not-procedural. If it's
             | having side effects, it's procedural in a sense.
             | 
             | Order, here, is not about inbound event order, but about
             | ordered steps in processing any one event.
        
               | dragonwriter wrote:
               | > That doesn't make the application not-procedural. If
               | it's having side effects, it's procedural in a sense.
               | 
               | "Procedural" is a structural paradigm; unstructured
               | imperative code (old-school BASIC) has side effects but
               | is not procedural in any sense (it is part of the broader
               | category of imperative languages.) Also, "procedural" (or
               | "imperative" or "functional") is an attribute of
               | programming languages, not applications/systems: Haskell
               | is a pure functional programming languages, in which
               | evaluating functions has no side effects, but it can
               | define systems that have effects.
        
               | cryptonector wrote:
               | Perhaps I should have written "imperative" rather than
               | "procedural", except no one really writes imperative non-
               | procedural code now.
               | 
               | > Also, "procedural" (or "imperative" or "functional") is
               | an attribute of programming languages, not
               | applications/systems:
               | 
               | It can be an attribute of programs regardless of
               | language. It is possible to write functional-style code
               | in C and procedural code in Haskell (just do everything
               | in the IO monad!).
               | 
               | > Haskell is a pure functional programming languages, in
               | which evaluating functions has no side effects, but it
               | can define systems that have effects.
               | 
               | That's a bit of a fiction. Pure functions are pure, yes,
               | but you can have impure functions -- Haskell is only
               | interesting because you can have impure functions
               | (otherwise every Haskell program would compile down into
               | a constant), and what's really interesting about Haskell
               | is the ideas that have evolved in its community about how
               | to deal with impure functions.
        
               | dragonwriter wrote:
               | > Pure functions are pure, yes, but you can have impure
               | functions
               | 
               | Not in Haskell, you can't.
               | 
               | > Haskell is only interesting because you can have impure
               | functions
               | 
               | No, it's interesting because it provides a way of
               | representing series of effectful operations that are
               | distinct from its functions, which are pure.
               | 
               | > otherwise every Haskell program would compile down into
               | a constant
               | 
               | Every program in any language that is compiled _compiles_
               | down to a constant (which is, itself, usually a program,
               | often an imperative one in a native or virtual machine
               | language); but in Haskell, each program _is_ (in the
               | model of the language, not merely what they compile to)
               | normally a constant, non-function expression, most
               | commonly of type IO ().
               | 
               | It's true that values of type IO a are isomorphic to
               | "impure nullary functions returning a" in a language
               | which has impure functions, but they aren't functions in
               | Haskell, and can't be called from functions, they can
               | only be operated on as values within the language.
        
               | cryptonector wrote:
               | Haskell's effectful operations are functions. Oh sure,
               | not in the mathematical sense unless you model them as
               | functions from World -> World.
               | 
               | > Every program in any language that is compiled compiles
               | down to a constant ...
               | 
               | Sigh, yes, the compiled program is constant, but you know
               | what I meant: a constant value, such as a number, that
               | the program is expected to output. Let's not be this
               | pedantic.
        
               | dragonwriter wrote:
               | > Haskell's effectful operations are functions.
               | 
               | No, they aren't.
               | 
               | > Oh sure, not in the mathematical sense unless you model
               | them as functions from World -> World.
               | 
               | Also, not in the syntactical sense.
               | 
               | They are "functions" in that "in certain other
               | programming languages, the closest thing to something
               | filling the same role are called 'functions'."
               | 
               | But it's rather important to the way Haskell works that
               | they are a different thing that operate at a logically
               | different time, than functions.
               | 
               | > Sigh, yes, the compiled program is constant, but you
               | know what I meant
               | 
               | Yes, you meant reduction, not compilation, and you meant
               | non-function, not just constant, but Haskell programs
               | _do_ reduce to non-function constants.
        
           | munk-a wrote:
           | Things _might_ be sequential at the boundaries or they might
           | not. A lot of hardware interacting applications are forced to
           | handle concurrent and out of order inputs that are expected
           | to be processed in a particular order.
           | 
           | Really, since the boundary is where we push all the awful
           | stuff - that boundary (depending on the application) can be
           | any sort of terrible.
        
           | dragonwriter wrote:
           | > I think you very much can say that "at the boundaries,
           | applications are procedural" (i.e., they do side-effecting
           | things sequentially).
           | 
           | Well, side-effecting, yes, because that's literally how we
           | define boundaries.
           | 
           | Sequentially? Not so much; concurrency (whether asynchrony or
           | true parallelism) is important largely because simple
           | sequential behavior doesn't capture what happens naturally at
           | the boundaries well. (I suppose on an _individual_ boundary,
           | defined in the right way, there is likely to be a sequencing
           | constraint, but not in aggregate across the boundaries of the
           | system.)
        
             | cryptonector wrote:
             | You can parallelize as much as you want, but remember, you
             | can't go faster than the slowest serial code. There will be
             | some serial code. Again, I'm talking about order of steps
             | in processing events that themselves may not arrive in any
             | particular order.
        
       | PaulHoule wrote:
       | Time to wake up and smell the Flavor Aid:
       | 
       | https://news.ycombinator.com/item?id=535921
        
         | throwawaymaths wrote:
         | I think there are a lot of FPLs, like Erlang (and friends) and
         | F# (which is in TFA), Julia, which are #1 and at least mostly
         | #2, with their concommittant benefits, without the pain in the
         | ass for small benefit of #3
        
           | PaulHoule wrote:
           | I think it is more like somebody wrote a blog post that said
           | "FP rules, OO drools" and then other people thought "I'll
           | write a blog post like that!"
           | 
           | Or maybe it is easy to find fault with programming languages
           | you actually use and to idealize programming languages you
           | don't use.
           | 
           | I imagine a world where Common LISP won and
           | reddit/programming would be kvetching all the details CL got
           | wrong while asking "How can I get a COBOL Job?", "Did you
           | know that PHP syntax is based on Chomsky's generative
           | grammar?"
           | 
           | I worked on a project that involved building a stream
           | processing engine in Scala that heavily used Monads.
           | 
           | I remember being told by the manager what the error handling
           | strategy was and thinking "This is like that Amway
           | presentation where they 'draw circles' showing how 8 people
           | get a cut of the $7 tube of toothpaste they sell you and then
           | ask 'How can we beat the prices in the supermarket?', they
           | strike the presentation board with a pointer and say 'By
           | eliminating the middleman!'"
           | 
           | Now, they could have handled errors correctly with monads
           | just as they could have handled errors correctly with
           | exceptions, except that they didn't. That manager approved
           | code review after code review where error handling was
           | absent.
        
             | monksy wrote:
             | This really feels like most online communication and
             | promoted blogs on the social medias (hacker news included)
             | have gone to the "we're optimizing for tribe click
             | throughs" rather than technical due diligence and
             | excitement.
        
             | bigbillheck wrote:
             | > I imagine a world where Common LISP won and
             | reddit/programming would be kvetching all the details CL
             | got wrong
             | 
             | Much unlike the current timeline in which CL did not win
             | and people still complain about things it got wrong.
        
               | PaulHoule wrote:
               | I like the CL spec as an early example of a spec for a
               | language that balances performance and dynamism. In the
               | big picture I'd say that it taught people how to write
               | specs for languages like Java, Python, Javascript, etc.
               | 
               | CL was deeply unpopular at the time for quite a few
               | reasons: it really had a 32-bit mindset which made it a
               | bad fit for the machines many people had on their
               | desktops at the time, also the language has enough
               | performance-oriented details that it's not as simple as a
               | LISP can be.
        
       | xupybd wrote:
       | To me the big difference between OO and FP is complexity
       | management.
       | 
       | In OO complexity is hidden. So you don't have to deal with the
       | complexity of the internal state of an object while using the
       | object. It's a divide and conquer approach.
       | 
       | In FP complexity is constrained. Pure functions and immutable
       | data make it easier to reason about the code. This allows you to
       | see all the workings and not get overwhelmed.
        
       | cryptonector wrote:
       | > In 2011 I observed that at the boundaries, applications aren't
       | object-oriented. For years, I've thought it a natural corollary
       | that likewise, at the boundaries, applications aren't functional,
       | either. On the other hand, I don't think I've explicitly made
       | that statement before.
       | 
       | The next and last paragraph does not then explicitly make that
       | statement, instead ending with:
       | 
       | > Functional programming offers an alternative that, while also
       | not perfectly aligned with all data, seems better aligned with
       | the needs of working software.
       | 
       | I think that's right. OOP is just a disaster, but FP is not. FP
       | is not about making all of a program pure, but, rather, about
       | isolating all the bits that can be pure (thus making them easy to
       | test) and collecting all actual impurity into as small a bunch of
       | code as possible.
       | 
       | The impure code you end up having will look very procedural.
       | 
       | In Haskell, you do this by running all side-effect-having code
       | "in the IO monad", and monadic code looks procedural in the same
       | way that PROGN loops procedural in Lisp though PROGN is [or can
       | be] a macro that turns your procedural statements into one
       | singular expression.
       | 
       | So it's completely fair to say that "at the boundaries,
       | applications are procedural", because, well, it's patently true!
       | 
       | FP helps by helping the programmer push impure code as much as
       | possible towards that boundary, leaving the rest to be as pure
       | (and thus easily-tested) as possible.
       | 
       | For example, if you have code that uses the time of day for
       | externally-visible effects, then pass it the time of day so as to
       | make it more pure and easier to test. This one is counter-
       | intuitive because we like to just get-the-current-time, but I've
       | done this to make code that does epochal cryptographic key
       | derivation deterministic and, therefore, testable.
        
         | marginalia_nu wrote:
         | Theoretical arguments sound great and all, but where are the
         | results? If OOP is such a dumpster fire and FP is so
         | productive, where is all the FP code? Why are there so many OOP
         | projects? It's not like FP is something we've just discovered.
         | It's been around for decades.
         | 
         | Why does nobody appear to be having runaway success with it, if
         | it is the superior paradigm?
        
           | cryptonector wrote:
           | > Why does nobody appear to be having runaway success with
           | it, if it is the superior paradigm?
           | 
           | "Superior" doesn't always mean "winner".
        
           | [deleted]
        
           | CrimsonCape wrote:
           | I think the explanation is the fundamental reality: computers
           | are used to transform data, to store data, and to present
           | data; FP determines that two of three reasons are impure and
           | must be minimized.
           | 
           | John Carmack says that FP is a hinderance when rendering
           | graphics, working with buffers, etc. So in respect to
           | presenting data, UI and graphics will likely always be in an
           | imperative lang.
           | 
           | Rich Hickey's (creator of Clojure) company Datomics (i.e.
           | proprietary) uses his design for an immutable database which
           | has only been around since 2013. He says that disk storage
           | was so costly in the past but is now so cheap that the
           | existing server industry is built atop this legacy of old
           | ideas. So FP regarding storing data is likely in infancy.
        
         | monksy wrote:
         | I couldn't have said it better myself.
        
       | evocatus wrote:
       | Correct. In more mainstream language, quoth Gary Bernhardt,
       | "functional core, imperative shell."
       | 
       | Functional programming is a convenient fantasy, a highly
       | restrictive and controlled environment that allows us to make
       | large assertions about bodies of code - "no network IO can take
       | place here"; "your inputs will most assuredly be numbers that can
       | be added together."
       | 
       | It's the equivalent of assuming the cow is a sphere [0]. A useful
       | mental model, that ultimately breaks down upon contact with the
       | "real world."
       | 
       | Hence the imperative glue code / monadic actions wiring all of
       | the pretty, perfect abstractions together.
       | 
       | [0] https://en.wikipedia.org/wiki/Spherical_cow
        
         | cryptonector wrote:
         | Pure, deterministic code is easier to test than impure, non-
         | deterministic code. There's no test environment setup for the
         | former. No need to setup networking, DNS, PKI, etc. No need to
         | have containers. For the "functional code" you just furnish
         | inputs and compare to expected outputs.
         | 
         | Sort of like test vectors for cryptographic functions.
         | 
         | You still have to be careful to test all the edge cases
         | (assuming you can't test the full domain of each function),
         | naturally. But the fact that the functional core doesn't need
         | setup means the tests of it have less startup and teardown
         | overhead and so will generally run faster (unless they take so
         | much time that setup overhead is in the noise).
         | 
         | As for the "imperative shell", you may be able to mock
         | everything w/o having to change it, though you could also set
         | up a test environment with all the external things it needs.
        
         | kadoban wrote:
         | If they type system (or at worst norms of the
         | language/community) enforces it in parts of the code, that's
         | _much_ more than a mental model.
        
         | AnimalMuppet wrote:
         | I think it's more like, out of this actual, physical cow, we
         | are going to carve a perfectly spherical cow plus some...
         | "other" parts.
         | 
         | That is, you can make chunks of your application functional -
         | they just can't be chunks that touch the exterior. It's not a
         | "mental model" - it's something you construct in the code.
         | 
         | Now, you may not be able to do that with _all_ the  "interior"
         | code, either. Parts may have too much intrinsic state for
         | functional programming to be a useful approach. But for other
         | interior parts, hey, you like functional? Make it so.
        
       ___________________________________________________________________
       (page generated 2022-05-04 23:02 UTC)