Live data from Hacker News

Functional Programming Lessons Conclusion

jerf.org

11–20 of 48 posts

Re: Functional Programming Lessons Conclusion

#11
post #7
post #3

I've always thought that there should be mutability of objects within the function that created them, but immutability once the object is returned. Ultimately one of the major goals of immutability is isolation of side effects.

How does this work out for functions in the middle of the call stack? Can the objects a function creates be mutated by functions they call? Phrased differently, can functions modify their input parameters? If a function returns one of their input parameters (modified or not), does that mean the calling function can no longer mutate it? Maybe I'm discarding this too readily, but I don't think this idea of "local mutab…

If we include as a axiom for the purposes of this conversation that we must be able to refactor out any part of the "constructor" and that the refactored function must have the same mutation "privileges" as the original creator, which I think is fairly reasonable, this leads you in the direction of something like Rust I think, which can construct objects and lend bits and pieces of it out in a controlled manner to auxiliary functions, but the value being returned can still have constraints on it.

Re: Functional Programming Lessons Conclusion

#12
post #8

> I consider [having a big benefit at 100% vs an 80/20 rule] a characteristic of type systems in general; a type system that you can rely on is vastly more useful than a type system you can almost rely on, and it doesn’t take much “almost” to greatly diminish the utility of a given type system. This! This is why I don't particularly care for gradual typing in languages like Python. It's a lot of extra overhead but yo…

I agree with the 100% rule. The problem with Typescript is how many teams allow “any”. They’ll say, “We’re using TypeScript! The autocomplete is great!” And on the surface, it feels safe. You get some compiler errors when you make a breaking change. But the any’s run through the codebase like holes in Swiss cheese, and you never know when you’ll hit one, until you’ve caused a bug in production. And then they try to d…

In my rather small code base I’ve been quite happy with ”unknown” instead of any. It makes me use it less because of the extra checks, and catches the occasional bug, while still having an escape hatch in cases of extensive type wrangling.

The other approach, having an absolutist view of types, can be very constraining and complex even for relatively simple domain problems. Rust for instance is imo in diminishing returns territory. Enums? Everyone loves them, uses them daily and even write their own out of joy. OTOH, it took years of debate to get GATs implemented (is it now? I haven’t checked), and not because people like and need them, but because they are a necessary technicality to do fundamental things (especially with async).

Re: Functional Programming Lessons Conclusion

#13
post #7
post #3

I've always thought that there should be mutability of objects within the function that created them, but immutability once the object is returned. Ultimately one of the major goals of immutability is isolation of side effects.

How does this work out for functions in the middle of the call stack? Can the objects a function creates be mutated by functions they call? Phrased differently, can functions modify their input parameters? If a function returns one of their input parameters (modified or not), does that mean the calling function can no longer mutate it? Maybe I'm discarding this too readily, but I don't think this idea of "local mutab…

I mean, this isn't that different from any number of things you do in real life? You took a car to some destination. It is assumed you didn't change the engine. Took it to a mechanic, maybe they did?

More, many modifications are flat out expected. You filled out a job application, you probably don't expect to change the job it is for. But you do expect that you can fill out your pieces, such that it is a modifiable document while you have it. (Back to the car example, it is expected that you used fuel and caused wear on the tires.)

As annoying as they were to deal with, the idea of having "frozen" objects actually fits really well with how many people want to think of things. You open it to get it ready for use. This will involve a fair bit of a setup. When done, you expect that you can freeze those and pass off to something else.

Transactions can also get into this. Not surprising, as they are part of the vocabulary we have built on how to deal with modifications. Same for synchronization and plenty of other terms. None of them go away if you just choose to use immutable objects.

Re: Functional Programming Lessons Conclusion

#14
post #10

web development today is literally just massive massive mutation operations on databases. Functional programming can't stop it, it just sort of puts a fence around it. The fence makes sense if it's just 10% of your program that you want to fence off. But the database is literally the core of our application then it's like putting a fence around 90% of the house and you have 10% of pure functional programming. Most op…

There must be something wrong in the way FP is taught if the takeaway that people have is that it prevents or is somehow opposed to mutation. On the one hand you have bunch of FP languages that don't care in the least bit about "purity" (i.e. being side-effect free) or are more pragmatic around it, such as various LISPs, OCaml, Scala or even parts of the JS ecosystem. And on the other hand, there's a lot of research…

> The idea here is not that side effects are bad, but that we want to know where they happen and combine them safely.

Yeah, the idea is not that people gathering together in groups more than two and/or past the 21:00 is bad, but that we want to know where it happens and ensure safety for all. Now, your papers, please or we'll apply the type checker (we'll apply it to y'all anyhow, of course, but we'd like you to cooperate with the inference process).

Re: Functional Programming Lessons Conclusion

#15
post #7
post #3

I've always thought that there should be mutability of objects within the function that created them, but immutability once the object is returned. Ultimately one of the major goals of immutability is isolation of side effects.

How does this work out for functions in the middle of the call stack? Can the objects a function creates be mutated by functions they call? Phrased differently, can functions modify their input parameters? If a function returns one of their input parameters (modified or not), does that mean the calling function can no longer mutate it? Maybe I'm discarding this too readily, but I don't think this idea of "local mutab…

Local mutability is fantastic and practical... In a language like Haskell where the type system tracks exactly what values are mutable and all mutation is precisely scoped by functions that freeze the value they generate in a way that prevents leaking.

In a language that isn't so precise, it's a lot harder to get value from the idea.

