by flexagoon on 5/31/24, 11:52 PM with 61 comments
by sparkie on 6/1/24, 12:37 AM
Lost me at this part. Of course reading is a side-effect. A function which reads from a file/console cannot be pure. Purity implies referential transparency - a function given the same arguments will always return the same result.
If we consider the example given:
pub fn read_guess() -> int {
return io.read_int("Take a guess (0-100): ");
}
We should take `read_int` to be effectful because it will have the effect of advancing the position to read from in the console's buffer. If it didn't, it would always read from the same position, so even if the user took a second guess, the first guess would be read again the second time `read_int` is called. So given that `read_int` is effectful, so too is `read_guess`.> 1. Well, creating a variable is creating new state, but it’s not changing the state, therefore creating a variable is not an effect, but changing it is.
Creating a local variable isn't a side-effect, but allocating a variable is a side effect. Sure enough, if we also free the allocation before leaving scope, we can avoid propagating that effect, but if you return a value that contains anything allocated by a function, then the function becomes effectful.
by rebeccaskinner on 6/1/24, 3:09 AM
We've since rewritten the system and, while we still support an optional effects based style in our DSL, it's not being used very heavily. In practice, our user found the ergonomics of the effects system quite challenging, and it introduced some type inference challenges. The biggest problem was that our use-cases ended up with functions that would have hundreds of effects, and it was fairly unwieldy and the type errors were difficult to deal with. Since we've introduced the updated version, most users prefer to user our newer features that allow them to write more traditional code even though it means they don't get composable effects.
On the other side of the experience, I've come to believe that effects systems are a good idea, but when adding them to an existing language it's probably best to make them an opt-in feature that can be used to constrain specific small parts of a program, rather than something that should be applied globally. I also think we need a bit more research into the ergonomics before they are going to appeal to a lot of users. That said, the guarantees and optimization opportunities ours gave us were really nice, and were quite difficult to achieve without building on top of the effects system (our new system is about an order of magnitude more code, for example).
by turnsout on 6/1/24, 1:58 AM
Simply put, what does this buy us?
by taeric on 6/1/24, 6:15 AM
Specifically the part that views effects as growing in size and that being contrary to desired behavior. Strikes me as worrying about similar concerns.
Seems more that it is the eager evaluation of each line of code that is a problem. Nothing wrong with growing the footprint of concern on code. The problem is how to annotate the concern.
Consider, adding 'logger.whatever(...)' is likely not a concern to the program. Being able to annotate the logger as not an effect to check makes sense. Of course, all edge cases matter. Are the arguments eagerly evaluated? What if the logger doesn't even use them, due to level?
With lisp, the magic wasn't only that you can treat code as data, but also that you could define code that ran at different times. And you could largely do that in "user space."
by al2o3cr on 6/1/24, 2:18 AM
Seems like complex functions could end up with effects lists of unwieldy size, similar to how Elm programs end up with a giant "Msg" type.
by librasteve on 6/1/24, 7:07 AM
let x: string = "hello world";
x = 32; #type error
however, a well designed strong typesystem should be able to compose types to allow this if desired by the coder (eg. to read in a column of numbers from a csv) my $forty-two = 42 but 'forty two';
say $forty-two+33; # OUTPUT: «75»
say $forty-two.^name; # OUTPUT: «Int+{<anon|1>}»
say $forty-two.Str; # OUTPUT: «forty two»
Calling ^name shows that the variable is an Int with an anonymous object mixed in. However, that object is of type Str, so the variable, through the mixin, is endowed with a method with that name, which is what we use in the last sentence.by User23 on 6/1/24, 2:43 AM
by name_nick_sex_m on 6/1/24, 5:27 AM