The fact that Functions are Objects that can have properties/methods is supremely undervalued.
Are there other languages that do this so nicely? It's the perfect blend of OO and functional.
Programming is mostly about gradually figuring out the right design I find. JS/TS let's me evolve things naturally without big rewrites.
function foo() {}
function bar() {}
function baz() {}
const commands = [foo, bar, baz]
// Run commands
commands.forEach(x => x())
foo.help = 'This does something'
// Describe commands
commands.forEach(x => console.log(x.help))
// Add some state via closure.
const config = {}
function foo() { config.blah }
// Add some state using partials.
function _foo(config) { config.blah }
const foo = foo.bind(null, config)
I can flexibly do what I need, without ever having to define a class like `Command`, which I probably don't even know what it should be yet.
This premature naming of things in OO creates so many dramas.
It avoids things like `VideoCompressor#compress()`, `VideoSplitter#split()`. You don't have to think: what state belongs in which class...you just call the function and pass it what it needs.
In Go, you can attach methods to functions (and any type really, even "unboxed" primitives).
One of the canonical examples would be a net/http: A handler can both be a struct (or any other type really) that implements a serve method, or it can be a handler function that calls itself to satisfy the interface.
In Clojure you would achieve the thing you describe by attaching metadata on your function var. It being a Lisp, it also has macros so you can pretty much do anything.
Speaking of macros:
Clojure also implements CSP channels with macros in core/async, inspired by Go.
Channels are a very powerful construct that you might like a lot. With channels you can completely avoid the function coloring problem (callbacks, promises, async/await). Perhaps most importantly they decouple execution from communication.
So going back to your example, your commands could be producers that send their results on channels. They don't need to know what consumes w/e they make, nor do they need to hold on to it, mutate something, or call something directly.
Good analogies would be buses on motherboards, routers and switches in networks, conveyor belts, message queues in distributed systems and so on.
The same is achieved with Java's use of single-method-interfaces. It doesn't matter what the method is called, it can be used in function contexts without referencing the specific method name.
I'm not sure I'd like my functions to have properties (which gives them state and can alter what calling it does with the same arguments). A big benefit of FP is getting away from OO states. Perhaps the problems I work on aren't complex enough to benefit from them, or I simply make objects from classes.
To be clear, since "can be used in function contexts without referencing the specific method name" might be ambiguous to some- in Java you still call the "function" as if it was a class implementing the interface. IE: `interface Foo { String bar(); }` is still called as foo.bar().
It's just that there's now syntactic sugar for creating anonymous classes: `Foo foo = () -> "baz"` that automatically expands to an anonymous class that conforms to `Foo`. The compiler automatically assigns the method name for you.
Interesting, didn't know about single-method-interfaces.
There are lots of singletons in OO. I find it useful to add static metadata (not state) to them, without having to escalate them to a class. I guess Java has decorators for the same purpose. I'd really like decorators for functions in TypeScript though.
The properties don't have to be 'state' per se; they can also be used to store metadata about the function. e.g. you could have a function with 'domain' and 'range' on it, or an 'integrate' method.
If the result of `integrate(...)` depend on `domain` and/or `range`, what would you call that other than 'state'? Or if `integrate(...)` doesn't reference `domain`/`range` what's the use of it being there?
Or as I'm interpreting this, is sort of like documentation or information that could be used at runtime for code generation. Basically a shorthand for composing the function with its metadata. The nice thing about separation is that you know that when calling the function, there's no possible way for it to reference the metadata that is associated with it because it's composed externally.
JS also doesn't have TCE, but for Python even just the lambda limitations are surprisingly annoying. I can't tell you how many times i've been frustrated because it's nearly impossible to put a print statement into a python lambda
As a Python enjoyer, why do we want to shove so much into lambdas rather than just doing an inline function `def`?
Is it the fact that you have to give it a name? If so I'd say just using some generic name like `{f,fn,func(tion),callback,etc}` is fine (at least as much as an anonymous function is), and to me would usually be more readable than an inline definition of a multi-statement lambda would be.
Or maybe it's the fact that lambdas are allowed in the first place, so people are going to use them, and then when you want to debug a lambda you'll probably have to go to the trouble of changing it to a function? That is a fair complaint if so.
In any case I can see how it could be annoying if you're more used to a language full of complex inline lambdas.
JS also kills Python for inline functions thanks to hoisting.
It's much easier to follow the control flow with hoisting. I see `run()` being called, and then I want to know what it is. In other languages you are usually seeing a huge bunch of inline functions and then asking: okay, but when and how is this actually called?
def foo():
def run():
print("hi")
run()
function foo() {
run()
function run() {
console.log('hi')
}
}
honestly I don't like this style of writing... in your js example I see `run()` and my first though is where the hell is run defined? is a global? I don't search - nor I write - the called function AFTER the calling ones, it seems backward to me.
moreover... basically any half-decent programmer text editor has an outline with the list of functions, so this point may be moot in any way
Lambda is quite clunky. A lot is possible by abusing tuples and walrus assignment, which Ive on occasion used for one liners. e.g. you want to execute a function for each element of a list (print is a function in py3) so mapping over a generator with eg
This sets x to func1(x), then executes func2, then leaves None in place of the element in the map iterable. (of course you could do the same with a list comprehension, you wouldn't even need the lambda in that case, and good python would _actually_ be a for loop.)
The typical approach to this in Python is to define a callable class instead. If you define the __call__() method on a class, instances of the class will be callable.
Ah, that's not really that different than in TypeScript then. There you have to define a callable interface, and can add whatever properties you want
interface SomethingCallableWithAnExtraProperty {
// This means you can call it like a function.
(args: Whatever): SomethingReturned
// And since it's an interface, you can still do interface-y things
anotherProperty: string
}
The fact that Functions are Objects that can have properties/methods is supremely undervalued.
Are there other languages that do this so nicely? It's the perfect blend of OO and functional.
Yes. C#. The equivalent are `Func` and `Action` types representing functions with a return and without a return. In fact, the JavaScript lambda expression looks awfully familiar to C#.
One of the snippets below is C# and the other is TypeScript:
var echo = (string message) => Console.Write($"You said: {message}");
var echo = (message: string) => console.log(`You said: ${message}`);
The same signature in C# and TypeScript:
var Apply = (Func<string, string> fn, string input) => fn(input);
var result = Apply(input => ..., "some_string");
var apply = (fn: (input: string) => string, input: string) => fn(input)
var result = apply(input => ..., input);
The C# can version can also be written like:
var Apply = (Func<string, string> fn, string input) => fn(input);
var lowercase = (string input) => input.ToLowerInvariant();
var result = Apply(lowercase, "HELLO, WORLD");
Great repo. I've always wanted to build a transpiler from TS to every other language. We have so many languages but all syntax is so similar in the end.
I often wonder how much of the code we write is actually doing something not possible in another language. Like runtime-specific, or low-level stuff. Most of the business logic...loops and if statements are rather similar.
this doesn't really address OP's point, where in JS you can do:
const foo = () => doSomething;
foo.help = "this is a description of the function";
const commands = [foo];
// print help
commands.forEach(c => console.log(c.name, c.help || "No help is available for this function");
Presumably this isn't possible in C# because it's statically typed, so the object returned by "() => doSomething" can't be converted into one that supports adding more properties?
.NET/C# has a `dynamic` type (aka `ExpandoObject`). That would be one way to do it (but would require casting to invoke). It's not exactly the same since you'd assign the `Func`/`Action` to a property of the `dynamic`. `dynamic` is generally avoided due to how easy it is to get into a pickle with it and also performance issues.
An alternate in this case is probably to return a tuple which I think is just as good/better.
Example:
var log = (object message) => Console.WriteLine(message);
var foo = () => log("Hello, World");
var fn1 = (foo, "This is the help text");
var commands = new[] { fn1 };
commands.ToList().ForEach(c => {
var (fn, help) = c;
log(fn.Method.Name);
log(help ?? "No help is available for this function");
});
The tuple can also take named properties like this:
var log = (object message) => Console.WriteLine(message);
var foo = () => log("Hello, World");
var commands = new (Action fn, string? help)[] {
(foo, "This is the help text"),
(foo, null)
};
commands.ToList().ForEach(c => {
log(c.fn.Method.Name);
log(c.help ?? "No help is available for this function");
});
var log = (object message) => Console.WriteLine(message);
var foo1 = () => log("Hello, World");
var foo2 = () => log("Hello, Neighbor");
var bar = new[] {
new {
doSomething = foo1,
help = "This is foo1's help text"
},
new {
doSomething = foo2,
help = "This is foo2's help text"
},
};
bar.ToList().ForEach(b => {
var (fn, help) = (b.doSomething, b.help);
fn();
log(help);
});
Very much looking forward to this since it gives you a lot of the same power of the JavaScript map/TS `Record`.
> ...because it's statically typed
While this is true, the `dynamic`/`ExpandoObject` is an oddity and lets you do weird things like multiple dispatch on .NET (https://charliedigital.com/2009/05/28/visitor-pattern-in-c-4...). But C# has a bunch of compiler tricks with regards to anonymous types that can mimic JS objects to some extent. The tuple type is probably a better choice in most cases, however.
> Are there other languages that do this so nicely? It's the perfect blend of OO and functional.
In .net methods, properties, member variables, classes, etc. can have attached attributes that are objects. Attributes can be interrogated at runtime using reflection.
I really dislike attributes in C#. Their use is a big code smell for me. They look like C#, but they're not actually -- they're a part of the type system that has been disguised. The fact that their parameters must be compile-time constants helps perpetuate a harmful "primitive-centric" viewpoint that is antithetical to many good design principals.
Their only reason to exist is to support a use case of a 1:1 relationship between classes and functional units (or methods and functional units, or parameters and functional units, etc.), which is almost always an 80% solution that makes the other 20% extremely hard and/or impossible. I would go so far as to say that every single use of an attribute is your own code begging you for a better design. Unfortunately you're sometimes stuck with them, but that's only because you're sometimes stuck with a framework that itself is begging its creators for a better design. I wonder if Attributes were never added to the language, if developers would have just made those better choices to begin with. From my vantage point, they were a clear mistake in a language that was otherwise very well designed.
They certainly are nothing like the ability to attach methods to functions. A better example of that kind of convenience in C# is extension methods, which you can certainly define on types like Func<T>. I love extension methods and miss them in every language that doesn't have them!
> Are there other languages that do this so nicely? It's the perfect blend of OO and functional.
In Lua, you have regular, non-object functions, but you can also create a callable table using metamethods, and keep whatever data you have with it. Add to that the colon syntax sugar (implicit self vs explicit self) on table methods and I think you have a beautiful "opt-in" OO story without the unintuitive `this` business from JS.
If you take out all the “script” legacy (type coercion which was common for scripting languages when JS came out, the initial lack of a module system which led to all kinds of hacks, the scope of var declaration, etc), JavaScript and its prototype based approach is really good.
In fact I wish that constructor functions, and the class keyword never existed.
You can do the same with Object.create and a function closure, isn’t more verbose and it fits better with the mixed functional/oop approach of the language.
Sure. Everything is reducible to lisp. But even though lisp had the same capabilities decades earlier, scala is the language that brought them to the mainstream (which may well because of the historical accident that twitter's backend was rewritten in scala). I don't love scala but it has been hugely impactful.
The meaning of "mainstream" changes throughout the years. Remember that Common Lisp started as quite literally the XKCD joke of "14 competing standards", except it actually worked -- in the sense that this 15th competing standard killed all the other ones and gained widespread adoption in the Lisp community. Tons of vendors threw money and effort at the situation, and it all started as an ARPA manager's idea. Of course, we live in different times now... But Zetalisp is one fairly popular Lisp I can think of that had funcallable objects. Whether the idea was appreciated or preferred over alternatives is a separate thing entirely, of course. I would assume most people would use a simple let-over-lambda to achieve the same effect as funcallable objects.
I don't like that at all. it all seems to be too informally specified and requires divine knowledge to actually know how you're supposed to use the magic function properties.
give me something with ts’s type system and without exceptions and try/catch. i know go’s error handing gets shit, but i really like the explicitness of it.
The fact that Functions are Objects that can have properties/methods is supremely undervalued.
Are there other languages that do this so nicely? It's the perfect blend of OO and functional.
Programming is mostly about gradually figuring out the right design I find. JS/TS let's me evolve things naturally without big rewrites.
I can flexibly do what I need, without ever having to define a class like `Command`, which I probably don't even know what it should be yet.This premature naming of things in OO creates so many dramas.
It avoids things like `VideoCompressor#compress()`, `VideoSplitter#split()`. You don't have to think: what state belongs in which class...you just call the function and pass it what it needs.