7 ms·
TypeScript is an incredible language in general. The fact that Functions are Objects that can have properties/methods is supremely undervalued. Are there othe
by grumblingdev 3y ago
TypeScript is an incredible language in general.
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.
- karmakaze 3y agoThe 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.
- TOGoS 3y agoThe 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.
- karmakaze 3y agoIf 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.
- afiori 3y agoAvoiding this type of state is essentially impossible in FP, as captured bindings of closures have the same properties
- grumblingdev 3y agoInteresting, 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.
- rezonant 3y agoTo 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.
- _ZeD_ 3y ago>>> Are there other languages that do this so nicely? It's the perfect blend of OO and functional. Python, where everything is an object.
- c-hendricks 3y agoIn Python, can typings define a property added to a function? Example, I have some Redux helpers that are functions, but those functions also define a `.actionType` property. TypeScript handles that. edit: accidentally wrote "object" instead of "function"
- dataangel 3y agoI believe you could have a Protocol that requires the property and that the object is Callable.
- c-hendricks 3y agoAh, 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 }
- nerdponx 3y agoThe 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. class IntAdder: def __init__(self, x: int) -> None: self.x = x def __call__(self, y: int) -> int: return self.x + y def __str__(self) -> str: return f"({self.x} + ??)" add1 = IntAdder(1) assert add1(3) == 4 print(add1) # (1 + ??) As mentioned in the sibling comment, you can define a Protocol which is analogous to an interface, so if you had a protocol like this: from typing import Protocol, TypeVar T = TypeVar("T") class ValueUpdater(Protocol[T]): def __call__(self, y: T) -> T: ... Then IntAdder would be considered a subclass of ValueUpdater[int] by the type checker. Demo: https://mypy-play.net/?mypy=1.5.1&python=3.11&flags=strict&gist=345aeaaa8bfb0eef387b930bdf6ee295 https://mypy-play.net/?mypy=1.5.1&python=3.11&flags=strict&g...
- tomp 3y agoThis looks like plain JavaScript. What does TypeScript add in this scenario? Does the example above even pass typecheck?
- usea 3y agoYep. It's javascript, not typescript. Moreover, I think the post is only trying to convey that they like dynamic typing.
- sisypheanblithe 3y ago>>> Are there other languages that do this so nicely? It's the perfect blend of OO and functional. Everything is an object in Ruby as well
- hazebooth 3y agoI could be wrong, but can't Common Lisp do this?
- andyjohnson0 3y ago> 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. [MyAttrib(foo=42, bar="baz")] class MyClass { }
- feoren 3y agoI 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!
- thrashh 3y agoTypeScript is really nice but you're really describing a JavaScript feature to be honest. Here's some other languages that support objects as functions (although not necessarily functions as objects): https://en.wikipedia.org/wiki/Function_object https://en.wikipedia.org/wiki/Function_object
- gumballindie 3y agoI suspect many ts programmer dont even know it compiles into js. Potentially dont even know what js is. Such is the state of modern web dev.
- thefaux 3y agoThis blend of functional and oo programming was pioneered by scala.
- hocuspocus 3y agoScala also supports structural typing, even though it's a bit clunky and against its nature. And advanced typelevel shenanigans you see in TS usually have their counterpart in Scala, especially in Scala 3's meta-programming features.
- Zambyte 3y agoLisp of course supported both decades earlier though :)
- thefaux 3y agoSure. 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.
- koito17 3y agoThe 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.
- d13 3y agoWasn’t it Scheme?
- CharlieDigital 3y agoThe 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"); For the curious, I have a small repo that shows just how similar JavaScript, TypeScript, and C# are: https://github.com/CharlieDigital/js-ts-csharp https://github.com/CharlieDigital/js-ts-csharp Screen grab of the same logic in JS/TS/C# showing the congruency: https://github.com/CharlieDigital/js-ts-csharp/blob/main/js-ts-csharp.png https://github.com/CharlieDigital/js-ts-csharp/blob/main/js-...
- nightpool 3y agothis 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?
- CharlieDigital 3y agoIt can. .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"); }); https://dotnetfiddle.net/szxb9F https://dotnetfiddle.net/szxb9F 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"); }); https://dotnetfiddle.net/oPK1d8 https://dotnetfiddle.net/oPK1d8 Alternatively, use anonymous types: 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); }); https://dotnetfiddle.net/KyBtgZ https://dotnetfiddle.net/KyBtgZ The next release of C# (12) will include named tuple types: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/using-alias-types#summary https://learn.microsoft.com/en-us/dotnet/csharp/language-ref... 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-0/ 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.
- dgb23 3y agoIn 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.
- notnmeyer 3y agogive 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. oh and burn npm to the ground please.
- asddubs 3y agoI 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.
- pull_my_finger 3y ago> 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.
- akkad33 3y agoThis is a Javascript feature, not really a typescript thing. You can do the same thing with python
- diegof79 3y agoIf 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.
- paulddraper 3y ago> Are there other languages that do this so nicely? It's the perfect blend of OO and functional. Python compressor = lambda x: x / 2 compressor(5) helpful_compressor = lambda x: x / 2 helpful_compressor.help = "compressor" helpful_compressor(5) helpful_compressor.help Scala val compressor = (x: double) => x / 2 compressor(5) object helpfulCompressor { def apply(x: double) = x / 2 val help = "compressor" } helpfulCompressor(5) helpfulCompressor.help Ruby also has .call...but I can't remember it well enough
- deleted 3y ago[deleted]
- aabbcc1241 3y agothe flexibility you describe seems to be coming from lisp (schema) that javascript was designed on top of initially