11 ms·
Classes are a way of writing higher order functions
- redPaper 6y agothat's one way of formulating it. another would be: classes are just a set of methods with a common set of parameters (constructur) already applied (partial application)
- flowerlad 6y agoI don't get why so many JavaScript developers thing functional is cool. Functional has been around for 30+ years. OOP won against functional because OOP is easier to understand and leads to more maintainable code. These days people are taking a second look at functional because functional avoids state, which makes it easier to write concurrent code with no locks. (This has suddenly become important because Moore's law is coming to an end, and CPUs are adding more cores instead of making cores faster and faster.) But this advantage of functional is not applicable to JavaScript because there are no locks in JavaScript in any case! Functional programming actually brings some benefits to backend code. To take advantage of the extra cores in a CPU you add threads, but if these threads are continually needing to lock in order to serialize access to shared state then concurrency is diminished. The more cores you have, the more threads you have, and then the more you have to lock. Functional style solves this problem by not having mutable state. But when it comes to JavaScript these benefits don't exist. That's not to say there are no benefits at all to incorporating some functional style into JavaScript code, but wholesale replacement of OOP with functional style is not warranted.
- CardenB 6y agoMutable state is also a readability concern. If you have to model the temporal state of some object in your mind, it is pretty prone to error as you change code.
- wtetzner 6y ago> OOP won against functional because OOP is easier to understand and leads to more maintainable code. You say that as if it's the truth. It's not at all clear to me that's the reason OOP "won". > These days people are taking a second look at functional because functional avoids state, which makes it easier to write concurrent code with no locks. I think that's only a small part of the reason for the interest in functional programming.
- Weebs 6y agoAfter picking up functional programming < 2 years ago and using it constantly at work, I definitely have to say I disagree with GP that OOP is easier to understand. I think there's a lot of value in the concepts of OO's message passing, and exposing an interface that encapsulates some internals. It's a tool I regularly use in FP even with immutable types Imperative OO to me though is plain difficult to do correctly, and shouldn't be the primary way that your encouraged to write code. It's much more difficult to track dependencies and who touches what in an imperative class, and you end up digging around the file before you can even know what you have to keep in your head. With FP, you generally have pure functions that take in some state and dependencies, and produce a new state. Everything you need to know related to how that function behaves is explicitly given to you, and it's trivial to test the code. You can do this in imperative OO too, but you're nudged in that direction by default. I do think there are cases when imperative code and imperative OO make a problem easier to represent/understand and faster to execute, but I like to keep those sections small and easy to fit in your head, delegating most of the logic to functions. The imperative OO is mostly some glue to bring the business logic to life.
- narwally 6y agoOO: 'state is hard, let's make it easier by encapsulating it with only the procedures that need to manipulate it' FP: 'state is hard, let's not do it unless we absolutely have to'
- munificent 6y agoModern multi-paradigm language: "State is hard. Let's avoid it when we can and encapsulate it in procedures when we can't."
- Weebs 6y agoI think for FP it should be added, that the state is explicit and has a clear owner. Eventually someone has to replace the old value with the new value in the call chain What's really interesting is Rust arrived at a similar destination, I assume as a natural byproduct of pushing for memory+thread safety. State isn't discouraged, but you get explicit owner(s) of data who lend it out to others who are readonly by default, and explicit when they will modify the reference you gave them.
- yelloweyes 6y agoLet's be honest. 90% of JS developers out there probably don't even know what a lock is. I know a JS dev who gets paid good money to develop software, who thought the reason you shouldn't use floating-point to deal with currency was because "javascript is bugged".
- skohan 6y agoIt's worth noting that there are drawbacks to functional programming as well. It tends to result in a lot of copying, which is also not optimal for performance.
- ollysb 6y agoThis is somewhat though not entirely solved by https://en.wikipedia.org/wiki/Persistent_data_structure https://en.wikipedia.org/wiki/Persistent_data_structure
- lukevp 6y agoFunctional programming is getting popular in JavaScript not because of serializability of state changes across threads, but because immutable state and immediate-mode GUIs go hand in hand. React, as an example, works as an immediate-mode GUI (re-rendering VDOM on every state change) on top of a retained-mode GUI (the DOM). You manipulate your state using reducer functions and the library works behind the scenes to convert that to a UI. It alleviates all of the weirdness and performance issues of interacting with the DOM when state changes. GUI programming is traditionally retained-mode. OOP is traditionally state encapsulation and mutability. OOP and retained-mode GUIs are conceptually similar, and Functional Programming and immediate-mode GUIs are conceptually similar. Here's a good explanation of this: https://medium.com/@puppybits/immediate-mode-reactjs-7a6302cd89cd https://medium.com/@puppybits/immediate-mode-reactjs-7a6302c...
- pjmlp 6y agoNot all OOP languages support inheritance, nor all FP languages are immutable by default, though. GUI programming surely wasn't retained-mode on 16 bit home computers, in fact that was one of the issues getting used to GUI based OSes event queues back in the day.
- flowerlad 6y agoI don't see parallels between OOP and retained mode, and functional and immediate mode the way you are describing. UI components inherently have state. Pretending they can programmed in functional style with stateless functions is bizarre because UI functions do have side effects. See prior discussion: https://news.ycombinator.com/item?id=24363261 https://news.ycombinator.com/item?id=24363261
- lukevp 6y agoYes, ui components have state in the case of a retained mode GUI like the DOM, but in the case of React, relevant data is derived from the application state (eg IsDarkMode is a state property used to derive colors for a component, or the checked state of a checkbox) and a unidirectional flow propagates that state change to the VDOM on every re render. Functional doesn’t mean stateless (state exists as closures within higher order functions, such as in the composition of functional components within React), it means side-effect-free, it means determinism in the derivation of additional state based on immutable state updates.
- arcosdev 6y agoI would agree with you if JS wasn't JS. It is a terrible language to write OOP-style code in and a much better one to write functional-style code in. It is inherently a more functional language.
- azangru 6y ago> because OOP is easier to understand and leads to more maintainable code How is OOP easier to understand exactly? If you are debugging the code that looks something like this: class Child extends Parent { launchRockets() { this.launch(this.rockets); } render() { this.score = this.getScore(); return `<span>${this.score}</span>`; } } How do you know which part of your code contains implementation of this.getScore method? How do you know which part of your code is responsible for the number of this.rockets and how many methods in how many classes can change it? How do you find out which part of your code is going to call the launchRockets method, and whether this will even be this class's method or one of its children's? How many hops across your codebase do you need to make to figure all this stuff out? How is this style any simpler than functional?
- bitL 6y agoYou have IDE for that in general. If you are writing all code in Sublime, then yeah, you have a problem.
- symlinkk 6y agoIf you need an external tool to understand code it’s an indication that something is wrong.
- bitL 6y agoIs that? If you don't write all your code with `cat`, is that an indication that something is wrong as well?
- ragnese 6y agoI somewhat disagree. This will be true for pretty much any non-trivial code project. I'd love to see a language where I didn't need tools to understand what's happening, but that's impossible currently. Do you really never need to see what a passed-in function actually does in your FP code? Never need to see what type an argument is in our fancy Haskell or Hindley-Milner-typed language?
- deleted 6y ago[deleted]
- allenu 6y agoI think it's debatable that OOP is "easier to understand and leads to more maintainable code". It depends on size of project and experience of developers. There are still certainly benefits to using FP in JavaScript from a design, readability, maintainability sense, even if you are not concerned with concurrent code with no locks. A good FP design can be easier to reason about than an OOP graph of objects.
- Zak 6y ago> OOP won against functional because OOP is easier to understand and leads to more maintainable code. OOP as we know it today, which is not what the inventor of the term had in mind, likely won because it was easy to add it to C in a manner compatible with low-level optimizations that were critical in application code at the time, and less so now. I dispute that one style is inherently easier to understand than the other, though each has certain problem domains where it maps particularly cleanly.
- chriswarbo 6y agoSlightly tangential, but Single Assignment C is well suited to functional programming
- loup-vaillant 6y ago> OOP won against functional because OOP is easier to understand and leads to more maintainable code. A couple points: I'm not sure whether OOP really won: pretty much any language now has taken lambdas & higher-order functions from FP; before those parametric polymorphism (ML was there before C++ templates and Java generics); many sources will tell you to limit mutable state; and even algebraic data types are starting to get mainstream traction (Rust). Speaking of which, I believe Rust does not sell itself as an OOP language? It is not at all obvious that OOP is easier to understand. Those animal/shape hierarchies are indeed very easy to grasp, and they are also useless. Even games have moved away from class hierarchies, which are both slow and too inflexible. They're more about ECS/data orientation now. I'm not sure that "easier to understand" does lead to more maintainable code either. If you're talking about daily use, why not. If this is a learning curve thing (and in practice I suspect it is), then I think ease of learning and code quality won't correlate with each other — provided only people who learned the stuff get to program with it of course.
- pintxo 6y ago> Those animal/shape hierarchies are indeed very easy to grasp, and they are also useless. That's a wonderful description.
- narwally 6y agoAs someone that had some background in cladistics, I knew OOP was gonna get real complicated as soon as I saw animal hierarchies as a 'simple' example.
- TimTheTinker 6y ago> OOP won against functional because OOP is easier to understand and leads to more maintainable code. Actually, OOP won because OOP is easier for non-developers to understand -- those who were making policy and purchasing decisions back in the 1990's. And the OOP we now call "OOP" (as realized in languages like Java and C++) is not at all the OOP that was first envisioned by Alan Kay and implemented in languages like Simula and SmallTalk.
- goto11 6y agoOOP won out over imperative because it fits very well to (desktop) GUI programming - UI controls maps elegantly to objects with state. OO turned out to be broadly useful, but GUI programming was definitely the driver. I don't think anybody had compared maintainability between similar projects in OO and functional style. Functional was not really a contender in this space. Almost everybody who adopted OO migrated from imperative. The popular OO languages were imperative languages adapted to OOP - Visual Basic, C++, Delphi etc.
- danielmain 6y agoHow no locks in JavaScript? Have you heard about async await?
- jk700 6y ago> OOP won against functional because OOP is easier to understand and leads to more maintainable code. If by functional you mean higher order as in the title and not what is commonly known as FP - it's never been true. For example, a function that has no outside side effect and that calls an anonymous function provided as an argument is way easier to understand, reason about and maintain than its OOP equivalent of creating an object and calling three methods in specific order mixed with your code. > These days people are taking a second look at functional because functional avoids state, which makes it easier to write concurrent code with no locks. Avoiding state doesn't make it easier to concurrently touch shared memory without locks. Event loop style concurrency simply doesn't touch shared memory concurrently, so it doesn't need locks and doesn't have all those multithreading problems. It can still mutate state left and right, higher order programming just allows to map intent better to the code, making "sequences look sequential" and all that. Actually solving problems concurrently with high performance today generally requires ditching shared memory based synchronously communicating concurrency models, leaving async communications of isolated entities where locking problems and overhead don't exist. But it has nothing to do with functional programming, OOP, higher order programming.
- munificent 6y ago> I don't get why so many JavaScript developers thing functional is cool. Let me make a meta-point. It's natural to find yourself with a thought of this form: I don't get why so many think ___ is cool. There are two answers to this: 1. None of them know something you know, and you are right. 2. At least some of them know something you don't, and you are wrong. Statistically speaking, 2 is much more likely to be true. So when you find yourself having these kinds of thoughts, which are entirely natural, you'll make better progress if you can formulate it as a good-faith search for truth and approach it with an open mind.
- yelloweyes 6y ago>Let me make a meta-point. It's natural to find yourself with a thought of this form: I don't get why so many think ___ is cool. I don't get why so many think cigarettes are cool. You're telling me they know something I don't about cigarettes?
- TimTheTinker 6y agoMany/most who have a lifelong cigarette addiction would not describe them as “cool”.
- munificent 6y agoYes, actually. * Cigarettes are stimulants and feel great when they first kick in, like a good cup of coffee. * Smoking gives you a culturally-approved reason to take breaks throughout the day and go outside. * Smoking is a social activity that provides an easy way to meet new people. Of course, it is strongly chemically addictive too with serious negative health consequences, but the reason people smoke isn't only addiction or no one would ever start. I learned the above list by once genuinely asking someone why they smoke.
- deleted 6y ago[deleted]
- pjmlp 6y agoAnother thing that people also miss all the time is that not all FP languages are immutable by default, most FP that achieve a certain level of mainstream adoption also have OOP like constructs, finally even Smalltalk-80 already provided the mechanisms to do LINQ like coding and passing closures around.
- dragonwriter 6y ago> Functional has been around for 30+ years. 60+, and OOP has been around for 50+. > OOP won against functional because OOP is easier to understand and leads to more maintainable code. OOP didn't “win”, it had a period of intense popularity starting in the late 80s and starting to fade around the mid-00s. And that was largely path dependence and building on C; had someone put the kind of effort into C-with-lambdas instead of Stroustroup's C-with-objects, we might have had a couple decades where the most popular languages were all C-descended impure functional programming languages rather than C-descended class-based OOP languages.
- didibus 6y agoFunctional is making a comeback, because the performance is now good enough. That's my personal assessment. It never really "lost" to OOP, it just wasn't ever a real contender before, because of performance reasons. With the revival of dynamic languages though, people heavily programming in JS, Ruby, Python, etc. Performance is no longer a concern of people. So now people are motivated by what makes the act of programming as convenient to the programmer. When it comes to that, it turns out a lot from FP does make things simpler for the programmer. It started with data transformation, I think helped by the appearance of LINQ in C#, which first exposed people to functional data transformation. Then Java got Streams, and everyone kind of followed suit in introducing some higher order functions and map/reduce/filter and all that. Also: callbacks. As asynchronous behavior became more popular, there was a real need to lower the amount of verbosity and just code you needed to type to program in an async style. So creating a class and an object for every little event in your app, to pass as callbacks to all async functions just got super annoying. So people introduced anonymous functions and lambdas, and added support for higher order functions that could be used as callbacks. Once you've got callbacks and higher order functions, closures are a natural extension, you often want to pass a callback that will set some state elsewhere when done or other such things. So Closures were also added. And here we are, with a full FP toolkit due to the fact that performance isn't a concern anymore and async programming is more convenient when your language has support for first class functions with closures and anonymous functions and lambdas. At the same time, like you said, people started squeezing more out of their cores and their seperate IO controllers, so concurrent and parallel programming became more common. That forced people to look at the immutable characteristics of some FP langs, and so support for immutability was also added to a lot of language. Once you're in the realm of immutability, Objects also become less useful, since there's no need to encapsulate and protect modifications to something you can't modify anyway. Thus once again, creating a class and a constructor and all that just becomes unneeded verbosity and extra code to write. So people wanted ways to go back to immutable value records and just write functions over them, etc. All these things in my opinion combined for what is today the "resurgence" of FP.
- hashkb 6y agoSee also: Let Over Lambda for more on this.
- tines 6y ago+1. "Objects are a poor man's closure, and closures are a poor man's object."
- azangru 6y agoPoor poor man...
- srean 6y agoI understand the amusement value of this quote, but I never quite understood what is so poor about a tuple of functions closed over same data into a closure. Apart from inheritance, what does it lose. In fact its good that it loses inheritance. Syntax of many OOP languages makes it way too easy to use inheritance -- common things ought to be easy and potential design mistakes hard.
- Izkata 6y ago> but I never quite understood what is so poor about a tuple of functions closed over same data into a closure. Apart from inheritance, what does it lose. In the closure-based version, getFullName and setFirstName are defined anew for each instance of Person, using more memory each time. Typically isn't an issue unless you're creating a massive number of them, but the class-based version doesn't have this problem. Also, just to throw it in the mix as well, the class-based version I believe is largely just syntactic sugar over the traditional way of creating objects in javascript, using prototype-based methods: function Person(firstName, lastName) { this.fName = firstName; this.lName = lastName; return this; } Person.prototype.getFullName = function() { return this.fName + ' ' + this.lName; } Person.prototype.setFirstName = function(firstName) { this.fName = firstName } const person = new Person("Ben", "Bitdiddle") person.getFullName() Prototypical inheritance is interesting in that an instance of Person could be used as the class for another object, ad infinitum.
- bitL 6y agoHigher order functions are just a fancy way of writing classes.
- drchopchop 6y agoA class, generally speaking, is a binding of functions and data. In the functional example, where does Person store the fName and lName variables? Is there an intrinsic/hidden 'this' pointer? If functions persist data, are they really classified as functions anymore? The message passing part isn't very relevant, as the concept of calling a function pointer via string lookup is present in many dynamic languages (Ruby, etc).
- ovis 6y ago> In the functional example, where does Person store the fName and lName variables? With a closure? And you get state hiding too. > If functions persist data, are they really classified as functions anymore? Sure, since otherwise returning a function from a function wouldn't be very interesting.
- crdrost 6y agoNo this is a really important question, and it has an answer. They are called closures. They are "environments" in which variables live, each of which can have a parent environment dictated by whatever rules there are. (Some programming languages have it where every use of curly braces creates a sub-environment; in JavaScript originally only the function keyword would introduce a sub-environment and if you write `if (something) { var x = 123; }` then `x` belongs to the parent closure and it might contain `undefined` if the branch was not triggered. Since then JS has introduced let/const which are scoped to curly brackets.) When you declare a function it still has access to the full space of variables which it was lexically defined with. If you mutate those with one function then another function which has access to that environment can see those changes, etc.
- drchopchop 6y agoGood point, I guess what I was trying to get at is that an object is more equivalent to a closure (function + data i.e. local stack environment) than just a function (which executes with input parameters). The top-voted response put that more eloquently than I did.
- lukevp 6y ago
- saagarjha 6y agoWell, somewhat. Passing messages is inherently more dynamic, as you can see: you can change the string to anything at runtime, even something that doesn't exist, you can intercept the message and send it somewhere else or handle it however you wish. Static dispatch doesn't let you do this, but you generally get the advantage of faster calls and better compile-time diagnostics. In simple cases, the programming style can be similar, but for more advanced features they really differ in the kinds of things they support.
- munchbunny 6y agoIt's a cool idea, and I'm sure everyone gets a pleasant jolt when they make the connection (I sure did), but you probably already understood this idea at some subconscious or conscious level in order to get anything nontrivial done with a functional language. It's one of the first things you're taught if you are learning a functional-at-its-roots language. Not JavaScript, which is mixed-paradigm at its roots. For me it was Scheme in college. If you're learning a functional language, combining closures and functions that return functions to carry around "instance fields" and "instance methods" is what you learn right after you've gotten over the basics like map/filter and tail recursion. The equivalence of this pattern to classes is something you'll understand intuitively, even if you haven't put words to it yet.
- commandlinefan 6y ago> Not JavaScript Funny enough, I actually had the referenced epiphany while learning JavaScript, since this idiom is so common in it.
- munchbunny 6y agoIs it actually that common? Using a higher order function that returns member fields/methods which you then actually call? Higher order functions are common in JavaScript, and closures are as well, but I don't recall seeing them used as substitutes for classes or vice versa for years at this point. With JavaScript you don't have to use closures and higher order functions to do modularization because you have classes, and you don't have to use the functor pattern (like Java before Java 8) because you have higher order functions. ES5's syntax for object types looked a lot more like "closures with syntax sugar" than ES6 and later JavaScript syntax with explicit class declarations.
- d13 6y agoE6 “classes” are just functions that return objects.
- Weebs 6y agoIt's probably not common, but for me the answer lies here: > I don't recall seeing them used as substitutes for classes or vice versa for years at this point I had a fairly large school project developing a JavaScript application back before ES6, and kept searching for a better way to structure my code because I found it awkward using prototypes + binding this for events. Eventually I found a hidden golden nugget of a tutorial that showed me how to do it with closures and I was in awe that it somehow wasn't the norm (and obviously how cool it was to build objects out of functions!)
- aaron-santos 6y agoTangentially related is the idea that some OO constructs can be represented as co-data. Previous discussion here https://news.ycombinator.com/item?id=24105755 https://news.ycombinator.com/item?id=24105755
- goto11 6y agoObjects are just a poor mans closure. Closures are just a poor mans object. (Not sure where this quote is from originally? Maybe here: http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...)
- Someone 6y agoThere also is the classical example of defining cons, car, and cdr as closures in SICP: (define (cons x y) (lambda (m) (m x y))) (define (car z) (z (lambda (p q) p))) (define (cdr z) (z (lambda (p q) q))) Here, cons, instead of returning an object with two values, returns a closure that captures the two values. car and cdr call that closure, passing it another closure. The cons closure calls that closure, passing the the two values. Explanation at https://stackoverflow.com/questions/21769348/use-of-lambda-for-cons-car-cdr-definition-in-sicp https://stackoverflow.com/questions/21769348/use-of-lambda-f....
- chriswarbo 6y agohttps://en.wikipedia.org/wiki/Church_encoding https://en.wikipedia.org/wiki/Church_encoding
- Joker_vD 6y agoAlso, https://en.wikipedia.org/wiki/Scott_encoding https://en.wikipedia.org/wiki/Scott_encoding Of course, it completely kills (or "stress-tests", if you will) the branch predictor in your CPU.
- avmich 6y agoI like this explanation - https://stevelosh.com/blog/2013/03/list-out-of-lambda/ https://stevelosh.com/blog/2013/03/list-out-of-lambda/ .
- mikorym 6y agoClosures in which sense here? Like closure operators?
- lolive 6y agoClasses is a semantic way of organizing your zillion of higher order functions, that would otherwise be just floating around. Beans is the way to build higher order data. Mixing them with care is the art of programming.
- Joker_vD 6y agoGod forbid the 'sin' function would be just floating around, outside of a static 'Math' class. Or 'reduce', for that matter—obviously you need a special 'Reducer' class for that. That reminds me of Steve Yegge's "Execution in the Kingdom of Nouns", actually.
- pjmlp 6y agoClasses are extensible modules. Although some people seem to like pre-history languages without modules that require workarounds like using prefixes everywhere.
- vblmark 6y agoA math module or namespace would be nice. Also for C headers I'd like to do "from math.h import inf" to resolve conflicts with <cmath> and c++11 with non-compliant implementations.
- kazinator 6y ago> Yup, really. Classes are just higher order functions, which accept arguments (constructor) and return a list of functions that can manipulate those arguments (methods). Nope, really they are not. Rather, objects are run-time, publicly accessible environments (bindings of names to slots). Since functions also have environments, we can come up with some contrived use cases whereby a rigorously encapsulated OOP example revolving around a class that is sealed against extension and uses no inheritance or other OOP mechanisms is nicely mimiced by a vector of functions that share the same environment. But imitating some facets of a thing is not being that thing. A dog standing on hind legs isn't a man. A more fully featured OOP can be built up, which yet keeps using lexical variables for slots. Public access to slots by name can be implemented by having a standard function supported at the same position in the function table, which takes a symbol argument, and switches on it to map it to a variable in its environment. Providing possible implementation mechanisms for thing is also not being that thing. We can easily reverse this, too, and claim that functions are just objects. Compilers for lexical closures do things like flatten the environment into an array in which items are accessed by position. So, first class functions are, under the hood, nothing but code, plus an array, in machine language. And since data structures are also some code offsetting into arrays, it's all the same. You know, the moment I saw a NAND gate schematic, my mind opened to an amazing revelation: we don't need both zeros and ones, just zeros and NAND gates! The number one is nothing but a zero, duplicated into the two inputs of a NAND gate.
- ookdatnog 6y ago> You know, the moment I saw a NAND gate schematic, my mind opened to an amazing revelation: we don't need both zeros and ones, just zeros and NAND gates! The number one is nothing but a zero, duplicated into the two inputs of a NAND gate. This is not as absurd as you think it is. The most commonly used definition of the natural numbers (https://en.wikipedia.org/wiki/Peano_axioms#Formulation https://en.wikipedia.org/wiki/Peano_axioms#Formulation) is structured in a way similar to your description: we have a single explicitly declared natural number (zero), and all other natural numbers are defined as a sequence of function applications on zero (ie 4 is defined as S(S(S(S(zero))))). Our notation for numbers is just "syntactic sugar" on top of this representation: useful when doing practical work with numbers, but only a hindrance when you're studying the concept of "number" itself. Analogously, in programming language theory (studying the properties of programming languages moreso than trying to do program things in them), we often prove properties of languages by studying extremely frugal, tiny languages (often called "calculi"), that only have the absolute minimum primitive constructs. Realizing that a construct can be expressed as "syntactic sugar" on top of a simpler one (objects on top of closures, for instance), is useful in this setting because it reduces the number of cases in proofs.
- ed25519FUUU 6y agoMaybe I'm confused by this particular example, but what languages would allow functions to store the value of the constructor like that without closures? The thing I don't like about closures is that they can get messed up easily, and not even all OO languages support them. I often think of functions as intentionally "stateless", NOT persisting any value. For that a class is used, if anything for safety.
- Izkata 6y ago> Maybe I'm confused by this particular example, but what languages would allow functions to store the value of the constructor like that without closures? It's javascript, where all functions are closures.
- choeger 6y agoActually, the more precise formulation would be: classes are related to functions as both are abstractions. The author conveniently leaves out the differences: classes yield objects, whereas functions yield any kind of value. Classes are second-class citizens, whereas functions (usually) are first-class. Functions have a type, whereas classes (often) are (or are isomorphic to) one.
- greenie_beans 6y agowould any devs want to start a book club for books like this? the article mentions SICP, which is on my to-read list. it just seems so daunting. i'm currently reading practical object oriented design in ruby by sandi metz. would love to have somebody to discuss some books like this.
- mrbonner 6y agoIsn't the function or OOO is just message passing?
- ledauphin 6y agoThis both is and isn't true, and the finer points revolve around mutability/concurrency and implementation inheritance. Modern computers are inherently concurrent and mutable, and these fundamental realities have been shown over and over again to cause hard-to-understand issues in sufficiently complex codebases. In the vast majority of OOP languages, instantiated classes are mutable-by-default and can be mutated by multiple concurrent operators (threads, coroutines, etc). This makes them actually quite similar to the underlying computing model - even though they're a high-level abstraction, they force you to deal with the knowledge that the memory at any address can be modified at any time with no warning. [1] Classes in most languages also offer implementation inheritance, which boils down to an approach to "rebundling" the constituent functions in ways that are arguably simple enough, but generally in practice it has been found to be hard for programmers to reason about. Dynamic dispatch is very powerful, and the way that most inheritance mechanisms expose this seems to be a model that programmers understand in theory but fail to consistently remember in practice. In my opinion, the fundamental difficulty with classes is that in most languages, you get all of these tools bundled (complected) into the same toolbox without any say in the matter. If you didn't want inheritance, too bad. If you didn't want concurrent mutability, you're now being asked to design a complex state machine to prevent it, rather than it being excluded off the bat by most functional approaches. Functional approaches force you to opt in to most of the footguns that are included "out of the box" in OOP. Classes will remain the best approach in many OOP languages for RAII-shaped problems, where some part of an application needs to encapsulate and manage a limited, shared, and potentially mutable resource (such as a file handle, a database connection, or an in-memory cache), because classes are an abstraction that maps cleanly to shared mutable state with locking mechanisms. [1] You can of course also shoot yourself in the foot with closures that themselves contain mutable state (e.g. some kind of mutable container), and I think this is a rarely-considered danger for functional paradigms in languages that default to mutable data structures.
- CodeArtisan 6y agoYou can implement an object-- a data abstraction technique where the said data (the attributes) is hidden behind a set of operations (the interface)-- with classes or high order functions or prototypes but that doesn't mean those are equivalent, especially software engineering wise.
- leafboi 6y agoOk. No. There's a lot of confusion with semantics. Following the most used definition of OOP as defined by JAVA, C++ and C#: Classes are in no way functions in the FP sense because classes mutate state. Classes are in no way functions because calling methods requires the instantiation of unrelated state. Example: class A def __init__(self, a, b, c): self.a = a self.b = b self.c = c self.banana = create_banana(create_gorilla(create_jungle())) def process_a(self): return process(self.a) process_a cannot be used unless a, b, c and the banana plus the gorilla holding the banana and the entire jungle the gorilla lives in is instantiated. Let's talk about terminology. There are three that often get mixed up. Procedure = a set of instructions that could encompass mutating and changing state. Function = a box that when given input produces an output. The box cannot modify the universe. It can only produce something new when given an input. Object = A grouping of a set of procedures and state that can mutate. The javascript programmer OP is mixing up function and procedure. He defines a procedure and says that it can be a class. A procedure can be a class as it's just a set of instructions which can encompass defining scope, state and more procedures restricted to scope and state (aka an object). This thing right here, written by the OP: function setFirstName(firstName) { fName = firstName } Is not a function... it is a method or a procedure that mutates state. A function can do none of the above. That's it.
- bitdizzy 6y agoYou're making an argument from definitions that would exclude ML from being a functional language, which is absurd.
- leafboi 6y agoNo I'm using the mathematical definition of a function. ML is not Math nor is it purely functional. All functional languages need to bend the rules a bit in order to have side effects but in general the person is not writing functions and therefore is not getting the benefits of using functions. FP is about reducing and restricting these functions that break the rules. Defining a class in terms of mutations and side effects is going against the philosophy of FP.
- erikerikson 6y agoReview: Consider replacing: return function(method) { switch (method) { case 'getFullName': return getFullName; case 'setFirstName': return setFirstName; } } With: return { getFullName, setFirstName, } And leaving client code unchanged. This leaves callers with a struct but is cleaner and more direct. So far as I can tell, modules and closure usage are objects in JavaScript. I'm curious if you might have insight into what this approach might block other than exclusively using functions?
- stopachka 6y agoHey erikerikson -- I think your solution is indeed equivalent, and I would say even more idiomatic js. The reason I chose the first option, was to really bring home the point that all of this could be expressed with closures.
- didibus 6y agoYou shouldn't analyse programming constructs based on their implementation in my opinion. Go down deep enough in the rabbit whole, and you'll see everything is just a Turing machine. A more useful analysis in my opinion is to focus on the developer ergonomics of the language. That includes how the construct is used or can be used, any properties it exhibits or guarantees, what it prevents you from doing, etc. From that lens, the ergonomics of a Class and a Function Closure are not at all the same.
- knubie 6y agoOOP is an abstraction over functions, but it can be explained much more simply without closures. Classes introduce a convenient syntax / abstraction over polymorphism, and can be written with plain functions without the need for closures. Methods are just polymorphic functions that accept an implicit "self" argument, which is used to dispatch. Taking the example from the article: function Person(firstName, lastName) { { type: "Person", fName: firstName, lName: lastName } } function getFullName(this) { switch(this.type) { case "Person": return this.fName + ' ' + this.lName; default: throw("TypeError getFullName is undefined for " + this.type ".") } } function setFirstName(this, firstName) { switch(this.type) { case "Person": this.fName = firstName; break; default: throw("TypeError setFirstName is undefined for " + this.type ".") } } From here whenever a "method" is called, the program inspects the "type" attribute (this process is abstracted and hidden in OOP languages), and uses that to invoke the correct implementation of the method. let person = Person("John", "Foo"); getFullName(person);
- duutfhhh 6y agoI see classes as nodes in the power grid that transmits and transforms data instead of electricity. These nodes are wired up to other nodes via methods - their binding points. To be precise, a class is more like a spec that can be used to build an instance of that node. In some cases there's only one instance. The main advantage of classes, I think, is that they define one underlying principle for all these data transformer nodes: that each node has a few binding points with certain interface: named methods. The nodes don't have to look this way, but restricting the freedom of design choices to this pattern makes it easier to compose many nodes together. It's like setting the expectation that all cars are squarish: it's easier to build the infra around cars when you know how they can look in general.