14
$\begingroup$

While in the shower thinking about my code, my mind went to C#'s type parameters, and I wondered what stops the type param from being placed in the middle of the function name like this:

List<T> Numbers<T>() (normal)

List<T> LoadDataAs<T>From(string filename)

which would be called like this, which in my opinion is a much nicer and more expressive syntax:

var Data = LoadDataAs<float>From("data.txt");

From there its easy to see how this could be expanded to standard parameters:

Add(int number)To(object ComplexObject)

var complexObject = new ComplexObject();
Add(5)To(complexObject);

This would of course be syntactic sugar for standard function calls, and you could even permit placing arguments anywhere in a name, so long as they are in the correct order, meaning you wouldn't necesarily have to define functions using a special syntax like the above. As far as I can tell, it would be a simple transformation to move the arguments to the standard form, AddTo(5, ComplexObject) and then call.

Is this a viable syntax? I imagine it would cause havoc attempting autocomplete in a IDE, but are there any massive cons I'm missing?

$\endgroup$
5
  • 4
    $\begingroup$ one possible drawback depending on the syntax of your langauge - it would cause ambiguity if it's ever valid to write two functions next to each other without calling them and without anything between them (for instance, if a list doesn't have to use explicit separators or for some kind of generalised implicit multiplication). also, with the proposed syntax for standard parameters, it's not immediately obvious how to refer to the function as a value (if that's something that's possible) $\endgroup$
    – Silver
    Commented Jul 20, 2023 at 1:17
  • $\begingroup$ I've done Add(5).to(complexObject), and our Java testing library has assertThat(complexObject).isEqualTo(5);, which are close emulations of this feature. $\endgroup$ Commented Jul 20, 2023 at 16:24
  • 1
    $\begingroup$ I'm suspicious that the syntax as presented will have a big ambiguity problem. In Objective C (I'm not sure about Smalltalk), method calls are always in square brackets, not bare: [foo convertTo: bar withAccessories: baz]. This version of the syntax doesn't leave any ambiguity about what is a function call. $\endgroup$ Commented Jul 20, 2023 at 17:03
  • $\begingroup$ This idea is Fan<Fu##ing>Tastic(). It also seems very SmallTalk-esque. $\endgroup$ Commented Jul 20, 2023 at 23:41
  • $\begingroup$ Robotframework does something like this. $\endgroup$
    – bob
    Commented Jul 21, 2023 at 15:24

4 Answers 4

30
$\begingroup$

These are mixfix or multi-part function names. They are most well known from Smalltalk, introduced in Smalltalk-76:

aBoolean ifTrue: [ ... ] ifFalse: [ ... ]
4 between: 3 and: 5

but they can be included in any language, including with your syntax. My PhD work was with a language called Grace that had almost exactly that, in fact. It even used that form to define its control structures: if (condition) then { ... } else { ... } was a call to a three-part method name if-then-else. Each part of the name is followed by an argument list (and perhaps a type argument list), and a following identifier is parsed as beginning another part of the name. Your examples would all be valid and working method calls in a version of that language.

As well as Smalltalk and its derivatives, this sort of construction exists in Agda, Objective-C, Enso, Lean, and a number of other languages. Typically, the entire name is used as a method or function selector, including all of the parts in some canonicalised form. Sometimes only the first part is identifying, and the remainder are only used to label parameters. Both approaches are feasible and have representation, either as sugar for a more standard function call (e.g. in Agda, if_then_else_ with three arguments) or specifically supported as multi-part message sends.


There's no reason for IDE autocompletion to be any more trouble than usual. You will be able to have multiple candidate completions for any given prefix, but that situation exists anyway: whenever a function name is partially-typed, or in a language with overloading. That's not a new issue, and is addressed in existing systems.

For the case where arguments can appear literally anywhere in the name, the completion would presumably be the remainder of the name, which may not be where the next argument would naturally be inserted. Adopting a structured multi-part approach like existing languages do is probably a better choice when this sort of tooling is desirable (and perhaps other times, too).

While this isn't an extremely common syntax, it's one that standard grammars are easily capable of expressing. Any tooling will be able to parse this construction, and only the normalisation of the components into a canonical identifier is an additional step to more typical syntax—and name-mangling isn't so rare, anyway.


You can imagine still-more-flexible paths: with multiple parts to the name, it's plausible to allow declaring methods that identify calls by a pattern describing optional or repeated components, which we did in this DLS'15 paper. You could step back even further than that too and allow still-freer identifier grammars. There's no real obstacle to the limited form in the question except user unfamiliarity: all the parsing, compiling, and run-time issues are fairly trivial.

