Stream: ideas

Topic: static dispatch - signature restriction


view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 01:30):

As a big fan of restrictions, I can’t help but think about the possible loose ends of piping syntax.

Having methods on types allows us to introduce the following rule: the first argument should be either self type or the return type should contain self type and no self type in the arguments.

Foo.method : Foo, Bar -> Baz # instance
Foo.method : Bar -> Foo # static

But not

Foo.method : Bar, Foo -> Baz
Foo.method : Bar, Foo -> Foo

This way all methods with leading self type are instance methods, which is preferable, and others are static methods.

Yes, it's very premature and I didn't think about this long enough. But it seems sane at least on the warning level.

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:32):

I'm not sure if this restriction is needed. The rule for static dispatch is "If a function is inferred or annotated to take the module's custom type as the first arg, you can call it like a method"

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:32):

Otherwise, you can't

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:32):

Also, I don't believe the current syntax plan for methods is Foo.method : ... or Foo.method = |...|, just method : ... or method = |...|

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:34):

Generally, you call static methods on the class that the static method is defined in, and you can do that today by calling ModuleName.static_method

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 01:46):

I probably overcomplicate the idea. Simply said, I would expect self type to be always as the first argument if it's present in args. It's probably a preference, but this kind of rule automatically makes piping preferable which is why not given the presence of piping.
In oop (forgive me), static/instance methods are explicitly expressed via self/this. With static dispatch there’s no actual difference between them, it becomes a matter of style, which is great. But there is a chance to encourage the use of pipe as it’s generally more convenient.

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:47):

I agree with you that if the syntax for defining methods is Foo.method = ..., then this restriction is good!

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:47):

But I don't think it is

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:48):

Let's say it isn't

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:48):

Then what if the type takes two Foo's

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:49):

Or what if the second arg to the function is a custom type and the first isn't because it's like create_logger_with_config

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:50):

Of type create_logger_with_config : (Str => {}), LogConfig => (Logger, ExtraInfo)

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:50):

Where LogConfig is a custom type

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:51):

I think I didn't understand your proposal properly to start

view this post on Zulip Sam Mohr (Feb 13 2025 at 01:52):

But re-reading, I think the "method syntax only works if Self is the first arg" is a very strong motivator

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:07):

I’m talking only about Foo.method : Foo ...

Foo, Foo -> Any is pipeable as a method
Any, Foo -> Any is not

The logger example is a static method. You have no logger in args, so no problems with it

view this post on Zulip Sam Mohr (Feb 13 2025 at 02:13):

Sorry, when you say piping, do you mean .method or |> method?

view this post on Zulip Sam Mohr (Feb 13 2025 at 02:13):

Because the latter won't exist in our current plan of Roc

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:17):

I already live in the future, Sam :grinning:
.method

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:22):

My proposal is “if a method of a type contains the type in the args, it must be the first arg, otherwise it hinders ergonomics of piping via Type.method”

view this post on Zulip Richard Feldman (Feb 13 2025 at 02:23):

I don't understand the proposal, sorry :sweat_smile:

view this post on Zulip Richard Feldman (Feb 13 2025 at 02:23):

can you rephrase without using the word "method" since that's not a first-class concept in the language and I'm not sure what it means in this context?

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:27):

Ok, is signature Wall.paint : Color, Wall -> Wall allowed according to the static dispatch proposal?

view this post on Zulip Richard Feldman (Feb 13 2025 at 02:28):

well yeah, you can still write functions of any type you like :big_smile:

view this post on Zulip Sam Mohr (Feb 13 2025 at 02:32):

Feels like this is predicated on that Wall.paint syntax that we don't plan on supporting

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:41):

That's what I’d like to avoid, because this signature leads fo this:

wall
  .(|x| Wall.paint(White, x))
  .addWindow()

With the restriction “if self type is present in args - it should be the first one”, it will always be

wall
  .paint(White)
  .addWindow()

The problem is, when you design your set of functions, you might think only about this kind of call Wall.paint(...) forgetting about the chaining option. Yes, the whole problem sounds ridiculous. But it might be a nice touch on the warning level. Like, “you may have a convenient chaining on the call site and you evade it? Are you sane sure?”

view this post on Zulip Richard Feldman (Feb 13 2025 at 02:43):

in general I don't think we should warn for "you wrote a function that had a particular type" :big_smile:

view this post on Zulip Richard Feldman (Feb 13 2025 at 02:45):

there might be a valid reason for it (e.g. in a DSL), and since warnings fail CI, we're basically saying "never do this even though it might be the best choice sometimes"

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:51):

When this kind of signature is exported from a third party module, you have to obey. Or create an issue/pr and wait. Or fork and maintain. In any case, call site ergonomics depends on the declaration.

But I agree, it's not a good idea to decide for a developer what is a good signature and what is not.

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 02:53):

Most of the developers are likely sane and the situation is hardly possible

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 03:03):

It brings me to the inevitability of a roc linter. I remember you mentioned somewhere that you’d like to avoid configurable linters (I might be wrong). But some things such as this may lead to community-driven solutions, which is not bad, but also not as good as a native toolchain.

view this post on Zulip Richard Feldman (Feb 13 2025 at 05:25):

I like project-specific linting, like "we have decided to stop using this style and want to deprecate it in our code base"

view this post on Zulip Richard Feldman (Feb 13 2025 at 05:28):

but I think one-size-fits-all "you should not do this" things should either be warnings at the language level, because they really are a mistake on every project (e.g. warnings for unused arguments whose names don't start with _), or else they shouldn't actually be failing peoples' builds

view this post on Zulip Richard Feldman (Feb 13 2025 at 05:28):

like when people start working around linting rules, that's a really bad sign

view this post on Zulip Kiryl Dziamura (Feb 13 2025 at 08:01):

Yes, we are on the same page here. My question is - would roc provide a configurable linter or should it be implemented in the userland?


Last updated: Jun 16 2026 at 16:19 UTC