Earlier quoted context omitted.
Can you elaborate?
They don't get stack traces, for one. (That's arguably the biggest problem with Rust: .unwrap() gives you a stack trace, but has problems; whereas ? erases your stack trace.) In principle, static analysis could identify unhandled exceptions, then trace the exception, then make that information available to the top-level "Err returned from main" handler. In practice, that's never going to happen in Rust.
Functional languages should be so much better at mutation than they are
151–160 of 188 posts
Re: Functional languages should be so much better at mutation than they are
#152Earlier quoted context omitted.
This is a hill I will die on. Probably literally. If you are writing in a metaphor of equations, than yes, mutation is almost certainly going to bite you. If you are writing in a metaphor of process, you almost certainly want to manage, as you say. I feel that early texts were good at this. Turtle Geometry is my personal favorite book in this vein. I seem to recall we spent a long time going over how to double buffer…
> The entire point is to change things We've other 'entire points' along the way. Allocation and freeing of memory are fundamental to computing. We don't do malloc and free any more. Control-flow (selecting which instruction to follow next) is also fundamental. We don't to goto anymore.
Control-flow is an awkward choice there, as goto is not necessarily fundamental for how control-flow is run for a lot of code. And I have absolutely used labeled break/continue in Java before for a control loop that ran great until people tried to refactor to use more indirect control.
I also think it is interesting as I greatly prefer code where you can do basic left/right and top/down reading to know what is intended by the code.
At any rate, my original intent was to discuss code that is controlling something works really well if you embrace a metaphor for the code you are in.
Re: Functional languages should be so much better at mutation than they are
#153Earlier quoted context omitted.
Aside: Could a Rust library provide an Rc interface but use a more sophisticated GC algorithm underneath?
You can, but it turns out that, as one may intuitively expect, a GC is never needed unless implementing a VM for a GC-based language or an API that required GC like fd passing on unix domain sockets, and those generally want an ad-hoc GC instead tailored to whatever you are implementing. Since it's not needed and it's massively worse than reference counting (assuming you only change reference counts when essential an…
So, there’s a performance hit of some kind with optional tuning that takes no expertise. Many people would go for those tradeoffs for some applications. Especially if they got to keep using safe, no-GC code in their libraries with their efficiency benefits.
Re: Functional languages should be so much better at mutation than they are
#154> ...functional programming is mostly about avoiding mutation at all costs Slightly different perspective from Grokking Simplicity[1]: functional programming is not about avoiding mutation because "mutation is bad." In fact, mutation is usually the desired result, but care must be taken because mutation depends on when and how many times it's called. So good FP isn't about avoiding impure functions; instead it's abou…
Re: Functional languages should be so much better at mutation than they are
#155Re: Functional languages should be so much better at mutation than they are
#156> A lot of people think that functional programming is mostly about avoiding mutation at all costs. People should try to stop thinking of mutation as something to be avoided, and start thinking of it as something to be managed. Mutating state is good. That's usually the whole point. What's bad is when you create "accidental state" that goes unmanaged or that requires an infeasible effort to maintain. What you want is…
Thinking this further, this is also performance related. I think there is an interesting relationship between FP and DOD:
A technique of data oriented design is to keep state minimal and lazily derive data when you actually need it, which may involve recomputing things. The rationale is that compressed, normalized data requires less fetching from memory and computation on it is faster.
In contrast caching and buffering, both of which are heavily stateful and require a lot of additional memory, are often necessary, because they minimize inherently slow operations that are out of your control. Those kinds of things are often best implemented as (computational) objects with encapsulated, internal state and small, general interfaces, like OO has taught us.
But once the data in your control, this mindset has to be flipped on its head. You want to model your in-memory data not that differently from how you'd model for databases: Neatly aligned, normalized data, with computed columns, views and queries to get richer answers.
Interestingly if you follow this approach, then code starts to look more similar to functional code, because you potentially need the whole context to derive values from it and a lot less like independent objects that send messages to each other.
Re: Functional languages should be so much better at mutation than they are
#157I recently ran into this issue when trying to memoize a simple numerical sequence in Hoon (yes, that Hoon. I know, I know...). Let's use the fibonacci sequence as an example. Let's write it the classic, elegant way: f(n) = f(n-1) + f(n-2). Gorgeous. It's the sum of the two previous. With the caveat that f(n=0|1) = n. In Python: # fib for basic b's def fib(n): ## Base case if n == 0 or n == 1: return n return fib(n-1)…
Re: Functional languages should be so much better at mutation than they are
#158Earlier quoted context omitted.
Yes, but transformers have a few drawbacks: the order of stacking alters behaviour, and you need to write n^2 instances for n transformers. Compare ExceptT e (StateT m a) and StateT (ExceptT e m a): if you just want your computation to have state and exceptions the difference shouldn’t matter.
> if you just want your computation to have state and exceptions the difference shouldn’t matter But... you don't just want that. You almost certainly care whether state changes are discarded when an exception is thrown. I don't claim that the types there are the most obvious or natural way to specify that, but there is a meaningful difference that shouldn't be handwaved away.
If effect A is invoked before effect B, effect A should happen and stay that way: if I update state before an exception is thrown, the update should persist. If I send a network message before an exception is thrown, the network message is still sent. If I launch the nukes before an exception is thrown, the missiles are still flying.
If I want to batch up state updates to only happen together as a unit, I'll use a transaction effect.
Re: Functional languages should be so much better at mutation than they are
#159> A lot of people think that functional programming is mostly about avoiding mutation at all costs. People should try to stop thinking of mutation as something to be avoided, and start thinking of it as something to be managed. Mutating state is good. That's usually the whole point. What's bad is when you create "accidental state" that goes unmanaged or that requires an infeasible effort to maintain. What you want is…
I like to say that immutability is a really good idea in the 1990s, especially considering how counterculture it would have been at the time. I don't mean that as diminutive or patronizing, I'm serious. It was a good cutting edge idea. However, nobody had any experience with it. Now we do. And I think what that experience generally says is that it's a bit overkill. We can do better. Like Rust. Or possibly linear type…
That's pretty easy to do in eg Haskell or even Rust.
Re: Functional languages should be so much better at mutation than they are
#160> ...functional programming is mostly about avoiding mutation at all costs Slightly different perspective from Grokking Simplicity[1]: functional programming is not about avoiding mutation because "mutation is bad." In fact, mutation is usually the desired result, but care must be taken because mutation depends on when and how many times it's called. So good FP isn't about avoiding impure functions; instead it's abou…
(Of course, if you really need impure functions for eg performance, `unsafePerformIO` has you covered in Haskell.)