Re: Functional Programming Lessons Conclusion

#16

> I consider [having a big benefit at 100% vs an 80/20 rule] a characteristic of type systems in general; a type system that you can rely on is vastly more useful than a type system you can almost rely on, and it doesn’t take much “almost” to greatly diminish the utility of a given type system. This! This is why I don't particularly care for gradual typing in languages like Python. It's a lot of extra overhead but yo…

It may be the exact opposite. You can't express (at least you shouldn't try to avoid Turing tarpit-like issues) all the desired constraints for your problem domain using just the type system (you need a readable general purpose programming language for that).

If you think your type system is both readable and powerful then why would you need yet another programming language? (Haskell comes to mind as an example of such language--don't know how true it is). The opposite (runtime language used at compile time) may also be successful eg Zig.

Gradual typing in Python provides the best of both worlds: things that are easy to express as types you express as types. On the other hand, you don't need to bend over backwards and refactor half your code just to satisfy your compiler (Rust comes to mind). You can choose the trade off suitable for your project and be dynamic where it is beneficial. Different projects may require a different boundary. There is no size fits all.

P.S. As I understand it, the article itself is about "pragmatism beats purity."

Re: Functional Programming Lessons Conclusion

#17
post #10

Earlier quoted context omitted.

There must be something wrong in the way FP is taught if the takeaway that people have is that it prevents or is somehow opposed to mutation. On the one hand you have bunch of FP languages that don't care in the least bit about "purity" (i.e. being side-effect free) or are more pragmatic around it, such as various LISPs, OCaml, Scala or even parts of the JS ecosystem. And on the other hand, there's a lot of research…

> The idea here is not that side effects are bad, but that we want to know where they happen and combine them safely. Yeah, the idea is not that people gathering together in groups more than two and/or past the 21:00 is bad, but that we want to know where it happens and ensure safety for all. Now, your papers, please or we'll apply the type checker (we'll apply it to y'all anyhow, of course, but we'd like you to coop…

I don't understand why people get so angry when a compiler points out that their code is broken. Is it better if runs and does the wrong thing instead?

Re: Functional Programming Lessons Conclusion

#18
post #10

web development today is literally just massive massive mutation operations on databases. Functional programming can't stop it, it just sort of puts a fence around it. The fence makes sense if it's just 10% of your program that you want to fence off. But the database is literally the core of our application then it's like putting a fence around 90% of the house and you have 10% of pure functional programming. Most op…

There must be something wrong in the way FP is taught if the takeaway that people have is that it prevents or is somehow opposed to mutation. On the one hand you have bunch of FP languages that don't care in the least bit about "purity" (i.e. being side-effect free) or are more pragmatic around it, such as various LISPs, OCaml, Scala or even parts of the JS ecosystem. And on the other hand, there's a lot of research…

No you missed the point. I completely get the meaning of segregating IO/mutation away from pure logic.

And my point is, what is the purpose of all of this is 90% of what your app does is mutation and side effects? Functional shell, imperative core indeed, but the shell is literally just thin layer of skin. The imperative core is a massive black hole.

Functional programming can't save you from black hole.

Re: Functional Programming Lessons Conclusion

#19
post #12
post #8

Earlier quoted context omitted.

I agree with the 100% rule. The problem with Typescript is how many teams allow “any”. They’ll say, “We’re using TypeScript! The autocomplete is great!” And on the surface, it feels safe. You get some compiler errors when you make a breaking change. But the any’s run through the codebase like holes in Swiss cheese, and you never know when you’ll hit one, until you’ve caused a bug in production. And then they try to d…

In my rather small code base I’ve been quite happy with ”unknown” instead of any. It makes me use it less because of the extra checks, and catches the occasional bug, while still having an escape hatch in cases of extensive type wrangling. The other approach, having an absolutist view of types, can be very constraining and complex even for relatively simple domain problems. Rust for instance is imo in diminishing ret…

Typescript's --strict is sometimes a very different ballgame from default. I appreciate why in a brownfield you start with the default, but I don't understand how any project starts greenfield work without strict in 2025. (But also I've fought to get brownfield projects to --strict as fast as possible. Explicit `any` at least is code searchable with the most basic grep skills and gives you a TODO burndown chart for after the fastest conversion to --strict.)

Typescript's --strict still isn't technically Sound, in the functional programming sense, but that gets back to the pragmatism mentioned in the article of trying to get that 80/20 benefit of enough FP purity to reap as many benefits without insisting on the investment to get 100% purity. (Arguably why Typescript beat Flow in the marketplace.)

Re: Functional Programming Lessons Conclusion

#20
post #7

Earlier quoted context omitted.

How does this work out for functions in the middle of the call stack? Can the objects a function creates be mutated by functions they call? Phrased differently, can functions modify their input parameters? If a function returns one of their input parameters (modified or not), does that mean the calling function can no longer mutate it? Maybe I'm discarding this too readily, but I don't think this idea of "local mutab…

Local mutability is fantastic and practical... In a language like Haskell where the type system tracks exactly what values are mutable and all mutation is precisely scoped by functions that freeze the value they generate in a way that prevents leaking. In a language that isn't so precise, it's a lot harder to get value from the idea.

You can get value out of local mutability in languages like Scala or Kotlin. Variables can be declared mutable in the scope of a function, and their value can then later be assigned to a non-mutable variable, for example. Collections also come in mutable and immutable variants (although this has some pitfalls in Kotlin).
Post reply on HN