Giter VIP home page Giter VIP logo

Comments (7)

zth avatar zth commented on July 18, 2024 1

@mks-h thank you for your thoughts, it's always interesting to discuss and sometimes also remind ourselves why things look the way they look (and what we can do, if anything, to improve that).

One additional point with regards to labelled vs unlabelled etc is that the pipe operator works best/most ergonomically with a leading unlabelled argument, since it literally means "pass the result of the left hand side into the first argument of the right hand side".

A concrete thing related to this that we've been exploring is whether we can retain the names of unlabelled arguments as they're defined in the code. As of today, the name of unlabelled arguments is dropped in type checking, which means we can't really show argument names of unlabelled arguments in hovers and so on. This is solvable in a number of ways, and as @glennsl states arguments doesn't always have names because they can be destructured right away. But having a form of that would mean we could show them in hovers, use them when autocompleting function templates, and so on. So, there's some work that can be done on the tooling side for sure to improve the DX.

from rescript-compiler.

cometkim avatar cometkim commented on July 18, 2024

Labeled arguments (aka named parameters) is one of the important/widely-used feature in ReScript codebase, which is missing in JavaScript world.

In JavaScript, there is no such thing, so a common pattern is to model arguments as a single object, which relies on a runtime feature (creates many temp instances by/with evaluating destructuring).

Because the rescript compiler optimizes labeled arguments into normal arguments with a fixed order, users can benefit from this without any additional overhead.

from rescript-compiler.

mks-h avatar mks-h commented on July 18, 2024

Thanks. I'm aware of this, my point is why not make every argument labeled? They have identifiers anyway, just allow the user of the function to choose which arguments they want to pass by order, and which by label. Right now this choice is made at the definition of the function, and users of the function cannot override it. Instead of forcing them to use either order or label, the choice could be left to them.

TLDR:

  • Make all arguments labeled (~ syntax at the definition no longer needed)
  • Allow to pass labeled arguments by order, instead of the label

from rescript-compiler.

cometkim avatar cometkim commented on July 18, 2024

What problem are you having and how does this proposal solve it? I can read some of your syntactic preferences, but honestly I don't understand how that would be any problem.

Also it will make breaking changes to call site that already relies on existing behavior today.

from rescript-compiler.

mks-h avatar mks-h commented on July 18, 2024

I don't actually have any practical issue that would be solved by this change. I was learning and trying out ReScript, and labeled arguments looked overcomplicated.

The behavior I talk about is similar to PHP 8+ named parameters, and it is more or less just an ergonomic improvement. That being said, it isn't a syntactic preference, but an objective improvement.

The ergonomic benefit is that it:

  • Unifies function call signature (the way you call a function doesn't change based on the way it's defined)
  • Allows to use already present but not exposed labels/identifiers (for all the same reasons labeled arguments exist in the first place)
  • Allows to skip using labels which may be unnecessary or annoying to write.

Note, that the proposed behavior doesn't take away, or make harder to use, any existing feature. It only simplifies and improves the feature set.

As for backwards compatibility, this doesn't have a behavioral change that would invalidate existing code. The only difference is syntactic — the ~ operator in function body definition isn't anymore necessary. It can be marked deprecated in the next major released, but the support can be kept in the compiler indefinitely. Migration (removing ~) can also be easily automated, even by a sed script.

from rescript-compiler.

glennsl avatar glennsl commented on July 18, 2024

...it isn't a syntactic preference, but an objective improvement.

Note, that the proposed behavior doesn't take away, or make harder to use, any existing feature. It only simplifies and improves the feature set.

That's some pretty bold claims to make.

Just to name one thing you're getting wrong, it's not true that all arguments have names. First of all, because there's a difference between implementation and interface. This separation enables abstraction, which is a very useful feature, and so to accomplish what you ask, all function types need to include argument names. Higher-order functions too, and the functions passed to them need to have the same argument names, which necessarily need to be very generic for functions such as Array.map, for example. Or there needs to be some exceptions to the rule, making it more complex and harder to understand.

Furthermore, function implementations don't necessarily have argument names either, because arguments can be destructured directly in the argument list, e.g. ({foo, bar}) => .... This is true for JavaScript as well. So you'd also need to give all those arguments mandatory names for this to work.

That's three major breaking changes based on just one bad assumption in your reasoning. And they all add quite a bit of inconvenience.

There's historical reasons for why labelled arguments work like they do too, notably that functions used to be curried, but my point here is that language design is hard. There are a lot of different features that interact in a lot of different non-obvious ways. No language designer would make such bold claims, about their own language even, without doing a fair bit of investigation.

One thing that should be possible once the language has completed its transition to uncurried functions, however, is to make labels optional at the call site. That wouldn't be trivial to implement either I don't think, for historical reasons, but it should at least be possible and not too invasive.

from rescript-compiler.

mks-h avatar mks-h commented on July 18, 2024

That's some pretty bold claims to make.

No language designer would make such bold claims, about their own language even, without doing a fair bit of investigation.

Indeed 😅 To clarify, what I meant is that the change wasn't about a syntactic restyling, but was supposed to achieve a practical benefit. I didn't mean to imply that my opinion is 100% error-less truth, and that's why the issue is a Request For Comments.

it's not true that all arguments have names. First of all, because there's a difference between implementation and interface.

To the best of my knowledge, every parameter in the function definition has an identifier (ignoring destructuring for now). The difference, as you noted, is in the semantics — regular identifiers aren't meant to be read outside of the function, while "labeled" are. Although not in the best way, but I have noted this issue in the OG post, but didn't in the previous one:

The only semi-practical difference I see, is that all arguments' names will be exposed by LSP, so developers might need to be a little more mindful of them.

Making the arguments' identifiers public will make developers rename some of them, which ideally should happen before the feature gets used. Once users start relying on those names, library authors will have to make breaking changes to rename them. This also requires library authors to always be mindful of their API's names. This is totally a valid issue, and practically a breaking change.

This separation enables abstraction, which is a very useful feature

I cannot think of any use-case, apart from keeping function declaration stupid simple with one-letter parameters.

which necessarily need to be very generic for functions such as Array.map, for example.

I don't necessarily agree. For example, while Rust doesn't have labeled arguments, it exposes the parameter names (through LSP ans API reference). It's definition of array.map uses f for the mapper: pub fn map<F, U>(self, f: F) -> [U; N].

function implementations don't necessarily have argument names either, because arguments can be destructured directly in the argument list, e.g. ({foo, bar}) => ....

Fair take, I totally missed it. This is the last nail in the coffin of the idea.

Many thanks for your comments and patience. Sorry if the RFC bothered you.

from rescript-compiler.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.