Real Programmers Implement Functions On Top Of Classes

class BaseFunction {
  static #allowInstantiation = false;

  constructor(...args) {
    if (!BaseFunction.#allowInstantiation) {
      throw new Error(
        "Why are you trying to use 'new'? Classes are so 2015! Use our fancy 'run' method instead!"
      );
    }
    for (const [name, validator] of this.parameters()) {
      this[name] = validator(args.shift());
    }
  }

  parameters() {
    return [];
  }

  body() {
    return undefined;
  }

  static run(...args) {
    BaseFunction.#allowInstantiation = true;
    const instance = new this(...args);
    BaseFunction.#allowInstantiation = false;
    return instance.body();
  }
}

class Add extends BaseFunction {
  parameters() {
    return [
      ["a", (x) => Number(x)],
      ["b", (x) => Number(x)],
    ];
  }

  body() {
    return this.a + this.b;
  }
}

console.log(Add.run(5, 3)); // 8



146 points · 29 comments · view on lemmy.world

29 Comments

BaumGeist@lemmy.ml · 39 pts · 1y
[ removed ]
pfm@scribe.disroot.org · 32 pts · 1y (1 reply)

A true FP programmer would make it apply instead of run...

xmunk@sh.itjust.works · 13 pts · 1y

Ahem, map...

And, of course, everything is a lazy list even if the functions can't handle more than one element in each list.

NorthWestWind@lemmy.world · 20 pts · 1y (8 replies)

It's just Java

Ephera@lemmy.ml · 21 pts · 1y (1 reply)

Yep, some code examples from the official documentation. This:

printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

...is syntactic sugar for this:

interface CheckPerson {
    boolean test(Person p);
}

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

...which is syntactic sugar for this:

interface CheckPerson {
    boolean test(Person p);
}

class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
        return p.gender == Person.Sex.MALE &&
            p.getAge() >= 18 &&
            p.getAge() <= 25;
    }
}

printPersons(roster, new CheckPersonEligibleForSelectiveService());

The printPersons function looks like this:

public static void printPersons(List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

Basically, if you accept a parameter that implements an interface with only one method (CheckPerson), then your caller can provide you an object like that by using the lambda syntax from the first example.

They had to retrofit lambdas into the language, and they sure chose the one hammer that the language has.

Source: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

mbtrhcs@feddit.org · 15 pts · 1y

That's not quite right. In bytecode, lambdas are significantly more efficient than anonymous class instances. So while the lambda implementation is semantically equivalent, characterizing it like you have is reductive and a bit misleading.

ornery_chemist@mander.xyz · 4 pts · 1y

Hence, Clojure. It's not just functions that implement IFn... as the string of "cannot cast to clojure.lang.IFn" errors that I get because I couldn't be bothered to validate my data's shape is eager to inform me.

LodeMike@lemmy.today · 3 pts · 1y (4 replies)

Golang also does this, but it's not classes.

firelizzard@programming.dev · 1 pts · 1y (3 replies)

How so?

LodeMike@lemmy.today · 1 pts · 1y (2 replies)

Golang uses modules, not classes. Each of which may have its own main function.

firelizzard@programming.dev · 1 pts · 1y (1 reply)

Huh? Main file? Do you mean main package? A module can contain an arbitrary number of main packages but I don’t see how that has anything to do with this post. Also are you saying modules are equivalent to classes? That may be the strangest take I’ve ever heard about Go.

LodeMike@lemmy.today · 1 pts · 1y

I meant main function. Oops

bleistift2@sopuli.xyz · 17 pts · 1y

validators is a shitty name for something that actually does type conversion.

navi@lemmy.tespia.org · 12 pts · 1y

Dont look at C++ with std:: function

Burghler@sh.itjust.works · 11 pts · 1y

JS disgusts me

FiskFisk33@startrek.website · 5 pts · 1y

That'll be fun in a multi threaded setting!

TunaSlap@lemmy.world · 4 pts · 1y (6 replies)

What theme are you using, i like it!

Chais@sh.itjust.works · 5 pts · 1y (5 replies)

Looks like Catppuccin Mocha

TunaSlap@lemmy.world · 2 pts · 1y

Thanks!

TunaSlap@lemmy.world · 2 pts · 1y (3 replies)

Actually now that check it again its not quite right for mocha. But it's close!

Chais@sh.itjust.works · 2 pts · 1y (2 replies)

You're right, the background is too dark. Probably crust instead of base. Maybe it was customised or created improperly.
But I'm fairly confident that the palette is Catppuccin, probably Mocha.

sebastiancarlos@lemmy.sdf.org · 3 pts · 1y (1 reply)

I can confirm it's Catppuccin Mocha. I am not currently aware of the background color issue, but I'll look into the matter soon. Thanks for letting me know. Also how dare you. But thanks.

Chais@sh.itjust.works · 2 pts · 1y

The background is most likely a color that is in the Mocha palette, just one that is intended for dark accents, not regular background.

FourPacketsOfPeanuts@lemmy.world · 4 pts · 1y

Amazing, lol

Chais@sh.itjust.works · 3 pts · 1y

I think that's called a functor.

CrossbarSwitch@lemm.ee · 3 pts · 1y

I've seen something similar to this at work. Horrible.

NigelFrobisher@aussie.zone · 3 pts · 1y

Very DRY.

30p87@feddit.org · 2 pts · 1y

"Why are you trying to use 'new'? Classes are so 2015! [...]"

Uses new to throw error

Alexstarfire@lemmy.world · 1 pts · 1y

Boomkop3@reddthat.com · 1 pts · 1y

Oh sweet caffeine, race conditions in the constructor