Dissociated functions

Rust already has associated functions and methods - why not to implement dissociated ones? The main idea of this topic is following syntax:

fn bar(_x: &str) {} "foo".(bar) => bar("foo") (*"foo).(bar) => bar(&*foo)

fn bar(_x: &str, _y: u8) {} "foo".(bar, 1) => bar("foo", 1)

Our answer to functional programming’s |> :sunglasses:

I'll ask the obvious question (even though I know ambiguity is a problem): why not use normal method call syntax ("foo".bar())?

Because bar is not associated with str :face_with_monocle:

As I understand, Rust will never see UFCS. And imho Rust doesn’t need UFCS. I think this syntax would be a nice addition tho.

Rust already have UFCS, although it's the opposite of what you're asking: it allows to call methods as if they were functions, instead of allowing functions to be called as if they were methods.

I think I've seen this called "UMCS", for "universal method call syntax".

fn bar(_x: &str) {} "foo".(bar) => bar("foo") (*"foo).(bar) => bar(&*foo)

fn bar(_x: &str, _y: u8) {} "foo".(bar, 1) => bar("foo", 1)

Rewriting this in a manner that I feel is a bit easier to parse for the human eye (or at least for my sleepy brain):

// Single argument
fn foo(_x: &str) {
   // ...
}

"sna".(foo)   // Alternate syntax for `foo("sna")`.
(*&foo).(sna) // Alternate syntax for `foo(&*sna)`

// Two arguments
fn bar(_x: &str, _y: u8) {
  // ...
}

"sna".(bar, 1) // Alternate syntax for bar("sna", 1)
6 Likes

This syntax looks really unfamiliar to me, and I would definitely thought foo.(bar) is foo(bar).

Why not just use a trait to make it a method?

trait Bar {
    fn bar(self);
}

impl Bar for &str {
    fn bar(self) {
        unimplemented!()
    }
}

"foo".bar();
1 Like

Because

"foo".(bar)

is easier than

trait Bar {
    fn bar(self);
}

impl Bar for &str {
    fn bar(self) {
        unimplemented!()
    }
}

"foo".bar();

Dissociated functions should be an analogue of pipe operator from functional languages.

I understand your motivation, I think postfix macros (if supported in future) will be the best solution here, instead of introducing a new syntax

I'll be straightforward here. I don't like it, and there is no plausible way you'll change my mind here. The syntax is foreign, unclear, and seemingly without purpose. The rationale is unclear. I do not believe a beginner would have even the slightest chance of understanding what code like this would do.

4 Likes

Somehow similar:

use apply::Apply;

fn single_param(_x: &str) { /* ... */ }
"s".apply(single_param); // single_param("s")

// two arguments must be combined into tuple
fn tuple_param((_x, _y): (&str, u8)) { /* ... */ }
("s", 1).apply(tuple_param); // tuple_param(("s", 1))

Edit: or create Apply1 similar to Apply:

trait Apply1<Res> {
    fn apply1<P, F: FnOnce(Self, P) -> Res>(self, f: F, p: P) -> Res
    where
        Self: Sized,
    {
        f(self, p)
    }
}
impl<T, Res> Apply1<Res> for T {
    // use default definition
}

fn baz(_x: &str, _y: u8) { /* ... */ }
"s".apply1(baz, 1); // baz("s", 1)