Skip to content

Commit

Permalink
add "use = not : for initialization and named parameters" draft
Browse files Browse the repository at this point in the history
  • Loading branch information
soc committed Sep 24, 2023
1 parent ecacf91 commit 5cf3337
Showing 1 changed file with 99 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: "Language Design: Use `=`, not `:` for Initialization and Named Parameters (WIP)"
date: 2023-09-20
---

# DRAFT

## The Problem, explained using Rust as a cautionary tale

Rust has two distinct syntactic variants for "invocations"[^1] ...
- one for calling functions and
- one for initializing structs and enums[^2]

... that provide different affordances and features:

- struct initializer arguments can be named, and can also use a shorthand notation
- method arguments cannot be named, and what looks like shorthand notation is just positional arguments

The following example shows both:

```rust
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

fn user(email: String, username: String) -> User {
User {
active: true,
username,
email,
sign_in_count: 0,
}
}

fn main() {
user("jane@example.com".into(), "Jane Example".into());
}
```

### Diverging Code Styles and Best Practices

In practice this means that you have diverging recommendations and best practices (option structs, default hacks, builders etc.)
for dealing with issues like "this thing has grown and is taking way too many parameters now".

And every improvement that is going to be made will change the scale slightly, causing churn due to another thing becoming the next "best practice".
(And most likely only apply to either struct/enum init *or* method calls, but not both.)


### Needless Ambiguity

Structs using `{` also means that something as trivial as `if foo {` is ambiguous in Rust.


### Lack of Consistent Rules for Type Ascriptions

It breaks the consistency of always being able to rely on while reading that `: Foo` is a type ascription.
Languages from the 70ies managed to get this right, Rust somehow regressed on that, failing to ship
[type ascriptions](https://rust-lang.github.io/rfcs/0803-type-ascription.html) and
[giving up on it after 8 years](https://rust-lang.github.io/rfcs/3307-de-rfc-type-ascription.html).


### Default Parameters Made Hard

Using `:` for struct initialization means that it's not a good syntax options for extending the language for default parameters:

struct X { x: String: "foo" } // ugly

## The Solution

What should they have done:

- Function calls, method calls, struct init, enum init consistently use `()`, not a random mix of `{}` and `()`
- Have *one* ruleset that all those invocation follow.
- Use `=` for named parameters, such that everything works if default parameters are added in the future.
- Shorthand notation or positional arguments? Pick one.

#### How to Distinguish between a variable assigment and a named parameter use inside a function invocation?

Example:

fun someFunction(a: Int64) = ...
var a = 12;
let b = 23
someFunction(a = b) // what does this mean?

Single rule: inside a functions argument list, the first level of `=` use is always a named parameter and never a
variable assignment, even if some variable `a` would be in scope.[^3]

If a variable assigment inside a function argument list is still needed, it could be expressed as:

someFunction(a = { a = b; b }) // still possible


[^1]: Using the term "invocations" loosely here. It's almost guaranteed that Rust fans show up anyway going "ackchyually" while completely missing the point, but hey – I tried.
[^2]: Except the tupled version of enums, for which initializers look like function calls again.
[^3]: Also, variable assignment returns `Unit`, so we never have to decide, right? RIGHT?

0 comments on commit 5cf3337

Please sign in to comment.