1. 38
  1. 11

    keyword generics, that’s some serious galaxy brain stuff

    1. 7

      I was literally ranting yesterday about how bifurcated the library and framework ecosystems in rust were because of Async. Then I saw this today and was like. Hrmmm. That might help fix it.

      1. 6

        This sounds like an effect type system. It would be limited/controlled, but this could provide a lot of expressiveness for interstitial code.

        For reference, Koka has an effect type system. (It also has algebraic effect handlers which are explicitly not a goal of Rust. See the FAQ.) Koka has a pretty light weight notation. I wonder how Rust will ultimately handle the notation since it’s already pretty dense.

        https://koka-lang.github.io/koka/doc/index.html

        1. 2

          This is the first time I’ve heard of Koka, pretty interesting. As you say, the syntax is very clean, and the effect system is fascinating.

        2. 6

          Here are earlier musings on the problem: https://without.boats/blog/the-problem-of-effects/

          1. 4

            I worry mere mortals (normal programmers) won’t be able to write a library that is both sync and async just due to how confusing it all is. I hope Its not too hard in the end, interesting to see what they come up with.

            1. 2

              Q: Are you building an effect system?

              The short answer is: kind of, but not really. “Effect systems” or “algebraic effect systems” generally have a lot of surface area. A common example of what effects allow you to do is implement your own try/catch mechanism. What we’re working on is intentionally limited to built-in keywords only, and wouldn’t allow you to implement anything like that at all.

              What we do share with effect systems is that we’re integrating modifier keywords more directly into the type system. Modifier keywords like async are often referred to as “effects”, so being able to be conditional over them in composable ways effectively gives us an “effect algebra”. But that’s very different from “generalized effect systems” in other languages.

              I think that this explanation is mostly wrong. “Effect systems” in the Programming Language community have long been used for fixed, runtime-provided effects. For example, early versions of Koka (one of the leading works on effect systems in the 2010) were that way, or the early works on effects in F*, etc. Some works focus on user-defined effects (using either monads or effect handlers), but academic language designers would immediately recognize both kinds (fixed effects or user-defined effects) as “effect systems”, and the answer effectively misses the point.

              1. 2

                Heh, this makes me wonder what an alternative timeline would look like, where Rust went with library-provided green threads (a-la Boost.Context) and “instantiation-time checked” const (where every function can be called in a const context, and would fail compilation if it isn’t actually const).

                1. 2

                  I think this will happen, and I also think it’s necessary, from where Rust is today. I also think it’s likely to put Rust firmly into the Scala bucket. It will live on, but in obscurity, and just generally though of as “too stuffy, too much work, and too hard”. There’s so much encoded in every statement and expression, with every addition being hampered both by the already-mostly-spoken-for ascii charset, and also by the conflicting desires of parsing speed vs “reduce wordy ceremony”, which leads to increasingly ridiculous levels of sigils and source code that is approaching line noise.

                  And I think most of the blame for this unfortunate turn of evens should be laid at the feet of async/await. I don’t think it can be done with enough sugar to be worth the cost in an established language while maintaining the “no expected runtime” promise.

                  Maybe if you start with it in mind you can coax it into something nice, but I suspect not. Maybe if you make functions and “async functions” fundamentally different types, but even then I suspect not, if you’re trying to be as flexible about implementation as Rust is. It might have been possible to do it nicely if it were built in such a way that your choice of async+futures lib was also involved in the compilation of client code, but I’m not sure that’s true.

                  JS was able to add async/await with (comparatively) little pain, because the promises are extremely limited in what they offer and how they work, the requisite runtime support was both already written and could be relied-upon to exist, and the fact the ecosystem was already filled with terrible code built around callback hell against which to contrast it.

                  1. 2

                    I also think it’s likely to put Rust firmly into the Scala bucket. It will live on, but in obscurity,

                    I am willing to bet that this won’t be the case. That is, that even if Rust ramps up complexity to ridiculous levels, it will still end up being used a lot.

                    The difference between Scala and Rust is that Scala is fundamentally a nicer “syntax” for Java, without any interesting innovations into how the application looks to the end user, while Rust delivers something tangible for the end users: security without heavy runtime. That’s the same reason why C++ sails (when the Vasa sank) despite being pretty complicated.

                  2. 2

                    It’s possible that I’m just not thinking pragmatically enough, and for sure my intent is not to cast aspersions on this initiative, but it kind of bums me out? Without a more general abstraction that allows for things like this to be expressed as special cases, it feels like Rust is settling into an unfortunate local minimum of implementing concretions like this individually in a way that feels increasingly unwieldy. To be sure, part of not prematurely optimizing is not prematurely abstracting: it’s unlikely that you’ll create a good abstraction without having a number of example cases to generalize from, and I think the language team was previously correct to hold off on things like higher-kinded types in light of that, and of course it’s not clear how those semantics might interact with the borrow checker. This makes me feel, though, that it might be time to revise that position. The article that kornel links elsewhere in the comments here gestures at the fact that abstractions like monads have an intrinsically steep learning curve, and while I’m not convinced that that’s true, even if it were that doesn’t seem contrary to the position that Rust stakes out: after all, developing an intuition for lifetimes is non-trivial, but it’s mostly a fixed cost that you pay once and reap returns on perpetually in that you no longer have to worry about enforcing safety guarantees manually at each site where they’re relevant because the compiler has your back. It seems to me that something like an effects system would have the same cost profile, and so would be very much in keeping with the language’s ethos. Maybe it’s still better to solve concrete problems like this individually instead of holding out for a general solution, I don’t know, I’m not nearly as informed about language design as the people working on this are, but a part of me still wonders.

                    1. 1

                      Is the a fancy way of avoiding the word “monad”?