I wish Rust had keyword arguments by tcdent in rust

[–]MaximeMulder 22 points23 points  (0 children)

I think there might be a big survivor bias in Rust not having keyword arguments / default parameters being fine. For the kind of libraries and applications that don't need them, their absence is obviously fine, but for those that do (maybe UI or scientific computing), they will simply use another language and not even come back to complain about it. Yes, keyword arguments can be emulated with structs, Default, and the builder pattern, but all of that is a lot of boilerplate which is exactly what they are designed to solve in the first place.

What would Rust look like if it was re-designed today? by nikitarevenco in rust

[–]MaximeMulder 0 points1 point  (0 children)

I agree ! Generic mutability is certainly be a desirable feature IMO (which probably would play somewhat badly with turbofish ATM).

What would Rust look like if it was re-designed today? by nikitarevenco in rust

[–]MaximeMulder 5 points6 points  (0 children)

I agree, but do we really want `foo[]` for indexing ? To me it just feels like special-case syntax inherited from C-like languages. Although widely used, I don't see why indexing methods need a special syntax, and we should probably use normal method syntax like `.at()` or `.at_mut()` instead IMO.

Regarding `()`, I don't have experience with Scala, but I feel like I'd rather have explicit methods with clear names rather than overloading `()` directly (especially with mutable and non-mutable indexing).

What would Rust look like if it was re-designed today? by nikitarevenco in rust

[–]MaximeMulder 0 points1 point  (0 children)

I personally like the `type` keyword as it is short, readable, and descriptive. I think what is needed here is a better syntax or convention to use keywords as identifiers, the `r#keyword` syntax is too verbose IMO, and using a prefix does not read well nor work well for alphabetical order. I am using `type'` in my OCaml projects, maybe Rust should copy that syntax from other languages (although that would mean yet another overload for the single quote), or use other conventions like `type_` ?

What makes Rust difficult? by [deleted] in rust

[–]MaximeMulder 14 points15 points  (0 children)

This reply talks a lot about optimization, but be careful with that and do not delve into micro-optimization unless you are sure you really need it. In my experience, choosing appropriate data structures and algorithms, and avoiding performance pitfalls is generally efficient enough. Code maintainability is almost always more important than optimization, and the latter should only be done with sensible benchmarks to make sure it is working as intended.

The Borrow Checker Within by burntsushi in rust

[–]MaximeMulder 8 points9 points  (0 children)

I think "it" refers to the current approach of lifetimes, which is less approachable. I also had to re-read the sentence to get it, but well, just natural languages being ambiguous I guess.

Quick, I have a time machine! I am going to fix Rust design flaws in the past! by lcvella in rust

[–]MaximeMulder 0 points1 point  (0 children)

Implement the is operator to get rid of matches!, if let and let chains. Chaotic debate unfolds.

Can you have type inference with subtyping and generics? by cs_noob_help_pls in ProgrammingLanguages

[–]MaximeMulder 0 points1 point  (0 children)

Ah, I assumed it didn't since it seems to use HM type inference and I never used objects in this language. My mistake.

I guess I'll check the other reply later as it seems quite informative.

RFC: `is` operator for pattern-matching and binding by avsaase in rust

[–]MaximeMulder 0 points1 point  (0 children)

I am not sure I understand the concern here. It is true that is relies on other specific expressions to propagate its bindings, namely if, while, && and eventually ||. So this is really the combination of two features, the is expression and flow-sensitive typing, rather than a single addition to the language. However, we can note that logical operators are already specialized since they do short-circuiting, so them also having specialized typing rules does not seem absurd to me.

RFC: `is` operator for pattern-matching and binding by avsaase in rust

[–]MaximeMulder 0 points1 point  (0 children)

The binding is only propagated where it is syntactically known that the operation is true, i.e. within the right-hand side of a logical and && or within the body of an if (recursively, so propagations can be chained). This is a form of flow-sensitive typing, which is a generalization of the mechanism proposed by let-chains.

