Live data from Hacker News

Functional Programming Self-Affirmations

norikitech.com

41–50 of 119 posts

Re: Functional Programming Self-Affirmations

#41
FP nerd: The pure core is nice and composable, with the imperative shell at the boundary.

State Skeptic: Yes, But! How do you compose the 'pure core + impure shell' pieces?

FPN: Obviously, you compose the pure pieces separately. Your app can be built using libraries built from libraries.... And, then build the imperative shell separately.

My take is that the above solution is not so easy. (atleast to me!) (and not easy for both FP and non-FP systems).

Take an example like GUI components. Ideally, you should be able to compose several components into a single component (culminating in the app) and not have a custom implementation of a giant state store which is kept in something like Redux and you define the views and modifiers using this store.

Say, you have a bunch of UI components each given as a view computed as a function from a value and possible UI events which can either modify the value, remain unhandled or configurable as either. Ex: dialog box which handles text events but leaves the 'OK' submission to the container.

There are atleast two different kinds of composability (cue quote in SICP Ch1 by Locke) - aggregation and abstraction. Ex: Having a sequence of text inputs in the document(aggregation) and then abstracting to a list of distances between cities. This abstraction puts constraints on values of the parts, both individually(positive number) and across parts(triangle inequality). There is also extension/enrichment, the dual of abstraction.

This larger abstracted component itself is now a view dependent on a value and more abstract events. But, composing recursively leads to state being held in multiple layers and computations repeated across layers. This is somewhat ameliorated by sharing of immutable parts and react like reconciliation. But, you have to express your top->down functions incrementally which is not trivial.

Re: Functional Programming Self-Affirmations

#42

> Make illegal states unrepresentable This is a nice ideal to shoot for, but strict adherence as advocated in the article is a short path to algorithmic explosions and unusable interfaces on real life systems. For example, if you have two options that are mutually incompatible, this principle says you don't make them booleans, but instead a strict enum type populated with only legal combinations of the options. A gre…

The mistake here is having 2^16 valid options. If you truly do have 2^16 valid and distinct behaviors, it is not possible for humans to correctly write 2^16 different code paths anyway. More than likely, the majority of the combinations of your 29 Boolean flags are invalid. You should strive to have that checked by the program, and not only as a note in external documentation. No one is saying int should be turned in…

The parent is discussing a situation where you have 16 distinct, binary states many but not all of which are mutually compatible. So you can have 16 bit vector of the states, 16 binary variables or an enum of valid states - the enum would have O(2^16) members because of the distribution of valid states but unlike the others, an invalid state would not be possible to represent.

Re: Functional Programming Self-Affirmations

#43

I do not work in a functional language, but these ideas have helped me a lot anyway. The only idea here that I find less directly applicable outside purely functional languages is the "Errors as values [instead of exceptions]" one. On the surface, it makes complete sense, the non-locality of exceptions make them hard to reason about for the same reasons that GOTO is hard to reason about, and representing failure mode…

Non-locality of exceptions is a feature , not a bug. It's so you can focus on the success case, instead of error case, when reading your code. It's usually, but not always, what you want. "errors as values" is effectively the same thing as exceptions anyway, except it's explicit - meaning it's hurting readability by default and adding extra work[0]; modern languages go to extreme length to try and paper it over with…

In general, I find that explicit code is more easily read than implicit code. I prefer static over dynamic typing, I actually _like_ the explicitness of async/await or the IO monad. If something allows me to find out information about my current context without having to move up or down the stack and reading the code in other functions, I'm pretty happy about that something, because reading code is slow and tedious. What is it about implicit code that makes you feel it's more readable?

Re: Functional Programming Self-Affirmations

#44
post #41

