Hacker News new | past | comments | ask | show | jobs | submit login

Hi! I use F# for most tasks (from websites/JS generation, to packet capture and indexing, call routing and billing processing), and some C where required. Rust looks fantastic, and would give me the memory control I need when I need extra performance. A LOT of it comes down to simply being able to stack-allocate things; in F# I'm essentially forced to use the GC heap for even the most trivial things.

Rust looks fantastic, and I'm very excited about using it. When I found out about it and started reading how it worked, it was almost exactly what I had been wanting, on almost every count. I really wish it had existed a few years ago.

My comments are from someone that's just been playing around with the getting started guide of Rust.

-- Lack of custom operators limits expressiveness. For instance, look at Parsec or FParsec. Why shouldn't they be allowed to exist in Rust? But if custom operators are totally out of the question, then what about user-defined infix functions? (And then, why not functions with nearly arbitrary codepoints as identifiers?)

-- It seems that currying, partial application, and function composition are sorta cumbersome in Rust. Is this a conscious decision, that idiomatic Rust shouldn't be doing such things? Like " add >> inc >> print " being equivalent to "|a,b| -> print(inc(add(a,b)))" ? In F# I use this kind of stuff all the time, especially when processing lists.

-- It seems there's a difference between function types. Like if I do "fn foo(a:int, f:proc(int)->int)", I can call it with foo(1i, inc) if inc is a fn. But if I first do "let f = inc; foo(i1, f)", that's an error. Offhand, I'd assume this is due to lifetime management, but it feels a bit strange. When writing HoFs, do I need to implement a version for each kind of function? Or am I totally misunderstanding things?

-- Sorta related, does Rust allow something like:

  let inc =
    let x = ~0
    || { let res = *x; *x += 1; res }
The idea is to expose a function that contains some private state. I remember hearing that Rust changed the ownership stuff around a few times, but the basic idea is to create a globally-accessible closure. Is this impossible, requiring us to use statics to store the global state?

-- Why doesn't Rust warn when discarding the result of a function if not unit? Is it idiomatic Rust to return values that are often ignored?

-- Even without higher kinded types, monadic syntax is useful. Is Rust planning any sort of syntax that'd let users implement Option or Async? How does Rust avoid callback hell? Or is this quite possible today with macros and syntax extensions?

-- Has Rust considered Active Patterns (ala F#)? With that, I can define arbitrary patterns and use them with matching syntax. E.g. define a Regex pattern, then "match s { Regex("\d") => ... , Regex("\D") => ... , _ => ... }"

-- Consider allowing trailing commas in declarations; why special-case the last item?

-- And last but not least: Please, please, please, reconsider type inference. It's baffling why "fn" requires type annotations, but a closure does not. What's more, why is there even different function syntax in the first place? I've heard the reason that "top level items should have types documented", but that's a very personal decision to make. It certainly isn't something the Rust compiler should force me to do in all my code. Why do ya gotta limit my expressiveness? (Same argument I'd use for custom operators.) Statics/consts should also have type inference. And note that these items aren't necessarily public or exposed - but a private fn on a module still requires type annotations.




Rust 1.0 is supposed to be somewhat minimal, so things like custom operators and monadic syntax are out scope since they can be bolted on in a backwards-incompatible way in future versions. Active patterns would probably also fall under this.

Likewise, having functions have their own private global state is not on the table, and seems to me to actually be unsafe since the function could be called at the same time in two different threads overwriting the operations done to them.

For not warning when returning non-unit values for unit returning functions, a lint could probably help there.

Type inference isn't changing. The rule is that items need to be explicitly declared and expressions are inferred. Closures are expressions while most (all?) functions are items. It's a purposeful limitation on inference based on experience with prior languages.


> -- Consider allowing trailing commas in declarations; why special-case the last item?

They are allowed in most circumstances.


And if they're not, I'd say that might be a bug.


  > Lack of custom operators limits expressiveness.
I understand that this is going to be hard for some to swallow, but it is explicitly a non-goal of Rust to be maximally expressive. :) New features are motivated almost solely by solutions to concrete pain points in Rust code (especially Servo). This may sound particularly Blubby, but the devs are well-versed in Haskell (and Lisp, and Scala, and ML, and...). With respect to custom operators and infix operators, they're taking the cautious approach of leaving the option open for future versions of Rust, since they can be added completely backwards-compatibly if there's significant demand for them post-1.0.

  > It seems that currying, partial application, and 
  > function composition are sorta cumbersome in Rust.
There are two different camps in competition here. One camp wants Rust to have default function arguments as per C++. Another camp wants Rust to have automatic currying. These camps are in opposition because they both want to control what `foo(bar)` does for a function `foo` that takes more than one argument. So far the devs have resisted entreaties from both camps. Experience post-1.0 may change this.

  > It seems there's a difference between function 
  > types.
Closures are really terrible right now and are in the midst of getting a complete overhaul to be more useful and less hacky. :) Procs won't even be a thing afterward. Please excuse our mess!

  > Why doesn't Rust warn when discarding the result of 
  > a function if not unit?
It does warn, for any type that has the `#[must_use]` attribute. This includes the stdlib's `Result` type, which is basically Haskell's `Either` except explicitly intended to be used for error handling.

  > Is Rust planning any sort of syntax that'd let 
  > users implement Option or Async?
I'm not sure what this means, as users can already implement Option. It's not special-cased by the language in any way.

(As for the lack of HKT, that's a hotly-desired feature for post-1.0. It's still a bit pie-in-the-sky, but the devs have acknowledged that it would be very useful to improve our error handling story.)

  > Please, please, please, reconsider type inference.
Not going to happen. :) Requiring type signatures on top-level functions is so useful that even the languages that allow it to be inferred tend to enforce their presence via social pressure. In addition to providing powerful self-documentation, this vastly improves error messages. Finally, I suspect that Rust's trailing-semicolon rule (especially combined with the willingness to ignore the return value of most functions) would interact poorly with function signature inference.

  > Statics/consts should also have type inference.
IIRC this isn't possible, but I've forgotten the reason for now. It certainly isn't motivated by any sort of philosophy.


While I strongly disagree with limiting expressiveness, operators, etc., and the thing about top-level functions is misleading (because a nested, private, module isn't really "top level), this is a fantastic response and helps me understand Rust a lot better. Thank you very much.

I hope things will change (esp. type inference, which while playing around is really annoying, even if I eventually end up wanting to annotate. A REPL could fix a lot of the pain.). But there's nothing out there that competes with Rust, and the C-friendliness means I can fairly easily interop with languages with more expressiveness ;).

As far as async/option, I meant something like Haskell's do notation or F#'s workflows. This allows implementation of async code without callback hell or the huge limitations of promises or whatnot. (But without HKTs, you can't mix multiple monad types within one block.)


While this is far from a promise, any future implementation of HKT would likely be accompanied by a do-style notation. The `do` keyword is unused yet reserved for precisely this reason (https://github.com/rust-lang/rust/blob/master/src/libsyntax/...).


Actually, after asking around, I must have misremembered about inference on statics being impossible, it's merely difficult. :) In lieu of full inference, there are proposals to allow statics to have the same sort of very simple inference scheme that C++ and Go have: https://github.com/rust-lang/rfcs/issues/296




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: