Like the previous review for this, I'm very enthusiastic for static callables. To me, this is something that has to be in the language given that we already have dynamic callables. It would be a big hole to only have support for one and not the other.
That being said, I don't really like that func call
is the only thing that opts you in to this behavior. I realize that using func call
drastically simplifies the required implementation, however, I don't think it would be too much of a lift to also require some marking annotation on something. Now, I'm not too picky on what gets annotated, but I would strongly recommend it be on the type.
While marking individual methods as @callable
might be interesting, I feel it's important to pair this feature with how other features that use "hidden" requirements are implemented, namely with a marking annotation on the type. This allows:
- Allowing callable behavior to be an opt-in choice
- It has precedence with
@dynamicCallable
, @dynamicMemberLookup
and the proposed @propertyDelegate
feature.
I would say that the argument against type level annotations could've been applied to all existing @
annotations that give special behavior to a type. The reason given is
We feel this approach is not ideal because a marker type attribute is not particularly meaningful. The call-syntax delegate methods of a type are what make values of that type callable - a type attribute means nothing by itself. There's also an unfortunate edge case that must be explicitly handled: if a @staticCallable
type defines no call-syntax delegate methods, an error must be produced.
Now, I would argue that it is extremely meaningful. It specifies that a type is meant to be operated on in a special way, and so deserves a special marker. And that "unfortunate edge-case" isn't an edge case at all, if a user specifies a type should be callable, and they're not specifying a func call
or func _
or whatever the special method is named, then yes it should error!
As for adding callable behavior via extension, I disagree that this should be supported. Just like you can't add @dynamicCallable
via extensions, why should we allow adding static callables via extensions? In my view, this is a feature that should be used only with certain types that were designed to be used with call syntax. Just because there are existing APIs today that could benefit from doesn't mean we should let anyone tack callable behavior on types they don't own. If the author of a type thinks it should be worked with in a callable way, let them do that.