This idea was first talked about in . The basic idea is simple: In Static Dispatch we allow the form of subject.method(arg1, arg2, ...). Sometimes though you'd like to use that method with the subject partially applied to a argument that has a matching signature.
I am suggesting that if we can determine that a) the type of subject is a nominal type, and b) that nominal type has a method with the name method(and obviously the module that the type and method come from), we can desugar to a closure.
# Subject.roc
module [Subject, method]
Subject := {}
new = |{}| Subject.{}
method : Subject, Str -> U64
method = |{}, str| 0 # Implementation not important here
# Other.roc
module []
import Subject
not_important := {} => Result (Str -> U64) _
subject = Subject.new({})
some_effect!.map(subject.method)
The not_important function in Other.roc would be desugared to:
not_important := {} => Result (Str -> U64) _
subject = Subject.new({})
some_effect!.map(|str| subject.method(str))
which would then be desugared by SD to
not_important := {} => Result (Str -> U64) _
subject = Subject.new({})
some_effect!.map(|str| Subject.method(subject, str))
Can you give an example in this thread of where you'd want to use this?
I have to run off to work, but if you watch Richard's new video in #show and tell > roc-realworld initial exploration he talks about it
I know it's for Ok(router.handle_req!), but I don't know why I want this feature without reading that other thread
Though it's early
Ah, ok. Basically you want it for using a limited form of partial application in a closure position without writing out the closure
It's pure sugar
And, I think people who come to the language because of PNC and SD will find that very intuitive to write as they use it a lot
what happens if I have a custom record defined in the same module as a function named foo and then later I add an exposed field to the record named foo? Do all the usage sites silently change to being field access because that takes priority?
Note: let subject = Subject -> subject = Subject.new()
Do all the usage sites silently change to being field access because that takes priority?
At least if that happened it would immediately fail to compile
Hmm... unless the record field loaded happens to be a lambda of the exact same type as the original code
yeah
arguably it's a bad enough idea to have a method and field of the same name that we could emit a compiler warning if you do that
just be like "hey this is error-prone, pick a different name for one of them"
I can't think of a situation where it would definitely be a good idea haha
In expression contexts, I don't think record.(field!) is currently used for anything. Wouldn't that let us do Ok(router.(handle_req!)) in a way reminiscent of the .pass_to(...) alternative?
that's interesting :thinking:
certainly the syntax is open right now
I'm not a fan of that at all. Adds weirdness and friction where it doesn't seem needed
Also, I still really hope we use .(func)(args) instead of .pass_to(func, args)
So really don't want to steal the syntax currently
That's what this pushes for
Well, it's different, I suppose
Yeah, those would be totally different meanings
Brendan Hansknecht said:
Note:
let subject = Subject->subject = Subject.new()
I've been writing too much Rust :-)
I think Sam is on to something actually....
The desugaring is the same (at least textually)
Ok(router.(handle_req!))
to
Ok(|req| router.(handle_req!)(req))
Oh no, it would have to be
Ok((router.handle_req!))
that desugars to
Ok(|req| (router.handle_req!)(req))
Yeah, it is definitely different. One is using static dispatch, the other is using an imported method.
So
subject.method() # static dispatch
(subject.method)() # record closure field apply
subject.(func)() # pass to local function
Ok(subject.method) # static dispatch
Ok((subject.method)) # record closure field apply
Ok(subject.(func)) # pass to local function - which is actual generic partial application
:dizzy:
Looks logical
in general, (a) evaluates to a
I don't think we can make (subject.method) evaluate to something different than subject.method
I definitely like the first half:
subject.method() # static dispatch
(subject.method)() # record closure field apply
subject.(func)() # pass to local function
I feel like I need to look more at the real world repo before I have an option on all of the partial application cases
Feels convenient, but not sure how necessary
Tearoffs are never necessary, but I think with method syntax it's very helpful
Sorry, necessary was the wrong word. How much I will feel the helpfulness outways the cost of adding another special cases.
For sure
Last updated: Jun 16 2026 at 16:19 UTC