Live data from Hacker News

Functional Programming Self-Affirmations

norikitech.com

91–100 of 119 posts

Re: Functional Programming Self-Affirmations

#91

Earlier quoted context omitted.

> 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".

GP seemed more worried about maintaining invariants in the face of mutability, so that's what my answer spoke to.

For modeling the data in the first place, just use the right combination of sum types, product types, and newtypes - that's not specific to functional languages. I'm sure GP knew this already without me saying it. Sum types may have been a "functional programming" thing a few decades ago but they aren't anymore.

Re: Functional Programming Self-Affirmations

#92
For "Errors as values", I agree 100% that it's better then special values or untracked exceptions but I also think that current programming languages lack the features that allow encoding errors as values conveniently. Firstly, there is no composition of errors. If I use a library for a network call and then use another library for a database query, now the possible errors should be the union of the errors that can be returned from the either of the functions. But most practical languages lack the mechanism to do that (except OCaml). One has to define a wrapper type just to encode that particular composition. And it won't work if I want to handle for example Not Found case but not Internal Server Error. I see this is because most statically typed languages have nominal typing and not structural typing. But it is a necessity for pretty much any software otherwise people will just see that tracking errors is too much trouble in terms of composition.

Re: Functional Programming Self-Affirmations

#93

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…

Simple, your compiler/type checker/static analyzer/whatever needs to ensure that any value that is not unused gives an error. But you need some sort of static analysis for that. Without type checking you don't do that of course.

Re: Functional Programming Self-Affirmations

#94

> 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…

In this example, is it really the case that all 20 are dependent into each other so you have to combine all of them into a single enum? For the ones that are independent you can keep them as booleans and you should.

Re: Functional Programming Self-Affirmations

#95
post #92

For "Errors as values", I agree 100% that it's better then special values or untracked exceptions but I also think that current programming languages lack the features that allow encoding errors as values conveniently. Firstly, there is no composition of errors. If I use a library for a network call and then use another library for a database query, now the possible errors should be the union of the errors that can b…

> I see this is because most statically typed languages have nominal typing and not structural typing.

I don't think that's it. I think what's needed is an accumulator method for error interfaces so that you can accumulate new errors into existing errors.

Re: Functional Programming Self-Affirmations

#96
post #37

Earlier quoted context omitted.

Nobody says you have to have a single enum type containing all the combinations. Chances are, you can use sum types (discriminated unions) to factor things nicely if you think about them. For example if option B is only relevant when option A is set to true, you can have something like data OptA = ATrue OptB | AFalse data OptB = BTrue | BFalse There are three valid combinations but no type has three alternatives. Nob…

Imagine a case where you have 4 options. W, X, Y, Z. Y and Z are mutually exclusive. X can only be set if W is set. If Y is set then X must be set. Going down this road you end up encoding your business logic into your datatypes. Which is good to a degree, but makes things messy when new options are added or requirements change. Imagine a new option U is introduced that is only valid when W is unset and Z is set but…

What is the alternative? And is it really better than encoding into the data type?

Only option I can think of is writing unit tests for each case, which doesn't seem like too much different.

And without type encoding you never sure that invariant always hold. You can always manipulate any options in any part of the program. Then after any modification you have to perform a "sanity check" if options are in a well defined state or not. I don't see how this is better than encoding the invariant into the types.

Re: Functional Programming Self-Affirmations

#97

Earlier quoted context omitted.

Where does this exponentional size requirement come from?

Imagine you have a program where there are 4 options, W, X, Y, Z. Y and Z can't be set at the same time, and X can't be set unless W is set. If Y is set then X must be set as well. How do you represent this in a way that makes it impossible, even through programmer error elsewhere in the program, to have the flags in an invalid state? You can create en enum that looks like: enum program_state = ( W_X_Y_NZ, W_NX_NY_Z,…

  enum program_state {
    X_W,
    Y_X_W,
    Z,
    W,
    Z_W,
    Z_X_W,
  }
You only have these 6 options. And in this case you can easily use a SAT solver to generate the cases for you so that you don't have to write them by hand.

Re: Functional Programming Self-Affirmations

#98
post #92

For "Errors as values", I agree 100% that it's better then special values or untracked exceptions but I also think that current programming languages lack the features that allow encoding errors as values conveniently. Firstly, there is no composition of errors. If I use a library for a network call and then use another library for a database query, now the possible errors should be the union of the errors that can b…

> I see this is because most statically typed languages have nominal typing and not structural typing. I don't think that's it. I think what's needed is an accumulator method for error interfaces so that you can accumulate new errors into existing errors.

But this way you don't track what you handled in type level it's only available in runtime. So you are back to untracked errors.

Re: Functional Programming Self-Affirmations

#100
post #82
post #71

Earlier quoted context omitted.

""Make illegal states unrepresentable" is way too expensive to implement." This has not been my experience. The speed increase in development not having to worry about the unrepresentable cases have been very valuable. In addition as requirements change migrating old data hasn't been a huge concern. For code changes refactoring the types helps address new cases as well.

The difficulty of Making illegal state unrepresentable depends entirely on the domain you're working on. And whether discarding the occasional invalid transaction is viable. If you're writing a CMS/wiki software, it's gonna be pretty straightforward to do. If you're working with transactions, trades, contracts etc, it's not.

>If you're writing a CMS/wiki software, it's gonna be pretty straightforward to do.

>If you're working with transactions, trades, contracts etc, it's not.

why not?

what's the difference between those two categories, mentioned in your last two sentences, as far as this argument about illegal states is concerned? not clear to me.

in fact, just a few weeks ago, I saw a video by yaron minsky of jane street about ocaml. the title of it might have been "why ocaml". I know he has a video by that name. just not sure whether that was the one I saw or another one by him.

it can easily be googled.

in that video, he talks a fair amount about how jane street uses ocaml, including on how they use it (including defining types for kinds of business domain data, iirc) to make "invalid states unrepresentable" - in fact, iirc, that was the title of one of the slides of his talk.

i remember that very well because i was kind of impressed by the idea, although i have not checked it out practically myself yet.

but I don't know much about this area, so I'm not saying that either he or you are wrong. just asking for clarification / explanation.

also, trades and transactions seems to be the main area that jane street works in.

edited for spelling/wrong autocorrect:

s/one I show/one I saw/

Post reply on HN