$\endgroup$
5
  • 2
    $\begingroup$ Does Objective-C not count as a derivative of Smalltalk? $\endgroup$
    – Bbrk24
    Commented Jul 19, 2023 at 11:11
  • 3
    $\begingroup$ @Bbrk24 I left it off the list originally and then decided somebody would complain it was unfair to lump it in as a true derivative. So… yes, but it’s hybrid enough to call out, I think. $\endgroup$
    – Michael Homer
    Commented Jul 19, 2023 at 18:44
  • 3
    $\begingroup$ @Bbrk24 Just tell me what /doesn't/ claim to be a derivative of Smalltalk these days. But its keyword syntax does have a lot going for it: unlike the rest of its left-to-right evaluation order which is- let's face it- only marginally better than APL's strict right-to-left evaluation. $\endgroup$ Commented Jul 19, 2023 at 18:55
  • 2
    $\begingroup$ @MarkMorganLloyd Objective-C is a lot closer to Smalltalk than any other C-family language I've seen. If nothing else I don't know of any other language that adopts their method call syntax, even if the class/metaclass situation is more common now. Also, everything claiming to be Smalltalk was a lot less true in 1982 when the OOPC was created. $\endgroup$
    – Bbrk24
    Commented Jul 19, 2023 at 19:33
  • $\begingroup$ See also Parsing Distfix Operators (Peyton Jones, 1986). $\endgroup$
    – davidbak
    Commented Jul 20, 2023 at 22:29
9
$\begingroup$

A while ago, I built a language that did this: https://github.com/Telastyn/Tangent

say (words : string) to (username : string) => void {
  print "Dear:";
  print username;
  print "";
  print words;
}

entrypoint => void {
  say "Hello" to "World";
}

are there any massive cons I'm missing?

Many of the big cons in Tangent are related to the dynamic dispatch, but the flexible syntax didn't help. object.function(params) syntax is very easy to parse left to right. More importantly, it's very efficient to parse left to right. As soon as you run into parameters first or multiple identifiers or expression nesting, things get dangerous.

Is Add(5)To(complexObject) one function call or two function calls (or three function calls because Add(5) returns a partially applied delegate)? You'll be tempted to parse more of the expression to disambiguate things which will effectively change your grammar from context free to a context sensitive one - including all of the computational complexity that entails.

$\endgroup$
8
$\begingroup$

This is effectively Currying the function but with a name for each subfunction which can allow you to differentiate overloads.

That's kinda how objective C and swift do their calls. Though that is more [object functionName:param1 paramName2:param2]

But you can easily convert that to a regular parenthesis syntax and drop the requirement for the first object.

If you use dot syntax Add(5).To(complexObject); then existing auto-completion will work fine assuming that Add returns a special type that has a To member function.

$\endgroup$
2
$\begingroup$

Other answers are already great, so I'll juts drop a link to ObjectiveC example - a language widely used in Apple ecosystem until it recently gets replaced with Swift and other newer designs.

Check for example https://en.wikipedia.org/wiki/Objective-C#Interfaces_and_implementations

@interface classname : superclassname {
  // instance variables
}
+ classMethod1;
+ (return_type)classMethod2;
+ (return_type)classMethod3:(param1_type)param1_varName;

- (return_type)instanceMethod1With1Parameter:(param1_type)param1_varName;
- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName
                              param2_callName:(param2_type)param2_varName;
@end

The method3 and method2with2params show exactly that construct you asked about. It looks a bit clunky at first (if you are used to i.e. typical C/C++/C#/Java/etc notation), but after a tiny bit of getting used to (and with good naming scheme!!), it reads quite naturally..

Note that the ReturnType and ParamTypes are NOT part of the function name (partially that's why they are in parenthesis).

So:

+ (return_type)classMethod3:(param1_type)param1_varName;

is a function named classMethod3 param1_varName
for example sortBy NAME (classmethod=sortBy; param1=NAME)

and

- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName
                              param2_callName:(param2_type)param2_varName;

is a function named instanceMethod2With2Parameters param1_varName param2_callName param2_varName
for example: sortBy NAME and AGE (instancemethod=sortBy; param1=NAME; param2callname=and; param2=AGE)

Note that in the latter example, when function has 2 parameters or more, you can use these additional callnames like by - these do not affect the parameters at all. These are just interleaved additional parts of the function name, and they exist only for better readability of the function names. With them, you don't have to name parameters in "weird" way like "byName" or "fromClient". The "by" and "from" are injected as extra parts, and parameters have natural identifiers: name, client.

$\endgroup$
1
  • $\begingroup$ There's already two other answers that mention Objective-C. Swift has also been around for nine years now, and I've seen Objective-C fairly infrequently in comparison to Swift (especially since the advent of SwiftUI). $\endgroup$
    – Bbrk24
    Commented Jul 21, 2023 at 2:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .