3 ms·
A related advantage is expression-orientated code. In ML languages, code forms a beautiful tree structure where you can see the binding definition of each bind
by substation13 5y ago
A related advantage is expression-orientated code.
In ML languages, code forms a beautiful tree structure where you can see the binding definition of each binding by looking down and right.
let x =
something
really
big
and
complicated
whereas in a language built around statements it will be scattered all over:
let x = null;
x = something();
x = somethingElse(x);
x = anotherThing(x);
In the first case the definition of x is in one indented block; you can read it and move on.
- tarsinge 5y agoI just did some PHP the other day and I stumbled on a structure I was happy to have forgotten: something(x, result); somethingElse(result, result2); anotherThing(result2, result3); with the occasional surprise mutation when you just have to do something(x) and x contains the result. I also got caught recently with MomentJS with that one when doing mydate.add and discovering it also mutated the original instead of just returning the result. I'm a very average programmer and far from a FP purist (I mostly use JS and Rails) and I'm surprised how much I now use some FP principles and how it feels very natural to me.
- pintxo 5y agoYou are free to structure it differently: let x = anotherThing( somethingElse( something() ) );
- ziml77 5y agoThat's worse because now you have to find the innermost function and then walk back outwards. It gets especially ugly when the calls require other parameters let x = anotherThing( somethingElse( something( x, y, z ), 1, 2, 3 ), "a", "b", "c" ); Or maybe this looks cleaner? let x = anotherThing( somethingElse( something(x, y, z), 1, 2, 3 ), "a", "b", "c" ); Neither is easy to follow.
- pintxo 5y agoI take you are no big fan of Lisp? /s Well, actually I agree with you - sometimes. I have actually written helper functions to get similar pipeline processing capabilities in js. function pipe(...args) { return args.reduce((x, fn) => fn(x), null); } pipe( () => 2, (x) => 7*x, console.log )
- ziml77 5y agoYou can get something similar via method chaining: x.something().somethingElse().anotherThing() C# extension methods aren't the perfect solution for this, but I love that they exist to at least make chaining possible without needing to modify the class that I'm applying the function to. D has an even better version of this called UFCS which makes it so `Bar something(Foo f)` can either be called as `something(x)` or `x.something()` without needing any special annotations like C# requires.
- substation13 5y agoIn F# (the most widely used ML?) expressions can be much more complicated than that, incorporating loops, conditionals etc. To illustrate: let x = let mutable maxScore = 0 for item in items do if item.Score > maxScore then maxScore <- item.Score maxScore Compared to: const x = (() => { let maxScore = 0; for (const item of items) { if (item.score > maxScore) { maxScore = item.Score; } } return maxScore; })(); Not necessarily the best way to write this (you would probably use a sequence library) but hopefully conveys the idea. Extension methods are definitely useful in OOP languages. However, a much cleaner language design is to remove the need for a "class" altogether and just have free-functions, function composition and a pipe operator. None of this is something you can't simulate in OOP / procedural languages, it's just much more clunky.