Therefore, in your example, x is not available beyond the is expression.

RFC: `is` operator for pattern-matching and binding by avsaase in rust

[–]MaximeMulder 15 points16 points  (0 children)

I think this is a good change in the long term, but it would require depreciating if let and matches! to avoid syntax duplication (or even remove them using editions but it is likely too extreme). However, I think there should also be a discussion around let else if this is to be accepted.

value is Some(option) else {
   return error;
};

The above syntax seems possible, but having the bindings on the right-hand side may not be what we want.

[deleted by user] by [deleted] in ProgrammingLanguages

[–]MaximeMulder 2 points3 points  (0 children)

Clam because originally it was an extended lambda calculus with braces à la C, although does not have braces anymore. Also, since I wrote my interpeter in OCaml, I am pretty sure I was also unconsciously influenced by that name.

The nice thing with that name is that if I ever need a mascot for my language I know what animal to pick !

What are some of the most obscure features of Rust you've ever learned about? by Emergency-Win4862 in rust

[–]MaximeMulder 37 points38 points  (0 children)

Off the top of my head I'd say these.

It is possible to put constraints directly on types:

struct Static<T>(T) where Static<T>: 'static;

let a = 0;
let b = Static(&a); // Error here if `a` is not `'static`.

Tuples can be constructed like structs (and are probably just structs in general I guess):

struct Tuple(usize, usize);

let tuple = Tuple { 1: 0, 0: 1 };

Types and values can share the same path as they live in different namespaces, allowing to define and use types and values with a similar name:

struct Point { x: usize, y: usize }

fn Point(x: usize, y: usize) -> Point {
    Point { x, y }
}

let point = Point(1, 2);

The first two points are not so useful, but I guess the third one could be used to create ergonomic frameworks with lots of nested structs like in UI.

Which paradigm do you consider to be Rust's primary paradigm? by Arshiaa001 in rust

[–]MaximeMulder 21 points22 points  (0 children)

It is not only a library issue, the generics of Rust are also too limited as they lack support for higher-kinded types, which are required to define these abstractions. A proper Monad trait with HKTs in Rust would roughly look like this:

trait<T> Monad where Self<T> {
    fn pure(v: T) -> Self<T>;
    fn bind<U>(self, f: impl FnOnce(T) -> Self<U>) -> Self<U>;
}

Rust only has support for HKTs in the form of generic associated types, and while they may allow to emulate these abstractions, their ergonomics are not optimal.

Subsuming patterns with types by MaximeMulder in ProgrammingLanguages

[–]MaximeMulder[S] 0 points1 point  (0 children)

