Live data from Hacker News

Functional Programming Self-Affirmations

norikitech.com

21–30 of 119 posts

Re: Functional Programming Self-Affirmations

#21

Earlier quoted context omitted.

I'm not convinced that you can follow all of these outside of Functional Programming. How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system? How can you do "Errors as values" at a large scale without do-notation / monads? How can you do "Functional core, imperative shell" without the ability to create mini DSLs and interpreters in…

> How can you do "Errors as values" at a large scale without do-notation / monads? You don't need monads for this. You just need the ability to encode your error in some term like `Either e a` and ability to eliminate those terms. In Rust for example that is the `Result` type and you use pattern matching to eliminate those terms.

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.

Re: Functional Programming Self-Affirmations

#22

These are great ideas and patterns even if you’re not doing functional programming. FP-first/only languages tend to push you in these directions because it makes programming with them easier. In languages where FP is optional, it takes discipline and sometimes charisma to follow these affirmations/patterns/principles.. but they’re worth it IMO.

I'm not convinced that you can follow all of these outside of Functional Programming. How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system? How can you do "Errors as values" at a large scale without do-notation / monads? How can you do "Functional core, imperative shell" without the ability to create mini DSLs and interpreters in…

> How can you "Make illegal states unrepresentable" with mutable state

By either making the data const, or encapsulating mutable state with private fields and public methods

> How can you do "Errors as values"

Go, Rust, Odin, Zig, and many more are imperative languages that do exactly this

> How can you do "Functional core, imperative shell"

Write a function that takes the old state and returns the new state

Re: Functional Programming Self-Affirmations

#23

These are great ideas and patterns even if you’re not doing functional programming. FP-first/only languages tend to push you in these directions because it makes programming with them easier. In languages where FP is optional, it takes discipline and sometimes charisma to follow these affirmations/patterns/principles.. but they’re worth it IMO.

I'm not convinced that you can follow all of these outside of Functional Programming. How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system? How can you do "Errors as values" at a large scale without do-notation / monads? How can you do "Functional core, imperative shell" without the ability to create mini DSLs and interpreters in…

> How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system?

I think you're confusing "make illegal states unrepresentable" with "parse, don't verify"? If your type cannot represent any invalid states, there's no way you can reach them through mutation.

Re: Functional Programming Self-Affirmations

#24

Earlier quoted context omitted.

I'm not convinced that you can follow all of these outside of Functional Programming. How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system? How can you do "Errors as values" at a large scale without do-notation / monads? How can you do "Functional core, imperative shell" without the ability to create mini DSLs and interpreters in…

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 abusing co-routines or perhaps async/await in various languages, but it will never be as good as something built for this purpose.

Type-inferences, type-classes and do-notation make monads workable in practice.

Re: Functional Programming Self-Affirmations

#25

Earlier quoted context omitted.

I'm not convinced that you can follow all of these outside of Functional Programming. How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system? How can you do "Errors as values" at a large scale without do-notation / monads? How can you do "Functional core, imperative shell" without the ability to create mini DSLs and interpreters in…

> How can you "Make illegal states unrepresentable" with mutable state By either making the data const, or encapsulating mutable state with private fields and public methods > How can you do "Errors as values" Go, Rust, Odin, Zig, and many more are imperative languages that do exactly this > How can you do "Functional core, imperative shell" Write a function that takes the old state and returns the new state

> By either marking your data as const, or encapsulating mutations with private fields and public methods

That would seem to be making illegal states unreachable rather than unrepresentable, closer in spirit to "parse, don't verify".

Re: Functional Programming Self-Affirmations

#26
> 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 great idea until you have 20 options to account for and your enum is now 2^16 entries long. Then your company opens a branch in a different country with a different regulatory framework and the options list grows to 50 and you code no longer fits on a hard drive.

Re: Functional Programming Self-Affirmations

#27

Earlier quoted context omitted.

> How can you do "Errors as values" at a large scale without do-notation / monads? You don't need monads for this. You just need the ability to encode your error in some term like `Either e a` and ability to eliminate those terms. In Rust for example that is the `Result` type and you use pattern matching to eliminate those terms.

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.

`?` and `let ... else` both partially address this, yeah.

Re: Functional Programming Self-Affirmations

#28

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…

Rust does "errors as values" pretty well, see [1]. You can manually handle them if you want, or just apply the '?' operator to auto-propagate them out of the function. In both cases, it is obvious that there was an error handling needed. [1] https://doc.rust-lang.org/book/ch09-02-recoverable-errors-wi...

I didn't know that! Every time I hear about Rust my opinion of it grows brighter. Thanks for sharing this!

Re: Functional Programming Self-Affirmations

#29

Earlier quoted context omitted.

> How can you do "Errors as values" at a large scale without do-notation / monads? You don't need monads for this. You just need the ability to encode your error in some term like `Either e a` and ability to eliminate those terms. In Rust for example that is the `Result` type and you use pattern matching to eliminate those terms.

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.

Re: Functional Programming Self-Affirmations

#30

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 caller, as opposed to control jumping around wildly because of thrown errors.

In a statically typed imperative language that would need support for sum-types, to be able to return either an error or a non-error-value. Then you would be less likely to ignore the errors by accident because you would always see the return value is maybe an error.

Isn't this also basically how Haskell does it, handling side-effectful values as high up as as possible which in Haskell means moving them into the runtime system above all user-code?

Post reply on HN