FP nerd: The pure core is nice and composable, with the imperative shell at the boundary. State Skeptic: Yes, But! How do you compose the 'pure core + impure shell' pieces? FPN: Obviously, you compose the pure pieces separately. Your app can be built using libraries built from libraries.... And, then build the imperative shell separately. My take is that the above solution is not so easy. (atleast to me!) (and not ea…

FP is not a silver bullet. GUI is the classic OOP showcase.

> Ideally, you should be able compose them several of them into a single app and not have a custom implementation of a giant state

If you are suggesting that components store their state, I'm not sure about "ideal" there. That works well for small GUI applications. In GUI applications of modest size, you do want a separate, well-organized and non-redundant data layer you can make sense of, at least from my experience. Qt, specifically, allows you to do both things.

Re: Functional Programming Self-Affirmations

#45

> Make illegal states unrepresentable This is a nice ideal to shoot for, but strict adherence as advocated in the article is a short path to algorithmic explosions and unusable interfaces on real life systems. For example, if you have two options that are mutually incompatible, this principle says you don't make them booleans, but instead a strict enum type populated with only legal combinations of the options. A gre…

True. And what is an "illegal state" anyway? If two options are "mutually incompatible" it means that they, as far as we know, so far, have never occured at the same time.

But that is just how things are currently. The world may change. And it's important to strive for maintainable code that can accommodate such change easily.

Expanding company operations to work in a different country is an example of this (our) "world changing". States that never occur together here, may occur together there. Or in the future more generally.

So, making illegal states non-representable is about avoiding errors in respect to the system specification. But it does not take into account the fact that in the real world specifications typically evolve and change even during development, and perhaps more so later.

Re: Functional Programming Self-Affirmations

#46

I do not work in a functional language, but these ideas have helped me a lot anyway. The only idea here that I find less directly applicable outside purely functional languages is the "Errors as values [instead of exceptions]" one. On the surface, it makes complete sense, the non-locality of exceptions make them hard to reason about for the same reasons that GOTO is hard to reason about, and representing failure mode…

The way to do functional programming in imperative languages is to handle the side-effects as high up in the call-chain as possible. That would mean that you return an instance of Error from lower-level and decide in some higher caller what to do about it. That as an alternative to throwing the error. This way you get the benefit of being able to follow the flow of control from each called function back to each calle…

Right, I understand. But my question is, how do you _ensure_ a failure value is dealt with by clients? In purely functional languages, your clients have no choice, they'll have to do something with it. In imperative languages, they can just ignore it.

Re: Functional Programming Self-Affirmations

#47

> Make illegal states unrepresentable This is a nice ideal to shoot for, but strict adherence as advocated in the article is a short path to algorithmic explosions and unusable interfaces on real life systems. For example, if you have two options that are mutually incompatible, this principle says you don't make them booleans, but instead a strict enum type populated with only legal combinations of the options. A gre…

The mistake here is having 2^16 valid options. If you truly do have 2^16 valid and distinct behaviors, it is not possible for humans to correctly write 2^16 different code paths anyway. More than likely, the majority of the combinations of your 29 Boolean flags are invalid. You should strive to have that checked by the program, and not only as a note in external documentation. No one is saying int should be turned in…

You only have 20 options, but making that many distinctive options is not exactly a stretch. It's not like every single set of options is its own code path, most options represents a small deviation at one particular part of the code. Most of the options aren't mutually exclusive either, only a few combinations are illegal.

Imagine a simple shipping system for example. The package might be routed via truck, boat, plane, pipeline, foot, etc... Probably even a combination of those options. The package might be low, medium, or high priority, although high priority packages are not allowed to be delivered by boat. The package might be small, large, or liquid, but liquids can't be delivered by foot. There are 195 different countries with even more regulatory regimes to consider, some of which may have different customs requirements based on mode of transport. Now imagine a problem that is actually complicated.

The idea of encoding all of this business logic into the datatype is a road to madness IMHO. Especially if the rules can change on you and require you to rework your datatypes to match. On the small scale this makes a lot of sense and is good advice, but strict adherence is impractical.

Re: Functional Programming Self-Affirmations

#48

Earlier quoted context omitted.

Genuinely curious for all these follow up questions: Is immutability exclusive to functional programming? Is the ability to use data/values exclusive to functional programming? Are monads exclusive to functional programming? For discussions like this, how do we separate "it was done first in functional programming but can also be done in procedural programming" with "it cannot be followed outside of functional progra…

> Is immutability exclusive to functional programming? No, but immutable defaults are powerful. E.g. in JavaScript / Python, the built-in lists and dictionarys (which are blessed with special syntax) are mutable. > Is the ability to use data/values exclusive to functional programming? No, but expression-orientation makes this less painful > Are monads exclusive to functional programming? You can hack them in by abusi…

You don't need coroutines or async or anything complicated to model monads, just functions and data structures. Search for "c++ monads" and you'll find a ton of examples.

Re: Functional Programming Self-Affirmations

#49

> Make illegal states unrepresentable This is a nice ideal to shoot for, but strict adherence as advocated in the article is a short path to algorithmic explosions and unusable interfaces on real life systems. For example, if you have two options that are mutually incompatible, this principle says you don't make them booleans, but instead a strict enum type populated with only legal combinations of the options. A gre…

This is an excellent illustration. I feel functional programming provides great tools and concept for a cohesive system (one with consistent requirements, which accomplishes a single thing, etc). But many programs you write involve parts that aren't very cohesive and trying to make them cohesive is far more work than just bolting on a case statement or similar things. Parent currently downvoted without comments. Seem…

I feel like you might be missing the point. A case statement is not to be treated as something you bolt on as a hack. It is the right tool for the job the vast majority of the times. When you use case statements, you refine and reduce your state space. It makes code easier to understand. When you combine this with the idea of making illegal states unrepresentable, a case statement gives you an exhaustive listing of what could happen. Even a lot of Haskell programmers, after using things like the `maybe` function to eliminate Maybe types and things like `fmap` over Maybe, eventually find that using case expressions produces the clearest code even though it may be one line longer.

I really hope HN enforces a rule that a downvote must be accompanied by a reply.

Re: Functional Programming Self-Affirmations

#50
post #29

Earlier quoted context omitted.

Then you end up with a pyramid of doom. Fine for small examples but it doesn't scale up easily. Rust has special syntax (`?`) for this AFAICT.

That's merely syntax sugar. Haskell doesn't even have this sugar and so in a monad you have to bind explicitly, and it's fine. It's not a pyramid of doom.

Haskell has do-notation which is a more general kind of this syntactic sugar
Post reply on HN