17 ms·
Write code. Not too much. Mostly functions.
- 83457 6y agoAnd functions should be no bigger than your head.
- warent 6y agoWell this explains why the most difficult people to work with always write the most disgustingly large god functions
- 83457 6y agoBecause they have big heads?
- modeless 6y agoThis is my least favorite programming advice. Splitting functions for no reason other than "it's too long" is a bad practice. https://news.ycombinator.com/item?id=25263488 https://news.ycombinator.com/item?id=25263488
- tenaciousDaniel 6y agoMost of the illegible code I've ever written was because I kept splitting functions up, thinking I was following good practice, but was really just making emotional/aesthetic decisions. When you go back to edit your code, it's like calling a 1-800 number and getting re-routed to 15 different departments to finally find the person you need to talk to.
- abernard1 6y agoI really agree with this. I see this all the time with "business rules" problems. If you have a situation where you can't make levelled abstractions, you've got some thorny interconnections in your logic (and those may be fundamental!!!). The way I handle this is with a "gauntlet pattern." You still can split your logic up into parts, but you just do it by "rejecting" certain logical chunks at a time within a function and comment above each state of the gauntlet. It looks something like this: // marketing told me we should never do this ever under any circumstances if (!a) { return CASE_1; } // if the user enrolled like this, we should check if there's this bad property about that user if (!b) { return CASE_2; } // oh, you crazy people in finance if (c > 4 && d < 0) { return CASE_3; } return CASE_4; The key thing is not to get hung up on duplication or having exact control flow like how the business thinks about it. You want 1) to return the largest percentage or simplest cases out first, 2) keep everything flat as possible without layers of branching, and 3) be able to associate tests to each one of those early-reject lines. The nice thing about this is the reduction of cognitive load. By the time you get to the 3rd case or whatever, your mind can already know certain things are true. It makes reasoning about rules much easier in my experience.
- m12k 6y agoI think the trick is to split it the eight way. If you do it the wrong way, you end up with functions becoming layer upon layer of indirection that feels like a rabbit hole you need to dive further and further into to understand what is actually going on. But if instead you keep the core control flow in the original function, but move sensibly named chunks of into into helper functions - that way the original function ends up reading like pseudo-code, and you don't really need to actually look at any of the helper functions to understand what is going on.
- modeless 6y agoThe "right" way to split, as advocated in the article I linked, is not based on naming but based on state. Have all your split out functions be pure. Retain all the state mutation in one place where you can keep an eye on it.
- cma 6y agoUse an editor with folding and this is just unnecessary indirection and jumping around to read a linear series of steps that happen one after another. If a function is only called in one place, it should rarely be a function. It may help a small amount when viewing a stack trace, so you can tell at a glance without looking up the line and jumping to the section, if that is a big enough advantage, can make it a lambda and leave it all in place as long as the debugger/stack trace will list the lambda assignment name.
- luhn 6y agoThis is the first time I've heard "functions should be no bigger than your head" and I actually really like it. Rather than prescribing some arbitrary function length, it highlights that functions are meant to be understood and the appropriate length should come from that.
- lmm 6y agoThat advice should be understood in the same way as "sentences should be no longer than a few dozen words" or "paragraphs should be no longer than a few lines". Of course adding a line break at an arbitrary point in a long paragraph doesn't make it better. But a solid wall of text is a red flag.
- the_cat_kittles 6y agoive developed a defacto rule of thumb / intuition that a function size should basically be as big as it can be without being inconvenient to test. which means that its pretty small most of the time
- jim-jim-jim 6y agoI thought I hated small functions too, til I realized I just hated scrolling. The moment it's off my screen, it's out of my head. Assuming you just need helper functions for f(), compare int f(int x) { return g(h(x)); } int g(int x) { return ... } int h(int x) { return ... } to f = g . h where g = ... h = ... Turns out my language choice was the problem, not over-abstraction. If you can fit it all on one screen, then have at it tbh.
- modeless 6y agoThe logical conclusion of this line of thinking is APL or K. Some people swear by it. I admit terseness is appealing for solo coding, and I loathe the bloat of Java etc, but when taken to the extreme terseness starts being a problem for collaboration.
- jim-jim-jim 6y agoI think you're right about those langs and collaboration; all the procedures are right there in front of you, but you gotta keep track of the data shape in your head. Just as much work as scrolling page-spanning functions imo. That's what makes point-free Haskell such a sweet spot for me: terse, symbollic control flow and nice combinators, but types to guide you. I try to use it as a better J.
- dathinab 6y agoI can't really agree to this. Through without questions there are always things you should not split out. Splitting a functions which consists of multiple logical steps is most times a good idea because it makes testing much simple and tends to show you where you accidentally had subtle cross-cutting concerns or unintended cross-talk between sub-domains (domains in sense of modelling, not http). What is important is to properly name functions (and if you can't you probably shouldn't write that function). Also it's important to learn how to live with abstraction, i.e. to reason about code wrt. a specific problem without needing to jump into the implementations about every function /method it calls. Through the later point is much easier with a reasonable use-full type system. And with this I mean useful for abstraction without making abstraction to hard and without allowing to many unexpected things. Languages like rust or scala have such a type system (as long as you don't abuse it) but e.g. C++ fails this even through it has a powerful type system. At least this are is opinion. And without question if you can't cleanly split something out, then don't split it out. If you can split it out but not name it reasonable either your understanding it lacking or it should never have been split out.
- chewbacha 6y agoWhat if my cranium is extra large? Do I get to write bigger functions?
- brobinson 6y agoPython's developers must have enormous heads if that's the case: https://github.com/python/cpython/blob/b8fde8b5418b75d2935d0ff93b20d45d5350f206/Python/ceval.c#L984-L4125 https://github.com/python/cpython/blob/b8fde8b5418b75d2935d0...
- cinntaile 6y agoIt's mostly a giant switch case for opcodes though, that's not really what is meant here.
- rightbyte 6y agoThey could have had inlined functions or macros if they would have wanted to keep the function lenght down for the table.
- rightbyte 6y agoGod I love reading code like that. Makes me feel better about the complex mess I produce thinking if it could be done in a clean way.
- AnimalMuppet 6y agoDon't write functions bigger than your co-workers' heads, either.
- brutusborn 6y agoThe quotes on nutrition remind me of arguments about the health implications of MSG, in that the argument tends to devolve to "is MSG good or bad" rather than "how much MSG is good or bad."
- secondcoming 6y agoWhat is the point of this article? There is nothing new or interesting in it.
- rocqua 6y agoWhat is the point of this comment? There is nothing new or interesting in it? My point not being to redirect your snark back at you. My point being, sometimes stating the obvious can be useful. I don't think that devalues an article.
- bob1029 6y agoI would say functions are important, but absolutely pale in comparison to modeling[1]. If you are unable to describe, on paper, what your problem domain actually is, then you have no business opening up an IDE and typing out a single line of code. I will take that further. If, with your domain model, you are unable to craft a query that projects a needed fact from an instance of the model, the you should probably start over. Dimensionality and normalization are a big deal with your model. You have to be really careful about nesting collections of things within other things if you want to allow for higher-order logic to project required views. This is something we struggled with for a really long time. Every rewrite conversation began something like "well customer and account need each other in equal measure...". And it took us god knows how many iterations to figure out the relationship should not be an explicit property either way. Put shortly, Modeling is the core of it all. Start with simple top-level collections of things. E.g. instead of modeling Customer.Transactions[], model Customers, Transactions and CustomerTransactions. This fundamental principle of keeping your model dimensions separated by way of relational types can help to un-fuck the most problematic of domains. These will start to look a lot like EF POCOs for managing SQL tables... At the end of the data, data dominates, and SQL is the king of managing data. If you embrace these realities, you might be encouraged to embed SQL a lot deeper into your applications. I spent a long time writing a conditional expression parser by hand. Feels like a really bad use of time in retrospect, but I did learn some things. Now I am looking at using SQLite to do all of this heavy-lifting for me. All I have to do is define a schema matching my domain model, insert data per instance of the model, and run SQL against it to evaluate conditionals and produce complex string output as desired. [1] https://users.ece.utexas.edu/~adnan/pike.html https://users.ece.utexas.edu/~adnan/pike.html
- tomrod 6y agoWhat if code is needed to explore the problem domain? There is utility in discovery, especially for analysts/data scientists who tend to write a surprising amount of code.
- corytheboyd 6y agoI think you’re both right. If you frame the original comment as “don’t write final production code without thorough modeling” it works both ways. If you want to counter with “well our non-final code always goes to production anyway!” You have a cultural problem that needs addressing.
- jhardy54 6y agoThat looks familiar: https://news.ycombinator.com/item?id=24920186 https://news.ycombinator.com/item?id=24920186
- brundolf 6y agoHah, I wrote it in a comment myself a while back :) (I'm the author of the article): https://news.ycombinator.com/item?id=24919615 https://news.ycombinator.com/item?id=24919615
- jhardy54 6y agoOh, nice! Seems like we got the same thing out of the article.
- gr__or 6y agoPhew, I remembered tweeting that comment (https://twitter.com/watware/status/1323610182560161792 https://twitter.com/watware/status/1323610182560161792) and was about to flame this post for lack of attribution. Glad I read on! Good stuff.
- serial_dev 6y agoI like the sentiment of this article. It's a great analogy. Might be a little off topic, but it reminds me how I am happy that the Go programming language and its philosophies gained popularity even though I don't use the language regularly. Watching Go talks made me appreciate simplicity and clarity. It made me accept that I don't always need to use every design pattern in the book. It made me think about the readers of my code, who might not always be experienced enough, or might not always have time to understand the brilliant architecture I came up with. I can have some repeated code sprinkled around in the codebase. I don't always need to have n+1 layers in my architecture where all the layers just call the next layer anyway. It might be better to use functions over a complicated hierarchy of classes. It made me appreciate simple tools and widely accepted conventions that result in codebases that feel familiar the second you dive in. Of course, go is not the only community where these ideas are prevalent, and it's good to know your design patterns and architecture, etc... Finding the balance is not always easy, but it's good to have a popular, successful "counter force" community.
- brundolf 6y agoI take issue with some of the decisions that went into Go, but I definitely respect the overarching philosophy of keeping things simple and not giving teams enough rope to hang themselves with
- michaelcampbell 6y agoInstead you only give them string, so if they want a rope, for any reason, they have to make it themselves. Every time.
- bitexploder 6y agoAs I get older my code gets a little more verbose and a little less idiomatic to the language I am writing. I’ve been writing code, starting with C, since 95. Mostly Python these days, but I try to make it clear and easy read. Mostly for myself. Future me is always happy when I take the time to comment my overarching goals for a piece of code and make it clean and well composed with enough, but not too many, functions.
- keyle 6y ago> Writing a 100% functional system ("going vegan", if you will) Sometimes an analogy is funny but that doesn't make it less wrong...
- xyzelement 6y agoYour statement is true because of the "sometimes" but how does it apply to this case? I thought it was a good expression of what the author meant to convey. (Which is that you can get the most benefit from mainly adhering to a principle, while full adherence has intense costs)
- motohagiography 6y agoThis nugget stood out for me: "In my experience most codebases have a pure functional subset, and I believe writing that subset in a pure-functional style is nearly always a win for the long-term health of the project." This is probably often true. In high level applications, the idea of classing or abstracting out your i/o from the core functionality is appealing from a security and reasoning perspective, but not sure if serious developers think that way. Would you need to understand the rationale behind the codebase from a functional perspective, and even the economics of the business logic behind the features? It's kind of an architects view of "this thing essentially reduces to a queue and if I optimize for this, I get more value."
- pydry 6y agoIMO it's useful for code which is heavy on logic/decision-making. Code which is mainly about hooking systems together and managing state benefits from it much less. I also find that people who are used to a certain kind of project (e.g. heavily logical) and spend years on it have this tendency to assume all code is the same way.
- zzzeek 6y agothis post seems like a graphically appealing way of just making the usual "FP is better" argument, except without any other actual argument other than "In my experience most codebases have a pure functional subset, and I believe writing that subset in a pure-functional style is nearly always a win for the long-term health of the project." in my experience I've seen codebases that are entirely object oriented where we are writing methods, not functions, or codebases that use methods or functions as appropriate, and they also have done just fine long term. How is this article making any real argument for the former and not the latter?
- cerved 6y agousing LINQ in C# is vastly different from going full Haskell
- brundolf 6y ago> this post seems like a graphically appealing way of just making the usual "FP is better" argument I would rephrase it as "FP is a better default". A big part of my thesis is that dogmas tend to have shortcomings, which contrasts with the stereotypical "FP is better" literature. > in my experience I've seen codebases that are entirely object oriented where we are writing methods, not functions, or codebases that use methods or functions as appropriate Using X "as appropriate" is one of the things I was hoping to encourage :) As for "most codebases have a pure functional subset", perhaps what I should have said is "most projects' domains have a pure functional subset". The idea being that some slice of the business logic is naturally just deriving values from other values, and that if you isolate that substrate you get many of the benefits people talk about FP having (lack of sprawling side-effects, composability, easy parallelization, etc), without the more extreme costs like having to learn what an IO-Monad is
- cerved 6y agoMonads are mainly hard to grasp because Monad is a terrible name and all explanations overly convoluted
- 6y ago
- earthboundkid 6y ago> Bad programmers worry about the code. Good programmers worry about data structures and their relationships. — Linus Torvalds
- ZephyrBlu 6y agoI'm not sure what exactly Linus means by this, but I read this as: for good programmers, coding is about managing data structures and their relationships whereas bad programmers are more concerned with just getting the thing to work.
- ludocode 6y agoI think it's more that bad programmers focus on surface details like code style. All too often I've seen a code review of a complex feature get derailed by nitpicking of inconsequential things like variable names, formatting, etc. I rarely comment on code style for this reason. I want the review to focus on functionality, not style. I don't really believe the notion "imperfect code style is a code smell" anyway.
- nkuttler 6y ago> I don't really believe the notion "imperfect code style is a code smell" anyway. Blaming developers for something that can be automated? Yeah, something is off here, though not necessarily the review. > nitpicking of inconsequential things like variable names To me it sounds like a good thing when there's nothing else to comment on. Making code more readable is a win for everybody.
- ZephyrBlu 6y agoI have a contrary view on code style. If there's no semblance of consistency to the code you write, how can you possibly formulate a consistent and sensible architecture? The style itself is inconsequential, but no consistent style is a red flag to me.
- abernard1 6y ago
- candiddevmike 6y agoLet's go full FP and demand immutable data too.
- redisman 6y agoI think there's going to be many different Venn diagrams of combining OOP and FP. Someone should identify and name them. Immutable data is hard in languages that don't support it unless you're happy with a 80/20 solution.
- cerved 6y agoMy beef with mutable data is that it's often very hard to see when it's supposed to be modified and by what. I don't get upset by a local var i in a while loop, although I often don't see the point. The problem is the dark mutable data. All those objects being passed around, are they just being read?, are they being modified by this method call? So if mutable data is used responsibly, it's not a problem. But there's no guarantee that it's used responsibly. Unless all data is immutable. Then it's guaranteed
- unrealhoang 6y agoYou can rely on a type system that help you to use mutable data responsibly, à la Rust.
- cerved 6y agoI'm not familiar with the type system of Rust but it seems like a really nice language. Ofc a type system that helps you deal with mutable data responsibly goes a looong way to alleviate the problem but it's not a guarantee. And often times such a type system comes with weird and unexpected quirks. Here are some examples from C#: For example "readonly" is not immutable, it's a compile time guarantee that a value can only be assigned in the constructor of a type and to that instance of the type only. This means that for type Foo with readonly int bar, you can have a constructor Foo(...) : this(...) and the value of bar is mutable in the context of those constructors. For most intents and purposes however, the "readonly" only field however acts immutable enough to give a reasonable degree of immutability. The you have something like private setter functions. They go a long way to guarantee encapsulation of state. But there's no compile time guarantee that an instance of Bar int baz { get; private set; } won't mutate the baz value of any other Bar instance. In-fact, it's a common misconception that private modifiers make something private to the instance, whereas it's only a compile time guarantee that it's not visible to any other type. Furthermore, private modifiers don't actually prevent anything from actually utilizing it in runtime. You can simply use reflection or other techniques and do what you will. Then you can do other weaker forms of type-checking "immutability", for example only exposing getter functions in a IFooReader interface. These things all help alleviate "dark mutability" to different degrees but the underlying values are still mutable. I guess the point I'm trying to make is, yes, it massively helps to have a type system that guarantees encapsulation and immutability to different degrees. The caveat is that we are still at the mercy of that type system and the way it enforces immutability is often non-obvious and less immutable than one might expect.
- boxmonster 6y ago'observe due measure; moderation is best in all things' -- Greek poet Hesiod (c. 700 bc)
- jariel 6y ago'Functional Style' is better articulated as 'bits of code with clear functionality and no dependencies. Hopefully without leaky abstractions. Or sometimes what we refer to as a 'Library'. The decoupling provides greater resiliency to change and thus doesn't add geometrically to the complexity of the code base, rather just linearly.
- whoisthemachine 6y agoGreat post. I would even go as far as to amend your last comment to be closer to its inspiration: > Code only those things that your grandparents would have coded Software has been around long enough that some people's grandparents were coding, but due to the limitations of many of the systems at the time, functional, simple programs were often the craziest they could get.
- brundolf 6y agoMy impression of early coding (could be wrong) is that you mostly couldn't afford to write in functional terms, because you had to carefully manage things like memory usage. Assembly, at least, is about as far from FP as you can get
- whoisthemachine 6y agoNot in practical terms - by their very nature, registers must be mutated during a non-trivial program. Still - processor instructions are functional by nature: for example, an ADD op requires two register inputs and one register output. The assembly I worked with in university also includes the ability to create callable procedures. So even when designing assembly code, it pays to think in functional terms, although as stated earlier, registers are mutable by nature. My point was the constraints of the time kept developers from making things too complicated, so their programs often followed functional like, simple designs - see the POSIX utilities for example. Still, Lisp was created in 1958 [0], so it's definitely likely some folks grandparents were programming in it :) [0] https://en.wikipedia.org/wiki/Lisp_(programming_language) https://en.wikipedia.org/wiki/Lisp_(programming_language)
- klyrs 6y agoMy grandma wrote FORTRAN in the 60s, and my grandpa wrote COBOL in the 70s. They both expressed quite a bit of bewilderment in describing their experiences there.
- enneff 6y agoMy grandfather wrote BASIC programs for the TRS-80, so I'm not sure I would necessarily agree with that sentiment. :)
- deleted 6y ago[deleted]
- formerly_proven 6y agoMajor deja-vu moment for me. I've been carrying that headline around (as a quote) for a few months at least as an expression of a sentiment I've had for years, and thought I've seen it in a few places (and on HN), yet Google only shows me this blog as a source (which obviously isn't possible and almost feels like gaslighting). Does someone have an older reference for this?
- dceddia 6y agoThe quote is based on one from Michael Pollan’s book In Defense of Food, around 2007. The original is "Eat food, not too much, mostly plants." It’s been reworded into at least a few things since then.
- brundolf 6y agoI (the author) and another person independently thought of it in the comments of an article a couple months ago: https://news.ycombinator.com/item?id=25501263 https://news.ycombinator.com/item?id=25501263
- centimeter 6y agoThis article is absolute garbage. It’s embarrassing that it got this high on the front page. It has zero information. It’s a paean to smug, unconsidered mediocrity. Not only is the premise of the tenuous analogy likely completely wrong (the preponderance of evidence suggests eating “mostly plants” is, in fact, anywhere from bad to terrible for you, depending on your choice of plants), but the way in which it’s presented is absurd as well; paying lip service to “incontrovertible scientific research” and then writing an article completely free of any references to such research. The only thing of value I took away from this article is that the author posted it 3 times this week, and it only got popular on the 3rd try. So if you want to get something to the front page of HN, post it a bunch of times!
- brundolf 6y agoIn the HN FAQ, under "Are reposts okay?" > If a story has not had significant attention in the last year or so, a small number of reposts is ok. Otherwise we bury reposts as duplicates. https://news.ycombinator.com/newsfaq.html https://news.ycombinator.com/newsfaq.html Usually you have to get lucky with the right time of day for something to actually take off the way it would otherwise (before it falls too far down "new"). Sometimes the staff will even email you asking if you can repost a link that "should have" taken off, but didn't because of bad timing. Personally I limit myself to three tries on posts I've spent time writing that haven't yet gotten out of the single digits.
- PlugTunin 6y agoClarification: Eating mostly plants is sound advice if you consider that it means eating fruits, vegetables, whole grains and legumes.
- deeviant 6y ago> Not only is the premise of the tenuous analogy likely completely wrong (the preponderance of evidence suggests eating “mostly plants” is, in fact, anywhere from bad to terrible for you... Where did you get that information, foster farms? Or are you defining "mostly plants" to be a diet that consists solely of green skittles?
- AntiImperialis2 6y agoThe closing line of the article: >Use the primitives that are there, when possible. Write what is simple, and natural, and human. I agree with this very much, but most of this article doesn't support this statement. Let's start with the statement it is based on. >Eat food. Not too much. Mostly plants. Michael Pollan is great but the human way is definitely not what Michael Pollan suggests. No human tribe ate that way prior to agriculture, which happened relatively recently, and has greatly complicated things when it comes to our diet. So, I'm in the "Eat a lot. Not too frequently. Mostly animals." camp because I believe that's what we'd have done as scavengers and later hunter-gatherers... and since we lost most of our digestive system because of the change in diet, we can no longer digest plants well and most importantly, we are no longer immune to plants toxins, which they use to protect themselves, and a lot of processing is required to get rid of the toxins (which is seen in various indigenous diets)... and still, it is never as good as animals when it comes to toxins and nutritional value. There are other considerations which may make eating more plants sound better but I have noticed that they're not well thought-through. Also, if I'm going to be conservative about one thing, it will definitely be food. We haven't evolved for 22 million years as hominids without certain adaptations to our diets. We can't arbitarily decide to change them without side effects. Similarly, when it comes to programming, I'm in the "Write code. However much is needed. Whatever works for you." camp, the one that I made up just now and I invite others to join it. Objects and classes are good analogies and work well for many use cases. Humans think object oriented programming makes a lot of sense because we already think in object oriented terms. A lot of software we use day-to-day are written like that and it has worked fine for a very long time. Same is true for patterns and frameworks built on top of them. They caught on because they resonate with human way of reasoning about things. They have their limitations though. Similarly, functional programming has a simplicity to it which works great in many applications. When complexity grows, you have to get used to various other patterns and concepts to write real-world applications. Functional programming originates in mathematics, which is also very human... but as complexity grows, it's not as human as you'd like it to be. A lot of humans struggle with complicated functional programming code, which is why it is not as popular. When it comes to programming, there are various useful theories and tricks you can learn and knowledge you can acquire... but ultimately you are responsible for collaborating with your peers and getting the work done. So, you should choose whatever programming paradigm, patterns, frameworks, abstractions make it easy to do so. There is no "one true way" to do things. If you really enjoy the "one true way" idea and want to take it to the extremes for fun, feel free to do so. But otherwise, there's no reason to beat yourself for not using some "superior" design pattern or programming language or paradigm. A lot of these things are context-dependent. While understanding all the intricacies and benefits and tradeoffs can be very interesting, and it is in fact explored in academia, it has little real-world value, apart of entertainment, if you just care about getting things done.
- chiefalchemist 6y ago> At the risk of stretching the analogy, maybe the equivalent is "code only those things that people at a junior level would recognize for what they do". Spot on. And I would add: Keeping in mind that junior level person could be under excessive stress. Perhaps something has failed. Perhaps they are looking at that section of code for the first time. Context matters. It affects readability, comprehension, and understanding. Straightforward > Cleverness
- m45t3r 6y ago> By "functions" here I mean "pure functions". After programming for Clojure for quite a long time (~2 years), I fully share this sentiment. Using pure functions for business logic (and also using simple data structures instead of, say, classes and encapsulation) seems to generally makes the code more simple and maintainable in the long run. > Of course the qualifier is "mostly": this isn't a dogma. Writing a 100% functional system ("going vegan", if you will) often requires you to jump through a bunch of extra hoops to get all the functionality you need. Also this. Sometimes going fully functional makes things much more difficult, so a little of "impurity" is also fine.
- eyelidlessness 6y agoI used Clojure (and ClojureScript) in production for several years, and more or less share your sentiments on this. The only strong objection I have is that the lack of types made even simple data structures an occasional nightmare. Especially when most of the composite types can easily be substituted for many functions, it’s trivial to accidentally recurse a string or a keyword where a data structure should be. And the runtime errors (especially in cljs) can be utterly baffling. I’ve taken what I learned in clojure (and I can just say the article, even just its title, adequately sum it up) and use the same approach in TypeScript. And it’s a breath of fresh air.
- m45t3r 6y agoI didn't say anything about types, but we use plumatic/schema as a "type system". It is not ideal (for once, it is checked at runtime instead of build time, so we only check types during tests and requests, otherwise the impact would be too great). It is not ideal, but it works and keeps our sanity.
- eyelidlessness 6y agoYeah something like that is essential in a dynamic language. To be honest though I don’t think I could go back to runtime-only type checking and having to write those tests where a compiler tests so much automatically.
- Geminidog 6y agoI'll take it one step further. Don't just write pure functions. Write point free combinators. Have you guys ever wondered why no matter how much care or planning you use to organize your code when you begin a project, some time down the line you will always encounter a situation where the organizational scheme you chose is less than ideal or even flat out wrong? It's a sort of inevitable technical debt that occurs. The industry spends an endless amount of time debating and coming up with all kinds of solutions to deal with the above problem. First it was OOP, the latest is microservices. The actual solution to the above problem is to use point free combinators as much as possible in your code.
- carapace 6y agoI've been playing with Joy (concatinative, combinator-based) and I have to agree. So far everything I've translated into Joy has been more elegant and easier to understand. Conal Elliott's "Compiling to categories" is one way in: http://conal.net/papers/compiling-to-categories/ http://conal.net/papers/compiling-to-categories/
- redisman 6y agoAny paradigm can look clean when you’re playing. Work on it for a year and then have management tell you that we must add a feature that breaks your core design because our big client needs it.
- Geminidog 6y agoMany aspects of a programming language can make it nasty or hard to use under certain contexts. This is something most programmers are aware of through experience. My point is, that the use of combinators and point free programming formally eliminates organizational technical debt. So for this specific issue, Joy should indeed be better by logic.
- AnimalMuppet 6y ago> My point is, that the use of combinators and point free programming formally eliminates organizational technical debt. Hardly. It may eliminate certain kinds of technical debt. Pretty sure it won't eliminate all of it. As you said in a parallel post: > Readability is definitely worse when using this method. Well, that's a kind of technical debt.
- ninkendo 6y ago> He contends that most of what Americans now buy in supermarkets, fast food stores, and restaurants is not in fact food Oh, just fuck right off.
- cjohnson318 6y agoSo, I have a question. I have a kind of serial number, and different systems expect slightly different formats. One system likes dashes, one doesn't, one system likes an extra two digits, while another system likes an extra four numbers. Should I write around n(n-1)/2 pure function converters between n systems? Or one class with n methods? (If you're curious, I'm talking about oil well API numbers. It's not rocket science, I'm just curious what y'all think.)
- Matheus28 6y agoIf you REALLY have to use all these different formats, I'd have one canonical one used everywhere in your app, and 2n methods to convert to different ones at api boundaries.
- cjohnson318 6y agoAh! Thank you, that is a very good middle road.
- brundolf 6y agoI'd have one canonical one used everywhere, and 2n pure functions to convert at API boundaries. By making them pure functions you guarantee (or at least suggest by convention; depending on your language's type system) that those conversions will have no side-effects. As methods, you can never be sure as the caller whether there will be ramifications to calling them in new places.
- bigbizisverywyz 6y agoAnother advantage of that approach is that if you make sure you always persist the serial number in the canonical format, and immediately parse the incoming value into the canonical form, then comparing 2 serial numbers is easy and consistent - including e.g. when constructing db queries. I encountered a similar problem where an identity number could be represented in different formats, and the solution had been to store the string representation in all its glorious permutations. Doing db queries to find anything by that key was then impossible until the representation had been made consistent. Then when e.g. creating reports for different systems we could format the output as per that systems expectation.
- kmclean 6y agoMaybe an apt analogy for the "not food" described here could be YAML and other config. I have seen way too much logic buried in strings in miscellaneous config files. It's so annoying to work with.
- m463 6y agoThat was my initial impression of systemd. Systemd has these stupid config files. And by stupid, I mean that literally. They replaced init files (with arguably too much logic) with these simplistic config files that have no logic available. What ends up happening is you replace init files with nothing, and you have to push out any logic to an intermediate script or binary which just adds indirection.
- gfxgirl 6y agoI find it interesting that React ignores this advice about pure functions. From the react docs import React, { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } It's clear "Example" will be called every time it's rendered yet "useState" is NOT "pure". Some magic state is being kept because the first time it's called "count" will be initialized to 0 but after that it won't. Same arguments, different results = not "pure"
- brundolf 6y agoYeah, I tend to agree (and this is why I don't like hooks). I'll write pure functional components all day, but if I need to introduce state, I'm going to use a class component and just acknowledge that state as state and not try to pretend it's still just a function.
- anchpop 6y agoI use hooks because they cut down on boilerplate, not because I'm trying to pretend a component is just a function.
- neolefty 6y agoCorrect, `useState` (and `useEffect` and `useContext`) are not pure. They are ways of managing impurities while writing mostly pure functions.
- enneff 6y agoThe React model is actually superb at managing state. It makes you explicitly acknowledge it directly, and codifies it either with properties or useState. In practice it's a fantastic model, as it does let you write "mostly functions" as the article suggests, but you can't have a UI without state, so it's great that they make it easy to handle.
- lincpa 6y agoI think you will like it. It mainly uses pure functions (pipes), and it is also a good example of the classic "Programs = Algorithm + Data Structures". The Grand Unified Programming Theory: The Pure Function Pipeline Data Flow with Warehouse/Workshop Model: Everything is a pipeline, the perfect way to achieve the simplicity and unity of the software ecosystem. https://github.com/linpengcheng/PurefunctionPipelineDataflow https://github.com/linpengcheng/PurefunctionPipelineDataflow
- dathinab 6y ago> In my experience most codebases have a pure functional subset, and I believe writing that subset in a pure-functional style is nearly always a win for the long-term health of the project. I came to the same conclusion ~1 year ago when writing rust. It's pretty common to have some "high level" method in rust then you split it up, some small parts go into other maybe private methods but a lot goes into split out functions (through this depends on the task, and sometimes could be made into methods if rust would support partial self-borrows). The only think which sometimes bothers me with that approach is where to place this split out methods. As long as I don't need their functionality in other places I want them to be keep close to the function because of which they exist. But they are function and not methods so placing them in the `impl` block isn't right. Alternatively making them free functions in the method using them seems better but also isn't quite right as this "blows" up the method...
- IggleSniggle 6y agoThis is a problem you will face regardless. It is fundamentally a code organization problem and faces all the problems that other organizational problems face. Which is to say, the best you can hope for is to have a reliable way of generating the report you need when you need it. Which for me suggests that it doesn’t really matter which way you do it, as long as you 1) dogmatically adhere to doing it the same way every time and 2) have the tooling to effectively manage the downsides of your choice.
- sameerds 6y agoI know my request might be off-topic ... the original article is from a blog that does not export an RSS/Atom feed. I am looking for recommendations for a service which can let me scrape a feed by guessing the structure of the articles.
- brundolf 6y agoI've thought about implementing RSS for it at some point, good to know somebody has interest in that. In the meantime I tried to keep the HTML semantic, so hopefully you're able to find a way to scrape it!
- pedro1976 6y agoI did exactly that [0]. Check out the live version [1]. [0] https://github.com/damoeb/rss-proxy https://github.com/damoeb/rss-proxy [1] https://rssproxy.migor.org/ https://rssproxy.migor.org/
- wirrbel 6y agohttps://lucumr.pocoo.org/2013/2/13/moar-classes/ https://lucumr.pocoo.org/2013/2/13/moar-classes/
- jahaja 6y agoWould be curious to know if he has changed his view since then. Also I think that the source code of his projects in Python pretty much validates the point of writing simple code. He has made great user-facing APIs (flask) but as soon you dive into the code base itself it's imo a rather dense and hairy mess of abstractions.
- ornornor 6y ago> code only those things that people at a junior level would recognize for what they do Couldn’t agree more. But my colleagues make a point of keeping cryptic code cryptic because comments explaining context and reasoning are for noobs. I guess it’s a mid level developer fallacy and until they’ve had enough of pulling their own hair out over this kind of code, they won’t change their mind.
- cjfd 6y agoI agree with the sentiment that things should be made as simple as possible. The sine function really has no reason to be anything besides a function. I am not sure, though, that writing the simplest thing possible results in mostly pure functions. In my experience programming is mostly about managing a state. My programming jobs have generally been about tracking what the state of some other piece of hardware and/or software is. It seems hard to escape state in that case. In my spare time I have lately been writing a compiler like thing. That seems to be, among other things, about maintaining a stack of all the stuff that has been defined/declared previously. If so many things are about maintaining a state, how practical is this 'mostly functions' thing actually?
- ritter2a 6y agoI would claim that the benefits of 'mostly functions' strongly depend on the task at hand. For the field of compilers, I can for example see value in making program analyses pure functions that just compute information about the program and separate them from the program transformations that use this information to (impurely) manipulate code. This makes the analyses more reusable and probably makes reasoning about correctness easier. For other tasks in the compiler, pure functions can be a pain. My favorite anecdote for this is that of a group of students in a compiler's course who insisted on writing the project (a compiler for a subset of C) in Haskell and who, when discussing their implementation in the final code review, cited a recent paper [1] that describes how you can attach type information to an abstract syntax tree (which is an obvious no-brainer in the imperative world). --- [1] http://www.jucs.org/jucs_23_1/trees_that_grow/jucs_23_01_0042_0062_najd.pdf http://www.jucs.org/jucs_23_1/trees_that_grow/jucs_23_01_004...
- ookdatnog 6y agoAn ad hoc solution is also a no-brainer in Haskell. They didn't need to read a paper to solve this issue, they did because they wanted the fanciest solution that is extensible in all dimensions.
- mbrodersen 6y agoI recommend watching this talk: “The Value of Values” https://www.infoq.com/presentations/Value-Values/ https://www.infoq.com/presentations/Value-Values/ It explains what the difference is between state and value and why most (almost all) programs actually have very little state and can be written mostly stateless. It was a big eye opener for me.
- pro_methe5 6y agovoted up. write code, and then visualize it: https://www.visualsource.net https://www.visualsource.net if it's too complex, simplify.
- ritchiea 6y agoWhat's the difference between writing OO code that depends on internal state and writing a pure function that expects an argument that is a data structure of a specific type (and thus has internal data that could be different)? Is the pure function no longer pure if the argument is a data structure thats complex and the values within the data structure dictate the outcome of the the function? Or is it a pure function because if you pass the same data structure with the same internal values the function will return the same value?
- yarg 6y agoIt's the difference between c++ and c. He's not suggesting pure functional programming - and beyond a certain point the simplicity associated with functional programming will be completely negated by the complexity of the arguments. From my understanding of what he said, I doubt that he'd advocate for pure functional in circumstances of significant necessary complexity.
- garethrowlands 6y agoPassing a big structure to a function doesn't make that function impure. But passing a structure containing objects with impure methods does make the called function impure. Passing a big structure to a function is a bad idea in any paradigm because it's a big dependency. Instead, you should write the function to operate on the data that it needs and pass just that when you call it.
- brundolf 6y agoPurity is a question of mutability, nothing more. If the function mutates its arguments (or its closure, or its global environment), it is impure. Any useful program will of course need to do these things at some point, but there's a lot of logic that just goes from A -> B (or A, B, C -> D, or whatever), that doesn't need to concern itself with these things, and should be insulated from them. There's nothing inherently impure about taking large data structures as arguments, though it does make it trickier to enforce immutability in most languages (compared to primitive values). It's worth noting that it's entirely possible to write "pure methods". Unfortunately most languages don't really let you a) have mutable structures, and b) write enforced-immutable methods on them. Rust is the only one I know of: a method can take a &self instead of a &mut self, which prevents it from mutating self (recursively, which requires knowledge about ownership unless your language is 100% immutable like Haskell or Clojure, which is why this feature is so rare). What I tend to do in other languages like JavaScript, C#, or Python is to use property-getters as a convention that strongly suggests purity; unfortunately that's about the best you can do. In multi-paradigm languages the decision on whether to make something a pure "getter" method or a standalone function is mostly one of aesthetics. Standalone functions give you a bit more flexibility in use, but sometimes the foo.prop syntax is more readable.
- bobbane 6y ago> ... a practical tip is to eat only those things that people of his grandmother's generation would have recognized as food. So, Lisp?