1. 24
  1.  

  2. 4

    I still haven’t sat down and actually used Futhark, but golly I enjoy these blog posts. If I ever design my own programming language I will re-read this entire blog before starting.

    1. 3

      Incidentally, here is a piece of advice: if you are ever agonising over some design detail that is not core to what makes your language special, and all options seem equally reasonable, just go with whatever Rust does. Odds are that whatever Rust decided was preceded by significant debate, and that whatever they picked has few gotchas. Lexical syntax is one area where this is particularly good (and easy) advice to follow.

      Questionable.

      1. 10

        I think it’s a good idea if you want to avoid having your language called weird or academic. Rust is very conscious about its “weirdness budget”, and carefully threads the needle between looking familiar and fixing old problems. It managed to get traction as a C-family language despite being influenced by ML-family and other “exotic” languages.

        There are many similarities between Rust, Swift and TypeScript. All of them are here to stay, so it’s likely that they are influencing what the evolution of preferred “C-like” syntax and set of features is going to be.

        1. 1

          fixing old problems

          Which specifically?

          1. 12

            If we’re talking about syntax and comparing with C-family languages as a baseline:

            • Making variable declarations unambiguous
            • Making types unambiguous – AFAIK, you can always look at an arbitrary identifier and from a minimum amount of context know whether it is a type or a variable
            • Relatedly, getting rid of (foo) bar as a way to cast expression bar to type foo, which in my experience is a great way to make parsing hard
            • Making generics use <> (yeah yeah I don’t like it either) but making it unambiguous with regards to the greater-than and less-than operators
            • If’s are expressions, removing the need for a ternary operator
            • Almost everything is an expression, actually
            • Curly braces after if/while/etc are mandatory, removing a source of errors
            • Parentheses after if/while/etc are absent, removing a source of noise
            • Basically everything has a literal syntax of some kind or another
            • Optional trailing commas everywhere you could want them, making code generation easier

            I could go on. There are a few warts that are widely acknowledged as such I think, and you can do some outright painful things with pattern matching if you try hard enough (I recently had occasion to write if let Ok((&mut mut foo,)) = bar {...} and it made me rethink my life), but all in all if you want something that looks kinda like C++, Rust’s syntax is pretty good. If you don’t want something that looks kinda like C++, you can make some fairly superficial alterations and get something that looks more like a typed Lua.

            1. 1

              I don’t think there is a lot in that list in terms of “fixing old problems”.

              It reads more like “don’t be stupid, don’t be C” … welll d’oh!

              1. 1

                Well, C and C++ are still the most popular languages for low-level development, so just being “a better and safer but more powerful C” is actually a great pitch.

                1. 1

                  Yeah, just not one that supports the original claim.

              2. 1

                Parentheses after if/while/etc are absent,

                One of the saddest choices…

                1. 3

                  I think this one contributes to Rust looking “ugly” to some people, but as a syntax it works fine. Optional braces were proven to be dangerous (goto fail bug), and once you fix that by making braces required, the parens are unnecessary.

                   if (foo) bar();
                   if foo {bar();}
                  

                  is the same number of characters and the same number of shift presses (at least on a US-layout keyboard).

                  1. 1

                    That’s disingenous, usual style uses whitespaces around {}, so if foo { bar(); }. Two characters longer, and I believe I am not alone when I say it’s still uglier even with two characters more.

                  2. 2

                    Why? They are not functions

                    1. 1

                      gratuitous incompatibility with parsers already installed in many, many human brains for no benefit

                      1. 4

                        Eh, I thought it was weird at first, but it very quickly goes from weird to normal. I actually think it reads much more clearly than with parens.

                        1. 1

                          Wait until you discover that it isn’t Java you’re parsing. 🤯

                          It’s not gratuitous at all, unlike languages which keep all of C’s defects for sentimental reasons.

                          1. 1

                            since this one isn’t a defect, but merely an arbitrary choice, going with the existing popular choice seems good. There are dozens of languages sharing the parenthesized if/for/while/etc syntax

                      2. 1

                        Gets rid of single-line ifs, which is a great way to cut down bugs. You can still put the whole thing with braces on one line, too.

                      3. 1

                        Regarding garnet research, I’m curious if you had a look at Nim too? I didn’t notice it mentioned in your notes there, at least at a glance. One of the things I find interesting about it, is that from what I read somewhere, its author’s original intention was to try and build it all based on macros, with as minimal core as possible. He claimed there that he kinda failed at that (sorry, I don’t have a link), but I think it leaves Nim with a rather powerful macros system, that (together with Rebol, which I see Nim as a kinda typed variation of) made me start to understand how powerful can macros probably be in Lisp. Other than that, I like how it manages to look nearly like Python, but be statically typed, and that the type declarations tend to nearly disappear. Though, similar as with OCaml, I find it somewhat of a language more focused on being practical than being “pretty from CompSci perspective”, with quite a number of seemingly somewhat sketchy, weird and randomly slapped on features here and there, that surprisingly tend to work together quite well. Sorry for the ode, I just like this language for some time recently :)

                        edit: uh, and one more thing related to modules, where I believe Go struck gold and Nim does rather poorly (though that’s understandable given it has “universal function call syntax” and operator overloading), is with naming and “avoiding stutter” - in that module names give mental context to their contents, and can be assumed as part of the name, but then in quite many places can be thus omitted (esp. inside a module). So you don’t need “newRegex” but just “regex.New” (IIRC Lua indeed also enables a similar approach (in fact, when I was trying to write a Lua interpreter for Go long time ago, I got super challenged by the “uncanny valley” of many “parallel evolution” similarities between them, such that I found it extremely difficult to write code in both languages in a single file)).

                      4. 2

                        Declarations always start with a keyword (particularly fn). This neatly avoids the Most Vexing Parse from C++, and it allows function declarations to be nested inside other function declarations.

                        But more importantly, it will make writing a Rust REPL in the future so much easier, because you’ll be able to type items into a prompt that expects an expression, and it’ll just work. Joe Armstrong complained bitterly about how Erlang can’t do that, because the item grammar and the expression grammar in Erlang aren’t compatible. He flat-out said “Erlang has a bug” because of this, and he’s right.

                    2. 4

                      I think Rust made a big mistake of choosing <> for generics instead of [], but if you are to follow X by default, Rust is probably not a bad choice of X (compared to, say, Scala). Futhark uses ML-style module for generics so does not need generics bracket anyway.

                      1. 3

                        I’d say that Scala 2 would be an excellent choice to follow.

                        The language is way more regular than Rust in the parts that are likely to be of relevance/copied/shared¹.

                        ¹ i. e. not the “emulating compile-time Prolog with implicits” part

                      2. 1

                        This section got my attention as well.

                        I’m curious if folks who do compiler / plt here work feel similarly.

                        1. 2

                          Rust did a good job with its string literal syntax, so I chose to copy it for a byte string interchange format for Oil called QSN:

                          http://www.oilshell.org/release/latest/doc/qsn.html

                          It’s very similar to JSON except it can express arbitrary byte strings, and doesn’t need surrogate pairs.

                          They got rid of the legacy from C like \v and octal, and the unnecessary ambiguity of \u1234 and \U00001234, in favor of \u{1234}.


                          But I think the advice is a little too “extreme” overall. I would say if you are designing a language that looks roughly like C or JavaScript, then Rust and Go are good places to look. Swift has made some good choices too. I think Go designers put just as much thought into it, and came up with similar if not identical conclusions (even though I don’t really use Rust or Go myself).

                          Though in the case of string literals, Go seems to inherit the legacy from C, which makes it slightly more annoying to implement.

                          It does feel like that kind of syntax is “winning”, i.e. ALGOL style with curly braces. Most popular languages now seem to converge on something that looks familiar to C or JavaScript users (e.g. Kotlin). There are definitely limitations to that but on the whole I don’t think it’s bad.