Threads for andyferris

  1. 1
    –math-mode=fast disabled

    –math-mode=fast is now a no-op (#41638). <…>

    Maybe it’s better to emit an error instead? You know, don’t make things fail silently.

    1. 2

      The code output shouldn’t become incorrect because of this, so there is no “failure”. A future version of Julia could make the flag emit an error, and by then that wouldn’t have much of an impact on users. The performance may change, which can be true from version-to-version anyway.

    1. 1

      This week I’ve had to dig into exclusive and shared row-level locking in Postgres. It’s been interesting comparing that (in my head at least) to how you get exclusing and shared borrows in Rust. E.g. SELECT * WHERE id=123 FOR SHARE is a bit like getting some kind of shared reference to an element in a hashmap in Rust.

      To fix my particular deadlock issue I used FOR NO KEY UPDATE and FOR KEY SHARE so it could get a better understanding of what data was changing when, and allow concurrent updates. I actually wonder whether Rust (and any other languages that take up the aliasing/concurrency correctness approach advocated in this article) need an equivalent of FOR KEY SHARE, which would be a kind of reference where you can’t read or write the value but you do know that it won’t be dropped before you are done with it (we already have exclusive ownership of a row via FOR UPDATE, exclusive reference of a row via FOR NO KEY UPDATE and shared reference via FOR SHARE). This might allow for a few more additional patterns where we are currently fighting/getting around the borrow checker.

      1. 1

        I actually wonder whether Rust (and […]) need […] a kind of reference where you can’t read or write the value but you do know that it won’t be dropped before you are done with it

        Would it be enough to have a newtype of a shared reference such that the newtype’s owner can’t access the contained reference?

        1. 1

          The idea is that this new 3rd kind of reference would allow for a concurrent mutable reference to be also held. You can’t read the item so it doesn’t matter if it mutates, so long as the item can’t be dropped (think of it as a liveness proof for an object). I don’t think you could do that in safe Rust just by hiding the shared reference away in an empty interface or whatever, right?

          (I suppose at least some cases could be handled by lifetime annotations relating to the thing you want to prove alive - not sure if we’d want to go beyond simple static cases though? Certainly the database example uses runtime locking).

          1. 2

            I suppose the thing I’m talking about is the “tag” reference capability in Pony.

      1. 2

        My guess is that the cloud will become an interchangeable commodity. There will be enough intermediaries between your code and the cloud APIs that you don’t have to care what’s underneath, and can easily use any cloud provider at will. But people have been predicting that for a while, and it hasn’t happened…

        1. 6

          I mean, I might have predicted I could run AAA games on MacOS by now, or Microsoft would distrubute Office for Linux by now, or…

          I suspect the big cloud providers think it’s in their interest to remain incompatible and “sticky”, and though there are driving forces to make things interchangeable they are probably being actively hindered rather than helped (by making decisions of the same flavour as Apple releasing Metal instead of jumping on the Vulkan bandwagon).

        1. 6

          I have two litmus tests for a GUI toolkit, which surprisingly few pass:

          1. Create a table with a million rows. Is it responsive when I scroll through it?
          2. Create two non-rectangular shapes and flow some text loaded from a file between them. Can you do this win under 100 lines of code? As a bonus, can I also change the line breaking or hyphenation rules for the text? Can I change the kerning and ligature rules for the first paragraph, but not the second.

          The first tells me whether the authors have considered scalability or whether I’m going to hit some performance cliff later. The second tells me whether the text system is flexible enough for anything I could want to do. If not, then at some point I am going to hit a wall and need to deal with two layout engines interacting badly.

          1. 2

            Which ones do you find work well for both of these?

            1. 1

              Cocoa does, GNUstep does for the first, mostly works on the second but you hit a bunch of bugs. Most other things I’ve tried have failed.

          1. 7

            There is no standard way to iterate over a sequence of values in Go.

            Go was publicly announced in November 2009, and version 1.0 was released in March 2012.

            I can’t help but feel like something went badly wrong here but what do I know.

            1. 20

              These types of comments really drive me up a wall. It feels like what you are saying is “this is a common feature in other languages, the people behind Go (vague bad thing) since they didn’t add the feature too” which is just not sound reasoning.

              In order to form a judgement about how bad something is, we should consider the consequences of it. The “normalness” of a behavior is an OK pointer, but thats all it is.

              Maybe you can argue that the consequences have been grave and thus this is a grave failure, but that doesn’t seem true to me.

              1. 26

                I can’t argue that any of the things about Go’s design that people have been unproductively grouchy about in comments sections for the past decade have had grave consequences for any given Go adopter or for the widespread adoption of Go. Having written one short program in Go for my own purposes, the lack of a proper idiomatic iteration construct (no map, no iterating for loop, no list comprehension, no yielding to a block, just the apparently-hacky for-range) was flummoxing. Go’s designers are entitled to their priorities but idk I feel like I’m entitled to make fun of those priorities a little bit, especially because they get paid more than I do.

                1. 14

                  IMO there is a solid iteration idiom, and they hit on it early in the stdlib, although the fact that they managed to do it several different ways afterwards is a disappointment. It’s the

                  for iter.Next() {
                      item := iter.Val()
                      // ...
                  }
                  

                  one. You can do pretty much anything with it, it doesn’t impose any burden on the caller, it doesn’t make the caller ugly, and you can implement it on top of pretty much anything else. With generics you could even codify it as an interface (parameterized on the return type of Val).

                  None of which is to say that I opposite this proposal — it looks pretty nice to me. But in 7+ years of writing Go professionally, the lack of an iterator syntax hasn’t been a major thorn in my side — or even a substantial annoyance.

              2. 7

                I’m pretty sure Zig has no concept of iterators either

                https://ziglang.org/documentation/master/

                I saw in the latest blog that the Zig for loop is being changed, but AFAIK there will still be no iterators. You basically do what you do in Go – write your own set of methods on a struct.

                So it seems like Zig will be a ~2025 language without iterators

                I would say languages have different priorities, and it’s harder than you think. Or you could just make vague statements about people doing bad things for no reason

                (edit: this bug seems to confirm what I thought https://github.com/ziglang/zig/issues/6185)

                1. 3

                  Zig deliberately has no interfaces or traits whatsoever. You put the methods in the struct and they get called and it will compile if and only if the types work out after comptime propagation. I might be wrong but as far as I understand “iterators” in the language will be a bit of syntax sugar relying on a documented (but informal) interface, and Zig will very much have iterators exactly like, say, Julia or JavaScript or Python have iterators (except those languages check if things work out at runtime instead of compile time).

                  On the other hand the major selling point of Go is having interfaces enforced by the compiler. But a fast iteration interface needs to be a generic interface so that wasn’t really possible until recently…

                  Hopefully it all works out on both fronts.

                  1. 4

                    Eh I don’t see what you’re saying. My point is that, as of now, Go and Zig are the same as far as iterators.

                    As of ~2025, Go might have iterators, and Zig probably won’t. Go is thinking about adding some concept of iterators to the language to enforce consistency.

                    Python’s for loop and list comprehensions understand iterators; I don’t think the same is true of Zig.

                    If you know otherwise, please provide a link to the docs.

                2. 4

                  The dominating quality of Go’s development over the past decade has been the most extreme caution when it came to adding features. You can’t have performant, extensible iteration without some kind of generics and they were stuck in place on that issue out of fear of C++ compile times until finally a couple of years ago.

                  1. 8

                    You can’t have performant, extensible iteration without some kind of generics

                    It’s even stronger than that: if you do want to map’n’filter, you need a boatload of machinery inside the compiler to make that fast, in addition to significant amount of machinery to make it expressible at all.

                    Rust’s signature for map is roughly

                    trait Iterator {
                      type Item;
                    
                      fn map<T, F>(self, f: F) -> Map<Self, F> 
                      where
                        F: FnMut(Self::Item) -> B;
                    }
                    

                    That is, .map returns a struct, called Map, which is parameterized by the type of the original iterator Self, as well as the type of unnameable closure F. Meditating on this single example for a long time explains half of why Rust looks the way it does.

                  2. 3

                    Go’s developers focused on higher priority concerns, such as pretty great performance, a pretty great (though basic) type system, an awesome runtime and compilation model, and fantastic tooling. Go’s feature set (including the features it elided) made developers really, really productive compared with other languages.

                    While there are a few use cases that weren’t feasible without generics, the absence of generics made for some really interesting and compelling properties–like “everyone writes their code the same way (and thus any developer can jump into any other project and be immediately productive)” and “code is very concrete; people don’t usually try to make things overly abstract” which aren’t present in other languages. It wasn’t actually as obvious that generics were the right choice as Go’s critics claim (whose analyses flatly pretended as though there were no disadvantages to generics).

                    The net upside to generics (including iterators) was relatively small, so it makes sense that the decision was deferred.

                    1. 4

                      Go is a Google language. If a proposal helps or is of benefit to Google, it’ll be added. If it’s bad for Google, it will be ignored. If it’s neutral, then the only concern Google has is how well does it externalize training costs for Google.

                      1. 10

                        Google doesn’t really figure in at this level of discussion. The Plan 9 guys who made Go are the relevant actors for this. They were skeptical of generics, so it wasn’t a priority for Go 1.0. With no generics, a generic iterator protocol doesn’t make any sense, so that wasn’t in Go 1.0 either. Now Go has generics as of Feb. 2022, so there is a discussion about the best way to do an iterator protocol. This is the second proposal, which builds off of ideas from the first discussion and some ideas that had been in the issues tracker before that. It’s not really more complicated than that.

                        1. 4

                          You’re obviously right that the decision making an process is entirely about Google’s desires, but I’d hesitate to assume that it’s necessarily utilitarian. Google does a lot of self-sabotage.

                        2. 1

                          There is no standard way to iterate over a sequence of values in Standard ML, which is from circa 1970s/80s depending on who you ask, and is widely considered one of the most elegant of language designs. Something went badly wrong here or…?

                          1. 1

                            After having to deal with Rust iteration for a bit and missing out on Python…. I think the decent explanation here is that in more dynamic languages with stuff like coroutines it’s pretty easy to come up with a nice iterator protocol, but in more serious things it’s harder to come up with one that is both flexible enough for the “right” use cases without being very hard to use.

                            Like C++ has iterators right? And they do the job but they’re kind of miserable to use (or at least were 5+ years back, I’m sure things are better now).

                            Combine that with the perrenial generics things meaning container classes aren’t a thing and “stuff things into arrays and use indices” feels like a pretty OK solution for a long time.

                            1. 2

                              I think C++ iterators are uniquely awkward in their design, and it’s not an inherent design problem or any sort of static typing limitation.

                              C++ iterators are based around emulating pointer arithmetic with operator overloading, with a state awkwardly split between two objects. There’s no reason to do it this way other than homage to C and a former lack of for loop syntax sugar.

                              And C++ iterators aren’t merely tasked with iterating over a set once from start to finish, but double as a general-purpose description of a collection, which needlessly makes both roles harder.

                              1. 2

                                There’s no reason to do it this way other than homage to C and a former lack of for loop syntax sugar.

                                I think this is a little unfair, the primary reason to do it this way is so that code, especially templates work on pointers or iterators, eg being able to have a single implementation for something like std::find work for list or pointers. It’s not a “homage” so much as a source level interoperability consideration.

                                1. 1

                                  OK, “homage” is a poor way of phrasing it. But it’s still an “interoperability consideration” with pointer arithmetic and C’s way of doing things, rather than a ground-up iterator design. The messy end result is not because iteration is such a hard problem, but because preserving C legacy is messy.

                                  1. 1

                                    Right it’s not inherent to “designing iterators in statically typed language.” Go doesn’t have a different language it’s trying to be incrementally adoptable from.

                            2. -6

                              but what do I know.

                              Not much.

                            1. 4

                              I’d like to see offset integers where they sort naturally in byte representation. So 00000000 is -128 and it goes up sequentially from there (01111111 is -1, 10000000 is 0, 10000001 is 1). It involves just flipping the first bit.

                              You can attempt similar transformations on (non-subnormal) floats. I have played with an Avro-esque data format where you can compare (eg sort into order) data directly via memcmp without decoding or understanding the semantics of the data format. I still haven’t decided whether this is an awesome or terrible idea…

                              1. 1

                                Data structures for ordered lookups can be a lot more efficient when they are specialized to dumb lexicographic order instead of some arbitrary ordering function. Radix trees and my qp-trie depend on lexicographic ordering to give meaningfully ordered lookups.

                              1. 4

                                Why is there all this excitement about wasm? I assume I’m missing something.

                                1. 23

                                  Here’s how I see it.

                                  WebAssembly is a simple thing: it’s a bytecode, which is independent of a particular language and particular runtime. The idea of the bytecode is, of course, not new, but this particular implementation is quite good.

                                  Then, we have a few increasingly important use cases for good bytecode:

                                  • As the web becomes more and more of a full operating system, it becomes important to run CPU heavy computations in the browser.
                                  • Edge compute is the current trendy thing, and it too needs some sort of bytecode to run on the said edge
                                  • And of course various blockchain-shaped things need a bytecode for smartcontracts.
                                  • Finally, while not new or recent, there’s a background humming of various plugin architectures, where, ideally, you need some abstract way to specify computation as data (bytecode!) to plug into the larger application.

                                  So, there’s a coincidence of various wants and a well-funded and well-designed technology which covers those wants.

                                  As maybe an additional factor, WebAssembly doesn’t have GC (bc it’s hard to do GC without compromising on language/runtime independence and performance), and we have some interesting recent GC-less languages, which make targeting Wasm-shaped bytecode easier.


                                  Finally, while this is more in the future of Wasm at this point, I get a feeling that the holy grail here is a component-based software architecture, where you get re-usable components without getting vendor locked into one particular ecosystem. So, COM, but done right.

                                  Not sure if that’ll fly (there were quite a few iterations on the relevant module linking proposal already), it looks like it might, given the high quality of implementation of what’s already there.

                                  1. 9

                                    WASI is also interesting in that it allows compiled programs (ie. Rust, C, etc.) to target WASM + WASI ABIs and effectively become JVM-like semi-interpreted languages. While I think server-side WASM is mostly silly, the ability to have shared language + modules in backend and frontend without having to use Node/JS is nice.

                                    1. 6

                                      I’ve heard it has nice sandboxing properties that are convenient for security on server

                                      1. 4

                                        I would really like to see a unikernel WASM runtime. No OS, filesystem, etc. Just networking stack and object storage.

                                        1. 3

                                          There were things like Cervus and Nebulet.

                                          1. 3

                                            I’d like to see something on the other side of the spectrum - a microkernel WASM system. Just a bare WASM runtime, scheduler, and memory manager, with everything else built on top as WASM modules.

                                            Not for any practical purpose, just for fun. Though it would be cool to have a system that actually seriously abstracts away the hardware almost entirely (including CPU architecture)

                                            1. 2

                                              You are looking for redshirt. Everything under programs, like e1000 device driver, are WASM modules.

                                          2. 2

                                            Yes - in particular, it omits a bunch of the insecure-by-default APIs you find in things like the Java and Lua stdlibs.

                                          3. 4

                                            I think server-side WASM will be non-silly the same place the JVM is useful for letting users send servers .jar files and asking them to execute them (things like Apache Spark and other things where users provide the logic to be run in a larger system, where the logic has to be executed fast yet safely). Similarly, desktop apps can allow for plugins and run them in a WASM sandbox. You can dynamically create and execute WASM on an iPhone or iPad (unlike native code - App store doesn’t allow native compilation). Etc.

                                            1. 2

                                              Yeah, that’s definitely a major use-case for WASM + WASI. I think WASM as a target for FaaS and microVM-based services will be an interesting topic too. Theoretically stronger sandboxing than traditional virtualization and containerization, so maybe we’ll see it start to supplant part of those markets.

                                          4. 3

                                            Well, sounds like for most of the benefits you listed (besides browser applications) proven, established solutions are already available (e.g. ECMA-335), featuring even the parts still missing in WASM today (like the mentioned GC). Ironically for the original WASM use case (browser applications) the progress seems to be very slow, and people recently seem more interested to use WASM in non-brower applications.

                                            1. 8

                                              I think quality of implementation really matters. While CLI and WebAssembly try to solve the same problem on the first glance, closer look at then reveals quite significant technical differences, which I think are enough to explain the fact that CLI didn’t fill WebAssembly niche despite having a considerable headstart.

                                              I have only a passing familiarity with CLI, but there’s quite a few things which jump at me as liabilities:

                                              • Built-in OOP
                                              • names, overloads, coercions (Wasm has just mechanically-sympathetic indexes)
                                              • unstructured control flow
                                              • no dual text/binary view into code
                                              • a boat load of already existing implementations of WebAssembly vs a relatively few for CLI

                                              It sort of like x86 vs RISCV — definitely, x86 has been around for longer, is deployed wider, and sort of works. But there’s clearly a design space for something more MIT and less New Jersey, and maybe even a market niche.

                                              1. 2

                                                Built-in OOP, names, overloads, coercions

                                                No need to use it; you can perfectly well generate bytecode which doesn’t use the dotnet framework nor OOP features. You can even do pointer arithmetics if you really want (a WASM index by the end of the day is just a pointer into the memory array).

                                                unstructured control flow

                                                Not sure whether this is supposed to be an advantage or disadvantage; in any case you can generate CIL code for both structured and unstructured control flow, and there is a verifier for the applications which want to be verifiable

                                                no dual text/binary view into code

                                                ILASM? Writing CLI assemblies in IL assembler is well possible; there are even several books about it

                                                vs a relatively few for CLI

                                                Well, there is the Mono and CoreCLR VM; Mono 5 is more than enough for all of my purposes; I’m looking forward to using the WASM micro runtime as soon as WASM has reached an essential feature set (e.g. GC and FFI).

                                                1. 6

                                                  No need to use it

                                                  The runtime still needs to implement it. That takes work, and requires design tradeoffs.

                                                  1. 1

                                                    Even if you don’t use it, there are still plenty of users who want, so it’s worthwhile the spend the design effort; it’s already spent anyway. With my Oberon+ compiler (see https://github.com/rochus-keller/Oberon/) I e.g. only use a fraction of the mscorlib.dll and the Mono binary which together are less than 10 MB, but nothing from the non-core .NET framework; and I can do everything I need including FFI and GC; none of the WASM engines today would support all required features to do an efficient implementations, even though my requirements are very minimal.

                                                2. 1

                                                  This boils down to “WebAssembly is simpler”; that’s not quality of implementation. In fact, CLI has better quality of implementation, like having a GC integration. C and C++ compile to CLI well, built-in OOP is not a problem in practice.

                                                  Edit: CLI has standard text form (it is specified in ECMA-335, together with binary form), so you are completely mistaken about text/binary issue.

                                                  1. 4

                                                    It seems to me that simplicity is the main QoI of an intermediate language itself. Number of different implementations is probably the definitive metrics for measuring success of “protocols”, and simplicity is a significant contribution to that.

                                                    CLI is probably better at “the thing you can compile C++ to”, but clearly worse at serving as an common intermediate infrastructure between independent actors.

                                                    1. 1

                                                      I argue that CLI is “clearly worse” at serving independent actors because independent actors refused. Even if it got a well done international standard. It is mostly politically worse, not technically worse.

                                                      CLI had interesting implementations like JSIL. If you are thinking CLI had only two implementations, .NET and Mono, because it is so complex, you are mistaken. In fact CLI is of quite reasonable size (and WebAssembly, currently, is a bit too small), and I think number of implementations mostly reflects popularity, not simplicity.

                                                      1. 2

                                                        If you are thinking CLI had only two implementations, .NET and Mono, because it is so complex, you are mistaken.

                                                        Thanks, this would be an important update for me! What would be the best thing read to understand what CLI is? A the moment, I indeed think that it is way to complex, but that ‘s base on background info and skimming the standard’s toc, not the actual knowledge.

                                                        I guess the answer might be “skim the standard”?

                                                    2. 2

                                                      Edit: CLI has standard text form

                                                      Hm, does it? Reading from VI.Annex CCIL assembler implementation

                                                      This clause provides information about a particular assembler for CIL, called ilasm. It supports a superset of the syntax defined normatively in Partition II, and provides a concrete syntax for the CIL instructions specified in Partition III.

                                                      Which sounds to me like an implementation defined, non-canonical encoding of a particular assembler. This seems close, but also importantly different from WASM, which specifies implementation independent text format, together with required syntactic sugar which makes hand-writing Wasm quite OK.

                                                      Again, this is a difference in degree/quality, both technologies has text format, but Wasm seems way more upfront about code being actually human-readable and human editable.

                                                      1. 2

                                                        Hm, does it? Reading from VI.Annex CCIL assembler implementation

                                                        All specifications and examples in the standard use the text form of IL.

                                                        1. 2

                                                          Yes it does. Partition II is standard, and it defines text form with BNF and all. Annex C is not, but there is no non-canonicality going on. Standard binary form can be canonically converted to standard text form.

                                                          In fact, this is exactly the same situation with WebAssembly. WAT is standard and WAST is not. See WAST vs WAT for an explanation.

                                                          1. 1

                                                            Hm, I am still not seeing it. Partition II delegates textual encoding of instructions to Partition VI (annex), see the table in II.15.4.1 Method body.

                                                            The situation in Wasm seems different, WAT (not WAST) is the thing that defines helpful abbreviations and symbolic $names (see, eg, abbreviations section from https://webassembly.github.io/spec/core/text/modules.html#text-func). WAST I think is just minor extensions to specify test directives (basically, a bunch of asserts at the top-level). That is, it doesn’t seem that the linked page entirely correctly explains what WAT is.

                                                            Quoting the spec,

                                                            Except for a few exceptions, the core of the text grammar closely mirrors the grammar of the abstract syntax. However, it also defines a number of abbreviations that are “syntactic sugar” over the core syntax.

                                                            Syntactic sugar for hand-authoring is specified.

                                                    3. 8

                                                      Note for people as ignorant as me, ECMA 335 refers to the Common Language Infrastructure (CLI), discussed downthread.

                                                  2. 6

                                                    My take is that it’s because it is (IIUC) an open standard of a bytecode that is not controlled by a single major player, while at the same time many major players have a serious stake in it. As such, people and companies feel relatively safe from being captured by a monopolist when using it, while also at the same time relatively safe that it won’t be abandoned soon, so an investment in it (money, time) won’t suddenly become wasted resources. Due to its evolution path, it was also immediately useful from the beginning and compatible with a huge target platform (“Da Web”) with a relatively clear path to quick performance improvements with low opportunity cost for browser makers (basically, “make some corner-case areas of JS run faster is still mostly a win for us even if this newfangled wasm thing shows up to be a fad”), which gave it big credibility and a promising future from the start (at least that’s what I thought when I first learned about asm.js).

                                                    1. 4

                                                      Because it gets us away from using JS, plain and simple.

                                                      1. 2

                                                        Good question; the most excitement at the moment seems even to be in non-browser or even embedded applications, where there are already good established solutions; even more since there is no GC (yet) one can fairly ask why one should compile a non-VM language to WASM and not directly to machine code. Personally I still prefer LuaJIT for dynamic (and even some statically typed) languages, and CIL/Mono for statically typed VM languages. The WASM micro runtime looks interesting though and I’ll keep an eye on it.

                                                        1. 5

                                                          WebAssembly is sandboxed, while machine code is not. WebAssembly is a good way to provide SFI(Software Fault Isolation), see RLBox for example.

                                                          1. 1

                                                            sandboxed

                                                            CIL/ECMA-335 or the JVM is also “sandboxed”; but why anyway should there be a benefit for a sandbox when you run code on your own desktop, server or embedded device? For browser apps run from anywhere on the web sandboxing is essential, but for the others mentioned?

                                                            1. 2

                                                              Software fault isolation is useful for the same reason process isolation is useful. For example, editor may want to recover from crash of plugin. In addition to RLBox, have a look at How to build a plugin system on the web and also sleep well at night by Figma.

                                                              I agree C++/CLI is great and browsers should have embedded Mono and C++ libraries should have been compiled with C++/CLI instead of reinventing the wheel that is WebAssembly. As you pointed out, C++/CLI already solved GC integration! Alas, browser vendors disagreed, and we are powerless to stop them. (One concrete problem is that as far as I know there is no open source implementation of C++/CLI, but if this superior technical choice was taken, it would have been easy enough to add support to GCC and Clang.)

                                                              1. 1

                                                                Ok, I see. But even without CLI or WASM you can isolate plugins today (if you really think it’s worthwhile) by running them in a separate process; in contrast to a WASM based solution this even works for all kinds of applications and languages; and if you want to run non-trustable applications you can use virtualization.

                                                                1. 1

                                                                  It boils down to “function call is easier and simpler than IPC”.

                                                                  1. 1

                                                                    Well, then apparently something like CIL is preferable; but even without CIL, there is still CORBA or similar technologies (e.g. Protocol Buffers, or even web services, i.e. all kinds of technologies where you can generate stub functions in your host language from given IDL specifications).

                                                                    WASM would be a great alternative to CIL for VM languages/applications which want to do without managed types, but it still has a long way to go before it is sufficiently useable.

                                                          2. 2

                                                            Portable binaries seem like another reason to prefer it to machine code

                                                            1. 1

                                                              There are plenty of existing, proven solutions for exactly this purpose. WASM doesn’t bring anything to the table which we didn’t have already. It’s just yet another intermediate representation.

                                                              1. 2

                                                                It certainly brings something new to the table: it runs in a web browser. Web browsers are great, because they are completely ubiquitous, and are able to fetch resources on-demand. They also allow you to deploy your server-side code whenever you want, so you can update your application and with one page refresh your users have the up to date version.

                                                                Can you name another binary format / intermediate representation that has that capability?

                                                                1. 7

                                                                  Can you name another binary format / intermediate representation that has that capability?

                                                                  Java applets.

                                                                  I think Webassembly has plenty of technical advantages; Java applets have a very different execution model that is much more naive. But let’s not fool ourselves into thinking this has never been done before.

                                                                  1. 4

                                                                    Yeah, it feels like wasm was made by the browser people as “let’s make a replacement for Java applets that is better-behaved for our use cases” - no expansive stdlib, the program only gets the interfaces that the client allows for.

                                                                  2. 1

                                                                    Sure, but the argument was because of the current excitment for non-browser WASM applications.

                                                                  3. 1

                                                                    One way to think about WASM is that it’s basically Java, but efficient enough to actually be usable.

                                                                    1. 2

                                                                      That’s a strange way to look at it. Java is amazingly efficient; no language prior to it was so incredicbly effective at trading off RAM usage to achieve better CPU throughput.

                                                                      I’d suggest a different take than yours, having implemented a JVM, a Java compiler, some prototype WASM compilers, etc. (i.e. barely knowledgeable enough to believe my own B.S.), and here it is:

                                                                      WASM is just like Java, without any language with it, and without dragging any language assumptions with it.

                                                                      Java byte code may be claimed to be language agnostic, but it is not. It is designed to support exactly one language: Java. You can compile other languages to Java byte code, but in doing so, those languages all become Java, because the Java language assumptions (including the Java type system, such as java/lang/String and java/lang/Object and java/lang/Exception and so on) fully permeate the design of Java byte code.

                                                                      WASM is more like “whatever … there’s no such thing as a type”.

                                                                      1. 1

                                                                        Java is amazingly efficient; no language prior to it was so incredicbly effective at trading off RAM usage to achieve better CPU throughput.

                                                                        That’s fine, but that’s rarely something I consider when evaluating efficiency. I can’t actually remember the last time I had a nontrivial system bottlenecked by CPU before it was bottlenecked by memory.

                                                                        It’s all a bit moot, though. Current browser-oriented Wasm use cases are more or less equivalent to the stuff that Java Applets etc. were meant to solve. The difference is the overhead, and that difference dominates the cost calculus.

                                                                        1. 1

                                                                          That’s fine, but that’s rarely something I consider when evaluating efficiency.

                                                                          I’m going to have to start adding the “/s” tag when I write stuff like that, I guess. I thought that my sarcasm was pretty obvious, but apparently my sense of humor is only appreciated inside of my own skull.

                                                                          1. 1

                                                                            Wow! I have the dryest sense of humor among everyone I’ve ever met, by a wide margin, and I didn’t catch this at all. Well done? 😉

                                                              2. 1

                                                                Because web browsers have support for it out-of-the-box, and it enables writing webapps in rust/c++/other languages, instead of JavaScript like it’s done now.

                                                              1. 3

                                                                Racket has an “equal-always?” primitive that equates values that will stay the same even if mutated.

                                                                This is so fuckin cool. One of those functions that makes you think “huh, why hasn’t anyone else done this before?”

                                                                  1. 2

                                                                    Not to mention https://p.hagelb.org/equal-rights-for-functional-objects

                                                                    It’s even cooler when it’s the default equality predicate in your language rather than one of seven or twelve or whatever.

                                                                  2. 2

                                                                    Pretty sure Julia’s === has this property.

                                                                  1. 16

                                                                    Having scoped threads in std is great. First language, AFAIK, with this kind of structured concurrency in it’s stdlib. Definitely the first that also statically prevents data races.

                                                                    1. 3

                                                                      Yeah I agree!

                                                                      I was wondering - what remains missing from “structured concurrency”? Just looking again at [1] I can see cancellation, error propagation and being able to create and pass around and inject the scope object s elsewhere (for spawning background tasks that outlive whatever calls s.spawn) as being things that I can’t immediately see as being possible or not. Does anyone know - would these already be possible with what’s there now?

                                                                      [1] https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful

                                                                      1. 3

                                                                        Error propagation is already possible (as (Scoped)JoinHandle<T>::join() returns whatever was returned by the scoped/spawned routine).

                                                                        1. 1

                                                                          Lifetimes stop you from exfiltrating the scope handle outside the callback, yes. You can’t mem::swap it somewhere because the destination would need to have the exact same lifetime (and therefore, created from inside the callback).

                                                                      1. 2

                                                                        Finally!

                                                                        Although its strange ive adapted quite well to shallow cloning w/o issues for a long time.

                                                                        Makes me question the real need for immutable data structures in day to day front end.

                                                                        On a tangent ive wondered if the problem of shared mutable state could be solved by build tools, like if a rust like mechanism were just a linter for JavaScript. Going further you could hover a variable & see all the read & write references with a warning for multiple writers or some more useful rules. Some nuances are required to do this correctly but for me this is more desirable than putting it in the language either at runtime (immutable data structures) or compile time feature like lifetimes, at least for front end where I value a rapid dev cycle.

                                                                        1. 2

                                                                          As luck would have me, I just had a shallow clone bug shortly after writing this :)

                                                                          1. 2

                                                                            Yes, I that could be helpful, TypeScript already has the readonly modifier. I found it frustrating to use though - I think as it is, there’s a tension between being guaranteed that this thing won’t change under me vs the guarantee that I’m able to mutate it. Not sure if the both caller and callee need to annotate appropriately or something (which is basically true in Rust) or whether this can be solved more ergonomically?

                                                                            1. 1

                                                                              Yea I don’t want to enforce readonly necessarily, I just want to know when its happening.

                                                                              For example in a language like svelte it would be nice how many times my variable is 2-way bound using bind:. Ideally this is only on form elements directly instead of custom components. Having powerful linting rules/warnings like that wouldn’t get in the way of local development but should still prevent difficult to debug bugs in production.

                                                                            2. 1

                                                                              How does the presence of immutable data structures adversely affect a rapid dev cycle?

                                                                              1. 1

                                                                                I was more referencing putting something like that in the compiler. I don’t want my hot reloading to stop working because some rule was violated - typescript bothers me enough with this.

                                                                                In terms of immutable data structures at runtime like Immutable.js, it just depends on the environment you’re in. ClojureScript or Elm obviously its a benefit if you buy into the language & ecosystem. But in plain js I find its hard to inspect & often needs to be converted to regular js & back. On top of that I really deeply value plain javascript syntax of spreading & destructing, so for me it slows things down, but maybe that’s not true for others.

                                                                              2. 1

                                                                                I come from ClojureScript where native data structures are immutable. It makes deep clone completely superfluous.

                                                                              1. 7

                                                                                Sexxxy features:

                                                                                • A performant systems programming language that feels like a scripting language.
                                                                                • Aims to combine the expressiveness of Scheme with the convenience of Python, Lua or Javascript and the performance of C. …
                                                                                • Compile-time resource management using view propagation, an annotation-free variation of borrow checking.
                                                                                • Fully interoperable with C libraries. Import and use C include files directly. …
                                                                                • Statically typed but fully inferred type system via forward propagation. Supports closures as zero-cost abstraction.

                                                                                Very tempted to download and explore … in other words, running a Scopes Trial 🙈

                                                                                1. 6

                                                                                  Boooooo and/or bravo.

                                                                                  1. 2

                                                                                    Scopes Trial

                                                                                    TIL

                                                                                  1. 3

                                                                                    “An” SPA implies the author doesn’t say spa but es-pee-ay. I was not aware some folks treated the word not as an acronym but as an initialism.

                                                                                    1. 39

                                                                                      Never in my life have i thought about pronouncing SPA as a spa instead of as an acronym.

                                                                                      1. 5

                                                                                        Exactly. How would you verbally compare and contrast an MPA vs an SPA in the same sentence? Spas and em-pee-aye’s? Spas and m-pas? It doesn’t make sense!

                                                                                        1. 7

                                                                                          Never in my life have I used the term MPA. :-)

                                                                                          I think I would call the opposite (server side) template rendered sites.

                                                                                          1. 4

                                                                                            Mpa pronunciation should be something like m̩ːpɑː.

                                                                                        2. 4

                                                                                          I typically pronounce it as an initialism, but I have to admit when I wrote that title it looked funny and I switched back and forth a bunch of times. I eventually just went with the way I would say it in conversation.

                                                                                          1. 1

                                                                                            In cases like that, rewrite: The SPA Alternative. It also makes your thing sound like it’s the one true solution.

                                                                                              1. 1

                                                                                                Okay fine, “An Alternative to SPA”. But you need to work on your marketing.

                                                                                        1. 15

                                                                                          As far as I can tell, this doesn’t actually refute the central point of the post it’s disagreeing with (that Julia has correctness problems). It seems to basically be listing a bunch of cool Julia features, which is nice, but not if they don’t produce correct results?

                                                                                          1. 9

                                                                                            As someone who isn’t a Julia programmer, the biggest omission in the original post and the thing that makes it hard to respond to, is the lack of an alternative. If the author doesn’t recommend Julia anymore, what do they recommend? As I see it, you have two high-level choices for numerical computation:

                                                                                            • Write everything from scratch.
                                                                                            • Rely on an existing ecosystem.

                                                                                            Option 1 is a terrible idea for anything non-trivial. If I tried to implement a statistics package then I can guarantee, with 100% certainty, that it would contain more bugs than an off-the-shelf Julia one, for example. If you eliminate option 1, then you have a choice between a fairly small number of ecosystems and most of the criticisms from the article seem to be applicable to most of them. The concrete example of @inbounds is interesting, but it gives the same behaviour as unannotated C/C++ and most of the Python and other scripting-language alternatives rely on C/C++/Fortran implementations for their hot paths. If Julia ignored @inbounds in debug builds (does it already?) then I’d assume that most of these cases would be caught: you’d hit them during development and if you don’t hit them then the same shape of data in production won’t trigger them.

                                                                                            1. 4

                                                                                              Yeah basically for a lot problems your choices are Matlab, Python, C/C++, or Fortran … from that perspective Julia looks very good. It’s faster than the first 2 and higher level than the latter 2.

                                                                                              But I’d say Julia is a pretty specialized language; some people have been trying to use it as a general purpose language.

                                                                                              1. 1

                                                                                                Yes, @inbounds is always ignored during unit testing, and whenever you pass the command-line flag to do so (e.g. which you might do for integration testing or even deploying “correctness critical code”)

                                                                                              2. 3

                                                                                                One of the issues that Yuri brought up in his original post was concerning incorrect computations when composing multiple types from different repositories, in this case, StatsBase.jl code that was quite old. There were some legit bugs W.R.T bounds handling that have been addressed since. See: https://discourse.julialang.org/t/discussion-on-why-i-no-longer-recommend-julia-by-yuri-vishnevsky/81151/14

                                                                                              1. 4

                                                                                                rope.go in the repository talks about persistence, but it sounds more like immutability — new, modified instance is returned for every change. Is the use of word persistence correct here, I thought it implies being written to disk or some storage?

                                                                                                1. 3

                                                                                                  https://en.wikipedia.org/wiki/Persistent_data_structure

                                                                                                  In this case, “persistence” means “the old versions stick around after modification as long as you need them to.”

                                                                                                  Among other reasons, it’s a nice property because it makes undo/redo trivial.

                                                                                                  1. 2

                                                                                                    Yeah unfortunately it seems the term is overloaded, meaning either immutable data structures or persisted-on-disk (or equivalent) data structures, depending on the author.

                                                                                                  1. 3

                                                                                                    I can only speculate that the purpose of this proposal is to make further TypeScript-ish inroads into existing JavaScript codebases for the benefit of tools like VS Code.

                                                                                                    1. 3

                                                                                                      one of the authors commenting in HN made it sound like the ability to past ts into console was at least a motivating factor, which is really not very compelling

                                                                                                      1. 2

                                                                                                        Perhaps not for you, but it is very compelling for someone like me who works in TypeScript codebases (and enjoys it!) and would also like to be able to use the JavaScript console as a REPL for interactive development.

                                                                                                        1. 4

                                                                                                          That seems like a reason for the TS folk to make a repl, not for JS engines to take the burden of parsing type annotations without providing any of the benefits (and worse burning the syntax so that they could never enforce types with that syntax in future)

                                                                                                          1. 1

                                                                                                            There is already the deno REPL but I think they would also like to use the browser console, which sounds useful to me.

                                                                                                            1. 3

                                                                                                              There is also ts-node, so no need to leap to Deno - though Deno’s CLI is a little faster, as AFAIK it doesn’t do type checking

                                                                                                    1. 14

                                                                                                      The tldr is that it doesn’t support the full TS syntax (so no skipping compilation) and it mandates that engines not enforcing the types (thus burning the syntax for any actual type safety in future)

                                                                                                      1. 3

                                                                                                        The organizing principle of web APIs is, “Don’t break the web.” The runtime type safety ship sailed for JavaScript the moment ECMA-262 was adopted. The only way to maintain backward compatibility for existing codebases is to define some other language browsers are expected to support:

                                                                                                        <script lang="someotherlangauge">...</script>
                                                                                                        

                                                                                                        Given the large surface area of the DOM and the likely complexity and performance overhead of running two scripting language engines in parallel, that seems unlikely.

                                                                                                        1. 3

                                                                                                          Adding type syntax to JS would not break the web, but if the syntax does not require enforcement (or in tis case mandates non-enforcement), then engines would not be able to enforce type safety in future because that would break content that previously worked.

                                                                                                          1. 3

                                                                                                            There are pretty easy ways around this if TC39 wanted to allow enforcing types in the future. The most obvious would be to create a "use types"; pragma similar to the existing "use strict";

                                                                                                            Google even experimented with something similar in strong mode a few years ago.

                                                                                                            1. 4

                                                                                                              Use strict had a phenomenal cost to the spec complexity, and required a significant amount of complexity in the implementation of basic semantics of JS.

                                                                                                              The only reason I accepted strict mode in JS is because of the removal of |this| parameter coercion, nothing else in strict mode warranted the semantic overhead of the mode switch.

                                                                                                              Adding another mode is not a think that is ever going to happen in JS.

                                                                                                              The solution to opting in to types in JS, is to add an optional syntax that allows type constraints and enforce it.

                                                                                                              This proposal adds the some of the syntax, doesn’t really specifies details, and also prohibits the engines from enforcing those constraints, which will burn the syntax.

                                                                                                              So you get a large increase in complexity, but no actual benefit.

                                                                                                              I need to be very clear here: the solution to optional/progressive typing in JS is to actually specify the constraint syntax and semantics, and require the engines enforce those constraints. I would be 100% on board with that. What this proposal does is half-assed.

                                                                                                              In a HN comment one of the authors said a motivating factor was pasting typescript annotated types into the console, and said minifiers would strip out the annotation. The first could be trivially solved by console level rules, the latter completely defeats the argument for adding this proposal to the language.

                                                                                                        2. 2

                                                                                                          Right, it’s neither one nor the other. And it’s not fully accurate either:

                                                                                                          Things like visibility modifiers (e.g. public, private, and protected) might be in scope as well; however, enums, namespaces, and parameter properties would be out of scope for this proposal since they have observable runtime behavior.

                                                                                                          Is this correct? JavaScript would just ignore private keyword, but not all of TS compilers do, or?

                                                                                                          1. 4

                                                                                                            Didn’t JavaScript come up with it’s # notation for private fields after typescript had its private keyword? Private appears a little superfluous now perhaps.

                                                                                                            1. 1

                                                                                                              Visibility modifiers (public, private, and protected) are not valid in JavaScript. When TypeScript is compiled to JS, those modifiers are removed. If it didn’t remove them, it would cause JS engines to throw syntax errors.

                                                                                                              JavaScript has also added a different way of having private fields (#privateProperty), but that is a separate thing.

                                                                                                              1. 1

                                                                                                                No, i know about the #, but I am asking what happens if you both remove these modifiers and enforce them. E.g. tsc removes the private modifier on compilation, so I have a method visible (but the source said private) on an object. In the new proposal, the annotations are optional, but enforced. So the same source but different results. Or am I getting something wrong?

                                                                                                                1. 1

                                                                                                                  The proposal is that certain slots in JavaScript syntax will be reserved for type information, and that type information can be used by external tools, but that the JavaScript runtime will ignore those slots at runtime. This allows TypeScript, Flow, etc. to use those slots to express their type systems, and to continue to improve without locking JavaScript itself into any of those competing approaches.

                                                                                                                  So to answer your question, if this proposal is adopted, and it includes visibility modifiers like private, then TypeScript would no longer have to strip out private, because the JavaScript runtime would just ignore the modifier.

                                                                                                                  It is worth noting that the original post says that the proposal may include visibility modifiers, but it may not.

                                                                                                            2. 1

                                                                                                              I think it’s important to acknowledge that Typescript also doesn’t enforce types, so a whole set of question of semantics and what to do in those cases would appear.

                                                                                                              “enforcing types” is also a whole weird thing, where it’s like… for dictionaries that would involve comparing a bunch of keys, and you get into a lot of questions about what X or Y is… remember, Typescript is not nominal typing but structural typing! So “enforcement” involves a lot more work than just checking a type tag

                                                                                                              1. 2

                                                                                                                Typescript does compile time enforcement, so while regular JS presumably can use incorrect types TS language projects presumably don’t

                                                                                                                In terms of checking type constraints, that is not a thing that is hard for modern JS engines - they can also do it more efficiently than JS based checks as they can coalesce property checks to get single pointer comparison to verify in the common cases

                                                                                                                1. 1

                                                                                                                  Well, no one has any experience using runtime type checking across the whole codebase. Typescript’s type system is extremely expressive and complex - it allows arbitrary recursive type-level computations and type transformation. For example, here’s a type-level SQL library: https://github.com/codemix/ts-sql

                                                                                                                  In many of my projects I use these capabilities to express a well-typed interface, but resort to any casting underneath the covers because I can’t prove my internal code follows the interface constraints. Most TS code today has issues like this to some extent. If we turn on runtime type check, how much inconsistency would there be? I expect quite a lot.

                                                                                                                  1. 1

                                                                                                                    If we add syntax now, but then don’t enforce the type checks then the results is exactly what you’re claiming: people will write code that fails the typechecks, so in future the spec will not be able to specify typechecking rules because of breaks to existing content.

                                                                                                                    As far as TS goes: I’m not convinced that their type system is decidable, and any standard for optional/progressive typing in JS will require an exact deterministic specification.

                                                                                                                    For performance: it is possible that you could make type constraints that are hilariously complicated, but I suspect that the slow check case is uncommon.

                                                                                                                    I also think you could start with a less complete specification that has the benefit of not burning syntax. For example, you can start off with only having the ability to to specify property existence rather than existence + type, etc in such a case the type annotation would be purely additive so unconstrained properties would not prevent the future addition of constraints

                                                                                                                    I’ve said it elsewhere: the problem with this “specification”, is not the addition of syntax, it’s the addition of syntax that the runtimes are expected to ignore. If they ship ignoring the syntax then the syntax is burned: it can’t be reused for actual constraints in future without breaking content.

                                                                                                            1. 4

                                                                                                              I wonder if you could support mutability somehow?

                                                                                                              I’m partly imagining torrent websites hosted on bittorrent (because it’s kinda meta) but could be generally useful/interesting perhaps.

                                                                                                              1. 4

                                                                                                                There’s a bittorrent protocol extension where you can distribute a public key that points to a mutable torrent, but I don’t know if it has been ported to webtorrent.

                                                                                                                1. 2

                                                                                                                  The reference implementation for BEP0046 is done with webtorrent, don’t know if/how it works in browser though.

                                                                                                                  1. 2

                                                                                                                    As far as I understand, you can’t use DHT in a web browser, as nodes do not support WebRTC. The Webtorrent project includes a DHT library that works with Node.js (which is used by the desktop application).

                                                                                                              1. 1

                                                                                                                This is cool - I’ve been thinking about this asymmetry too.

                                                                                                                I think it’s important to note that the tag is an important part of the enum. Product types have field names (structs) or indices (tuples), with each field containing a value. Sum types have tag names (e.g. MyEnum::Left in the blog post) where each tag might contain some value.

                                                                                                                So to me the dual to an anonymous tuple would be a enum with tags 0, 1, etc that automatically derives traits based on the types it encapsulates. I’m not sure if the syntax of rust is ammenable to having integer tags quite like this - you could probably do an anonymous enum with identifiers for the tags though (the dual of an anonymous struct). But “tagless” unions like u8 | str seem completely different to what is in rust today, and adding them wouldn’t remove the asymmetry.

                                                                                                                (I’ve been imagining a structurally-typed language with a subset of the grammar being a JSON-like literal syntax for both structs and enums with some kind of “dual” syntax between them. E.g. you construct a struct like x = { a: 1, b: 2 }, then destructure it with x.a. You’d construct a dual type by reversing the getfield notation, so I’m thinking y = .a 1, and you’d destructure this with a switch statement like switch y { a: 10, b: 20 } or similar. You could maybe manage something similar with nominal typing. Perhaps in rust for anonymous enums you have no name for MyEnum so a value is just constructed like ::Left(1). To make it nice to use it’s the compiler’s job to “union” the enum types automatically during type inference, say if two branches return two different, but compatible, anonymous enum values).

                                                                                                                1. 1

                                                                                                                  So I suppose in rust some potential syntax for a tuple-like enum value of an anonymous type with integer tags might be ::0(x), ::1(y), etc?

                                                                                                                1. 2

                                                                                                                  Nodejs compatibility sounds like a terrible idea. Why develop for deno at all in that case?

                                                                                                                  1. 3

                                                                                                                    Consider python 2 and 3 - compatibility tools like “six” are useful in the interim as the community transitions between incompatible languages/runtimes, even if in the final state they aren’t particularly desirable.

                                                                                                                    1. 1

                                                                                                                      Codemods I get. But maintaining source compatibility with Node requires all of the terrible decisions in Node to be supported forever as well. And it also makes it much less likely there’ll be a “killer app” package that runs on Deno only.