6 ms·
Functional calisthenics
- maxxxxx 9y agoIsn't this going a little overboard? I remember when OOP came up, some people made functions illegal. So instead of s = sinx(x) the OOP way was m = new Math() s = m.sin(x) And instead of switch statements you had to derive a new class for each case call a virtual function. Very "OOP" but it led to the OOP equivalent of spaghetti code. Are the functional guys also starting to leave the real world for the sake of being "pure"?
- Veen 9y agoI think it's more of an exercise than a stringent philosophy. Looking at these rules, they're designed to be helpful for cultivating functional habits and patterns of thought if you're not used to thinking and coding that way. I wouldn't expect anyone to follow them religiously in production code.
- epidemian 9y agoNitpick, but why m = new Math() s = m.sin(x) would be "the OOP way" instead of x.sin ? Besides this nitpick, i concur with Veen. The post seems to be about exercises/challenges, not FP best practices.
- maxxxxx 9y ago" x.sin " Good point. I guess in C++ numbers aren't objects so you can't do this. I hope they understand that this is just an exercise. I have seen too often these things being elevated into a religion.
- epidemian 9y ago> I have seen too often these things being elevated into a religion. Yeap. I have no hard preference between sin(x) or x.sin, in an OOP context or not, as long as the language is somewhat consistent (e.g., it bothers me that Ruby has some of these functions on the numbers themselves, like 1.23.floor, but others live on Math, like Math.sin(42)).
- munificent 9y ago> it bothers me that Ruby has some of these functions on the numbers themselves, like 1.23.floor, but others live on Math, like Math.sin(42)). Every time I type `len(some_list)` in Python, I die a little inside.
- CuriouslyC 9y agoWhile I agree to an extent, the reason for this is not entirely bad. The len function is basically a contract that the result you get will be an integer. An object could easily have a `.length` property that is non-integer, like a line/interval for instance. Think of this as a clunky response to a lack of type information in the language proper.
- agumonkey 9y agoit's probably doable as is in Smalltalk, the issue is OOP trying to ontologically categorize everything is impossible; one day we may need operations on numbers we don't know now and we'll have to "import CategoricalHomotopy as ch; ch.tropos(42)"
- dozzie 9y ago> Nitpick, but why "m = new Math() s = m.sin(x)" would be "the OOP way" instead of "x.sin" ? Because you can't do the latter post-hoc. You can't add hyperbolic functions by yourself if your standard library didn't anticipate them. Well, in Ruby you can and it's a standard practice to pollute this way classes at random, and in Python you can as well, though it's frowned upon, but certainly you cannot in C++ or Java.
- munificent 9y agoThe article states: These rules are constraints on how to create your code and are only intended to be applied when doing katas or exercises. These rules are an exercise to kick you out of an OOP mindset, not best practices for writing real code. By that same token, I think it is a good rule when practicing OOP to not allow bare functions. It helps train yourself to think "Is there a logical class that this method belongs to?" In real code, of course, the answer is often "no". But when first learning OOP, it's good to teach yourself to ask that question in the first place. Your code isn't a good example of "the OOP way", though. A fluent OOP solution would be: s = x.sin() Another good rule when practicing OOP would be "No classes without state." That helps you avoid the habit of writing pointless "namespace" classes like the "Math" class you define here. Of course, again, sometimes it is useful to wrap a bunch of related behavior in a stateless class, but that should be the exception, not the default.
- michaelg7x 9y agoIn Java static methods are a particularly simple way making clear to the compiler and runtime that a given functions does not have side effects (to instances of the owning class). I can imagine that, as a pattern, they would assist escape analysis.
- maxxxxx 9y agoIn C# you can have static classes. I often use these to group simple functions together.
- lederhosen 9y agoA static method can of course mutate an object of the same class (give the object as an argument (direct or indirect) or use a static variable of the class) I can not see how they could be used to help escape analysis more than that you have one less reference (this).
- michaelg7x 9y agoYes, of course that's possible. I was trying to point out their usefulness to the programmer as well as potentially to the runtime/JIT. Next time I'll list the obvious exceptions!
- nextos 9y agosinx(x) can be still object-oriented. Actually, really neat object oriented systems which implement multiple dispatch, like CLOS, tend to use this notation.
- MiyamotoAkira 9y agoAuthor here. A few have mentioned already, but this is to practice, to force you to understand what FP brings to the table, and lead you in path of discovery. When it comes to production code, you apply what you have learned, and make the decisions that you think more convenient. As an example, if you want certain performance gains on F# you do go to mutable state (Phil Trelford did a talk about it). Doesn't matter how pretty/OO/pure is the code if your application doesn't do what is supposed to ;-)
- maxxxxx 9y agoCool. I commend you for having a team that is willing to practice new things for fun. That's pretty rare.
- pygy_ 9y agoThanks for the article. There's a class of problems that I wouldn't know how to solve using the constrains you propose. Given { a: { b: { c: 4 } }, d: 2 } return { 'a.b.c': 4, d: 2 } My go to solution uses mutable state, self-recursion, and intermediate variables: function flatten(source) { var result = {} _flatten(source, null, result) return result } where `_flatten(source, prefix, result)` is self-recursive (`prefix` accumulates the keys) and populates `result[prefix]=value` when value is a string. `result` is conceptually an IO monad that's passed along. I guess the general problem would be traverse a tree of arbitrary depth and accumulate structured data on the go. Prominent examples would be compilers. Any hints on where to start?
- crimsonalucard 9y agoNo. Almost every functional language forces you to follow these "calisthenics." These look more like guidelines to follow if you want to execute the functional style in an imperative language.
- MiyamotoAkira 9y agoI would say that only Haskell (and derivatives) force you to follow most of these rules. Neither Clojure, nor F#, nor Elixir force you into most.
- emsy 9y ago>And instead of switch statements you had to derive a new class for each case call a virtual function. Very "OOP" but it led to the OOP equivalent of spaghetti code. I disagree. Switch statements are fine to react to incoming data, but terrible for everything else. If you add a value to an enum you have to change every place where the enum is used in a switch. And the code inside the switch is not reusable (Unless you make it a method, at which point you're close to the "OOP" way anyway). I agree that religiously avoiding switches lead to spaghetti code, but mindlessly using them does so too.
- CJefferson 9y agoI don't understand "no explicit recursion". How do you do any kind of binary tree search without recursion?
- abecedarius 9y agoThis is in contrast with structural recursion. When you define a binary tree datatype you should get for free a generic fold operation on these trees, analogous to the map and reduce functions on lists. I don't know if Clojure gives you that.
- MiyamotoAkira 9y agoThe people that talked about the rules at Codurance was none of the people that defined the original rules. We had to "reverse engineer" the thinking, find reasons and then discard, change or let as they were (one of the objectives of the write up was to put down those reasons). Explicit recursion did take a while. The reasoning we came with is to learn to use map/reduce functions (do take into account that our group has an ecletic knowdledge of FP and hybrid languages: Clojure, Haskel, Elixir, Scala, F#, ...). Can that binary search tree be converted into a linear search if you rearrange the tree into a list? Yes. Now you can use map /reduce. Is it performant? Most probably not. Are there other ways? Time to discover. Interestingly, most of the katas/exercises on which we are practicing these rules are not heavily algorithmic in nature. Maybe we do need to add some to understand better and modify the "rules" with the new knowledge.
- CJefferson 9y agoIf you want an example, consider writing a simple recursive sudoku solver, where at each location you fill in one empty square with the values 1-9, and reject early if you ever have a repeated value in a row, column or box. This is easy to write in C, and I always imagined would be something functional programming would excel at, but it doesn't seem to fit with your rules.
- MiyamotoAkira 9y agoSOLD!!!!! I will have to add this to my kata collection. That also reminds me of the 8-queen problem. The last solution that I created (C#) years ago did use recursion.
- crimsonalucard 9y agoI wouldn't even call these things calisthenics. These are requirements for the programming style to be called "functional." Really if you aren't following <Edit> Most <Edit> of these rules you are not doing functional programming. The best way to learn about functional is not to follow these rules but to use a functional language that basically enforces these rules. Javascript is not a good language to learn about functional programming.
- jw- 9y agoI'm not really sure why you need the requirement of infinite sequences to do functional programming, or the restriction of no intermediate variables. Let bindings seem like a nice thing to have.
- crimsonalucard 9y agoNo you're right. I missed the infinite sequences thing. I'm going to reword the post from "all" to "most"
- MiyamotoAkira 9y agoYou are right. If you don't follow most of those rules, you are not doing pure FP. My interest on them was because I was writing production code that felt procedural. The rules are there so you can practice with purpose. Once you go into production code, you can discard them, but with knowledge that it will not be because your OOP skills take over, but conscious decisions.
- gaius 9y agoI came here expecting an article on CrossFit!