6 ms·
Rust maybe a little adhoc in places (e.g. the misappropriated Haskell/ML function syntax, enum/struct asymmetry), but overall it is a fantastic effort. It is no
by willtim 5y ago
Rust maybe a little adhoc in places (e.g. the misappropriated Haskell/ML function syntax, enum/struct asymmetry), but overall it is a fantastic effort. It is not an easy task to combine an advanced static type-system with mainstream ergonomics, but they seemed to have pulled it off. The fact that it is also not owned and controlled by a single big tech entity is icing on the cake. I really hope it achieves even greater success.
- daoxid 5y ago> misappropriated Haskell/ML function syntax, enum/struct asymmetry I'm curious, could you elaborate?
- willtim 5y agoIn Rust, a function definition left-hand-side looks like an annotated pattern, e.g. foo(x : int) Therefore, one would expect to annotate the return type as, foo(x : int) : string Since the pattern is showing foo applied to x. The Rust syntax is actually confusing for both Haskell/ML programmers (where the arrow comes from) and mainstream programmers. It's too small an issue to change now though. Rust's support for proper "algebraic data types" is very good and gives it an advantage over languages like C++. However there are some small surprises, such as forcing all enum constructors/fields to be public (one must therefore wrap it to make an abstract data type). Every language has its warts and these are particularly minor ones.
- devit 5y agoThat would imply that "foo(x: int)" is a string rather than a function. Haskell doesn't use that notation either, it uses -> both for the parameter list and for the return type, and separates argument names (arguably, these are poor choices, since currying is not an efficient CPU-native operation and not intuitive so distinguishing between multiple arguments and returning closures is useful, and argument names are useful for documentation).
- valenterry 5y agoI don't think that would be the implication, because we are in the context of a function. Otherwise, we should also not write foo(x: int) but foo: int -> ?
- willtim 5y ago> That would imply that "foo(x: int)" is a string rather than a function But foo(x : int) is a string! It literally reads "foo applied to x". In the function definition, it appears to be used as a left-hand-side pattern which is "matched". The definition is written as if to say, whenever the term foo(x) is encountered, use this definition here. At least, that was my expectation. > Haskell doesn't use that notation either OCaml does and Haskell once had a proposal to add it. Haskell type signatures are normally written separately, but it does support annotating patterns with the right extensions.
- ZoomZoomZoom 5y ago> But foo(x : int) is a string! Can you say so decisively for a language with first-class functions?
- willtim 5y agoYou are quoting me out of context. In many languages, the term and/or pattern foo(x : int) is a string, if foo : int -> string.
- gpm 5y agoNo, foo(x: int) is not a string, it's not even an expression, it's not even an AST node. It's a fragment of the larger ast node fn foo(x: int) -> ReturnType { body } The ast here splits into Function { name: foo signature: (x: int) -> ReturnType body: body } I.e. the arrow binary op binds more tightly than the adjacency between foo and x: int. And the type of foo is a function, not a string. A "better" way to write this (in that it breaks down the syntax into the order it is best understood) might be static foo: (Int -> ReturnType) = { let x = arg0; body } Or to put it another way. Reading foo(x: int) as "foo applied to x" in this case is a mistake, because that's now how things bind. You should read that "foo is a (function that takes Int to String)". It's a syntactic coincidence that foo and x are beside eachother, nothing more.
- the__alchemist 5y agoFYSA, The Rust approach is the same way it's done in Python.
- willtim 5y agoWell the Python community is hardly an authoritative figure on static typing :)
- acdha 5y agoThis is true but Python has a user community which is several orders of magnitude broader, which confuses the issue a bit. These days if I was designing a language I'd probably ask “How would I explain this to someone who learned JavaScript/Python?” since even if you have great reasons for doing things differently it's a pretty reasonable way to predict sources of confusion for newcomers.
- ModernMech 5y agoI wouldn't say this is a wart or confusing (to me at least). As someone who uses Haskell, C++, and Rust regularly, I just accept that each language has its own syntax. It's true that Rust borrows ideas from many languages, but I view Rust's syntax as its own thing, and the meaning of the symbols are what they are. It doesn't have to do things the C++ way or the Haskell way. It does things the Rust way, and that's not a wart.
- willtim 5y agoIt did confuse me when I first saw it, but yes, it is not really a significant issue.
- uryga 5y ago> one would expect to annotate the return type as > foo(x : int) : string > since the pattern is showing foo applied to x. kind of like the C/C++ "declaration mirrors use" thing, i.e. int *foo; which means "the result of dereferencing `foo` is of type `int`" which is not the same as saying foo : Ptr<int>; because in the former, you're kind of describing what `foo` is a without actually saying it, if that makes sense. i find that way of specifying types counterintuitive in both C/C++ and MLs.
- ape4 5y agoI typed `foo(x : int) : string` when I was starting and there was an error message telling me to use `-> string` so many people expect this syntax.
- jeltz 5y agoHaving used both Haskell and main stream programming languages I did not at all think that was confusing. The type of "fn foo(x: int) -> string" is quite obviously "fn(x: int) -> string" for people coming from languages like C. I do not see how a colon would make anything more clear. Imagine the function "fn bar(x: fn(x: int) -> string)", would that be more clear with a colon? On the other hand the enum thing is certainly surprising.
- willtim 5y ago> Imagine the function "fn bar(x: fn(x: int) -> string)", would that be more clear with a colon? In your example, why bother naming the inner "x" variable for the function param? It cannot be used on the right-hand-side (definition of "bar"). For that reason, the notation is not exactly "clear". In OCaml the annotation would be: bar( x : int -> string )
- east2west 5y agoIn Rust you can write ```f: fn(i32) -> i32```. Ocaml's syntax is more consistent, I agree, but its colon operator has different precedence than in Rust, so I am not sure its rational applies to Rust.
- stared 5y agoPersonally, I find Rust syntax to be well-designed. At least, compared to any practical programming language I know. Quite a few times I was surprised that Rust breaks with some old patterns that were copied over and over in the last 50 years or so. For example: "match" instead of "switch", or the same if/else regardless if it is a statement or a value. These are small touches, but they show attention to detail.
- sireat 5y agoSyntax wise Rust seems heavily inspired by the "good parts" of Scala. - Pattern matching via "match" - if being an expression (among most things) Those are both found in Scala.
- lgessler 5y agoDidn't Scala get both of those things from ML?
- linkdd 5y agoRust syntax is weird. Weirdly good and sometimes bad. I'm currently designing my own toy language and writing the compiler (to LLVM IR) in Rust. Representing the AST with Rust's sum types is so simple. Visiting that AST through pattern matching is great. But the "enum" keyword still bugs me. The way you define product types (tuples, records, empty types) and then their implementation, just awesome. But the "struct" keyword still bugs me too. It feels "high level" with some quirks. Then you have references, Box, Rc, Arc, Cell, lifetimes etc... It feels (rightfully) "low level". Then you have traits, the relative difficulty (mostly for Rust newbies like me) of composing Result types with distinct error types, etc... It feels "somewhat high level but still low level". Sometimes you can think only about your algorithm, some other times you have to know how the compiler works. It seems logical for such a language, but still bugs me. The one thing I hate though, is the defensive programming pattern. I just validated that JSON structure with a JSON schema, so I KNOW that the data is valid. Why do I need to `.unwrap().as_string().unwrap()` everywhere I use this immutable data?
- cletus 5y agoFirst let me say: I like Rust. I'm a fan. But... it did make some early decisions that are going to be hard to shake off, most notably around build times. This [1] is well worth a read. [1]: https://pingcap.com/blog/rust-compilation-model-calamity https://pingcap.com/blog/rust-compilation-model-calamity
- capableweb 5y agoYeah, the build times. How does a normal Rust developers development environment look like? Do you have to rebuild after each change if you want to try out the change itself, after you've written tests and so on? How is the REPL experience if there is one? My only experience with Rust so far has been trying to learn it by writing applications in it and also use 3rd party CLIs, but quickly loosing interest because the "change <> try out change" cycle has been too slow and cumbersome, and installing/compiling dependencies take fucking forever, even on a i9-9900K.
- nessex 5y agoWhen I use rust, I find compile times faster and more manageable than other languages due to the speed of iterative compiles. Compiling from scratch is very slow, but iterative compiles are faster than most of my golang compiles and faster than running a JS builder in most projects. To make it extra fast, I follow the instructions from the bevy game engine[1]. With that setup, the feedback loop is quick. [1] https://bevyengine.org/learn/book/getting-started/setup/#enable-fast-compiles-optional https://bevyengine.org/learn/book/getting-started/setup/#ena...
- coder543 5y ago> iterative compiles are faster than most of my golang compiles Maybe you're comparing apples to oranges here. I've worked professionally with both Rust and Go for years now on a variety of real world projects, and I've never seen a similarly-sized Go codebase that compiles slower than a Rust one. If you're comparing incremental Rust compilation to first-time Go compilation, maybe they could be competitive, but... Rust is incredibly slow at compilation, even incremental compilation. Yes, using lld can speed up Rust compilation because a lot of the time is often spent in the linker stage, but... that's not enough to make it as fast as Go. YMMV, of course, but... my anecdotal experience would consider it disingenuous to say that Rust compile times are an advantage compared to Go, and I'm skeptical that Rust compile times are even an advantage compared to the notoriously slow webpack environments. Rust is good at many things, but compilation speed is not one of them. Not even close, sadly. "cargo check" is tolerable most of the time, but since you can't run your tests that way, that's not actually compilation.
- darksaints 5y agoBuilding an ecosystem on top of a advanced strictly typed language also has an initial hurdle that requires a lot of effort to overcome. Potential tool developers must go through the effort of becoming familiar with the language and seeing the potential for, as well as the path to major improvements. But once they're there, the result is extremely advanced tooling that other language ecosystems have taken years to develop. Things like IDEs, debuggers, static analyzers, superoptimizers, fuzzers, verification tools, build systems, language interop adapters, and code generators, are all examples of things that can take advantage of advanced type systems to develop strong capabilities extremely easily. I think Rust is starting to show signs of such benefits, and I think in 10 years we'll all be looking back at it with surprise that anyone ever doubted it.