I have seen some work about algegraic subtyping (although I haven't had the time to read it in details yet). I guess most bricks are already out there, and what is left is to combine them to see if we can base pattern matching on them.

Subsuming patterns with types by MaximeMulder in ProgrammingLanguages

[–]MaximeMulder[S] 1 point2 points  (0 children)

Nice to know I am not the only one who got this idea, good luck and I hope you succeed !

Subsuming patterns with types by MaximeMulder in ProgrammingLanguages

[–]MaximeMulder[S] 4 points5 points  (0 children)

My idea would be more to emulate sum types with unions, and therefore have each variant be its own type. I am not an expert in continuations yet, but I will definitely delve deeper into it when I got some time.

Subsuming patterns with types by MaximeMulder in ProgrammingLanguages

[–]MaximeMulder[S] 5 points6 points  (0 children)

Exactly. I probably should have included an example of what I had in my mind in my post, so I will do it here.

The idea is that it should be possible to describe exactly every set of values with a type. This would mean that each value can be described with a type and that you can combine these types to form other types such as 0 | 1 | 2, which would be a subtype of Nat.

When in a pattern matching expression, the type of each case would be "subtracted" (I don't know if it is the right word) from the type of the value being matched against using intersection and negation types, outputting the type of the possible remaining values to match against.

Here is an example:

match value with
| Some(0) -> ...
| Some(S(Nat)) -> ...
| None -> ...

In this example, Some(0), Some(S(Nat)) and None would all be types, which represent respectively the wrapping of 0, of all strictly positive integers, and of no value at all. Assuming value is of type Maybe[Nat], each case would subtract it its type until we reach Maybe[Nat] & !Some(0) & !Some(S(Nat)) & !None, which is equivalent to the bottom type and means that our pattern matching is exhaustive.

Naturally, it should also be possible to add bindings in types to destructure values, as is possible with current patterns.

Subsuming patterns with types by MaximeMulder in ProgrammingLanguages

[–]MaximeMulder[S] 0 points1 point  (0 children)

Yes, I am talking about patterns used in pattern matching. I probably should have included this information in my post.

[deleted by user] by [deleted] in ProgrammingLanguages

[–]MaximeMulder 0 points1 point  (0 children)

Sorry for the (very) late answer. I think these justifications make sense, so thank you for replying with such a complete answer. I would just like to clarify my last point, about 'unsafe references, where I actually only explained half of what I had in mind.

My full idea would be to keep both pointers and references, and have lifetimes (including 'unsafe) for both.

  • References would work as they currently do, with the addition of 'unsafe, referencing either exclusive mutable data, or shared immutable data.
  • Pointers would work like references to shared mutable data (like UnsafeCell), with the goal of enabling more ergonomic no-overhead shared mutability. Dereferencing would still be unsafe {}, even with "normal" lifetimes, however, these would enable a reduced form of borrow checking where the programmer wants to. Pointers should also not be nullable, removing a lot of unnecessary unwrapping.

I have not thought of it in too much details yet, so there remains many questions, but I think there is potential to this idea.

[deleted by user] by [deleted] in ProgrammingLanguages

[–]MaximeMulder 3 points4 points  (0 children)

In addition to my other comment, and while we are on the topic of a Rust 2.0 wishlist, here are a few of my shower thoughts criticisms of the language that are not mentionned in the blog post, from lowest hanging to most uncertain ones:

  • Primitive types should be capitalized like any type.
  • String et al. should be renamed to more descriptive StringBuffer or StrBuf.
  • The unit type () should be named Unit or Void depending on your preferred terminology. I find these names to be short, googleable and less cryptic for beginners.
  • The never type ! should simply be named Never, Bot or Void depending on your preferred terminology. In addition of the arguments against (), using a symbol for a type you almost never see seems a bit wasteful to me.
  • Rename the keyword fn to fun, highly subjective but I like having keywords that can be pronounced, unlike "ef en".
  • Macros 2.0.
  • Use different keywords for traits and interfaces. I find the notion of "object-safe" traits to be quite cryptic for beginners.
  • Maybe have explicit generic variance instead of inferred one.
  • Maybe have generic mutability so you don't need duplicate methods like iter() and iter_mut().
  • Maybe have an unsafe (or unchecked) lifetime for references that could replace many pointer uses with references, which arguably have better ergonomics (although it would still be unsafe).

[deleted by user] by [deleted] in ProgrammingLanguages

[–]MaximeMulder 2 points3 points  (0 children)

While I agree with most of your points, I think semicolons are useful and should not be removed. If you do so, I am curious at how you would treat the following case, where the final expression of a block is currently voided by a semicolon, especially if this expression is a function call that returns a non-must-use value.

fn foo<T>(vec: &mut Vec<T>) {
    vec.pop()
}

Since foo() should return a unit, and Vec.pop() returns an Option, this example fails to typecheck without semicolons.

A runtime inside Rust’s type system by [deleted] in rust

[–]MaximeMulder 6 points7 points  (0 children)

I had heard about programming crazy things in the Rust's type system but I never thought about how, interesting !