Hacker News new | past | comments | ask | show | jobs | submit login
Clojure for the Brave and True – Functional Programming (braveclojure.com)
162 points by nonrecursive on Sept 11, 2013 | hide | past | favorite | 188 comments



Has there been any exploration into an idea where the problem with side effects is not that they make functions impure, but they are often against the metaphor of the instructions being given? That is, if the metaphor that one is trying to model is traditional mathematics, then of course side effects are terrible.

However, consider a program where the main metaphor is controlling something. Logo, for example. Few people argue, I would think, that the traditional imperative styling there hurts and confuses things.

More extreme, consider stack based languages. These are strictly based on the current state of the program, yet my understanding is if you can fit your mind to that metaphor, it works very very well.

Or, my favorite category, cookbooks. Look at traditional baking directions: "Begin heating oven to XXX, mix dry ingredients in bowl, add butter, whip, ..." Doesn't get any more imperative than that, and yet people around the world often have great success replicating the desired results. (Granted, I often think that the difference in programming and teaching is that humans make an effort to understand what you were communicating, computers typically don't.)

Does this make sense?


> Does this make sense?

Are you asking "Is it possible that purely functional programming is yet another paradigm that, like OOP, is fantastically useful but is still destined to be the square peg that is forced into too many round holes?"

If so, then yes, you're making sense. And yes, that's probably what will happen. However, as with OOP, the end result should be a net gain in knowledge and maturity for our very young field and yet another wonderful tool in our tool box.

Having said that, I think that with the "functional wave" comes a bit more understanding that this, too, may not be the perfect fit for every problem. Look at Clojure: they try hard to give you mutability in terms of abstractions that seem to encapsulate what it means for mutability to be a valuable thing.

In Haskell, monads are a great model of "what it means to be imperative". If you think of imperative programming as:

    setup();
    goLeft();
    stop();
    lookAround();
    pickUp(ball);
    turnAround();
Then monads try to make precise just what exactly those semi-colons are doing. To me, the fact that this is hard to understand is mainly the result of the language being the playground of mathematicians who have long since forgotten why such concepts are hard.

In other words: there is a "meta paradigm" going on here and it's just starting to emerge. That meta-paradigm is to provide a language that gives you the abstractions to build the right abstractions to ultimately build the star-shaped peg that fits into the star-shaped hole.

So yes, if you're taking the long view then Clojure might be too opinionated for your tastes (and I have in fact opined on another thread today about what it would take to back slightly away from that lofty perch), then it sometimes can look like yet another oversold silver bullet language. But in the long term, we seem to be converging on something much healthier than the "stop using that paradigm and start using this one because it solves everything!" approach of the past.


Close. I'm more asking if there has been study into ways of plugging the different paradigms together into a whole solution. That is, I firmly believe that each have their strengths. To carry a sports analogy through to this, teams practice different strategies and techniques in isolation so that together they can operate better. Seems most of the reading is about this isolated level of practice. I would love a more holistic view that covers it all.

Edit: So, monads help show what happens under the scenes for when you are writing imperatively, but they do little to illuminate when that style is beneficial. My contention is that this is up to the metaphors one has defined for their program.


Me too. I have a personal "research hobby" aimed at exactly this.

For your monads example: one of the things I'm looking into is how to have the experts establish the monadic abstraction and have the system gain all of the benefit WITHOUT jRandomCoder at HugeCorp having to understand what a monad is... or even realize he's using one. Obviously Haskell falls short in that area.


> or even realize he's using one.

Uh, this happens all the time. Scala programmers everywhere are used to the pattern

    for { 
       a <- optionA
       b <- optionB
    } yield ("Both options have values! A = " + a + " B ="+b)


It would also seem that referential transparency takes away many real world applications. We need randomness and an ability to read files.

This is coming from a big fan of functional programming and removing side effects.


"Does this make sense?"

I've read your post three times, carefully, and no, I actually can not extract a clear question from it. (I do not mean that in a mean way, just as feedback.) It seems to resolve to an observation that functional programming is not the only paradigm that has produced successfully-running programs, which is certainly true, but not terribly information-rich.

There are actually a lot of interesting possible directions you might be trying to go, but I'm not sure I want to try to guess them. (I will take one stab with: http://en.wikipedia.org/wiki/Effect_system )

I will observe that baking directions aren't a useful comparison; the difference between programming paradigms do not manifest until you have something that is a real size. Imperative is just fine for a single page program. Anything is just fine for a single page program; I have unashamedly written outright spaghetti code for single page programs.


Yes, real size and real world examples are what counts. And then, as much as numerous examples of real world and big size problem (Google, Wikipedia, etc.) have been tackled with a mix of OOP glue code (Java, C++, Python) and declarative style data retrieval (SQL), I have much harder a time finding examples of FP uses in the real world.

I suppose some data analysis is done FP style somewhere, but is it comparable in complexity? (The data might be very complex, the algorithms too, but the code itself probably has much less branches, corner cases, ifs, and other PM-added mess).

Oh, and yes, I am aware of a web app written in lisp by pg, but I wonder if this experiment is really reproductible. Is this Viaweb code still maintained and running?

When the "world" from the point of a view of a program is an enormous drealocks-like hairball full of heterogeneity (which is the case for the code bases of most of the tools we use, Facebook, PowerPoint, etc.), I have hard time imagining that it can be modelled in a nice FP ether where well-behaved functions pass to each other all the data they need, and magically "reduce" it to a magically deterministic output.


The thing that bothers me about this attitude is that it discourages considering alternatives. I've had a blog post on this stewing for awhile, but the gist of my counter argument is that it is a common human condition to feel that the current level of technology is sufficient - people have made that argument countless times in the face of progress.


Or perhaps it encourages think about alternative-alternatives. Functional programming has been presented as the alternative to OOP for a looong time now and the indications are that it just isn't to replace it ever.

OOP is a paradigm that does work for gluing huge piles of flawed-but-useful code together. And OOP indeed has lurid flaws to it on multiple levels (the ambiguity concerning what "inheritance" is supposed to mean is one of many ridiculousnesses).

Something that replaces OOP would need to have SOME winning combination of gluing hunks-of-together so you didn't have to start from scratch and being-so-powerful-but-understandable that medium skill-level programmers could replace the junk reasonably painlessly.

If we acknowledge FP isn't going to be that paradigm, then we can start searching for a different one.


Interesting; I am amenable to that idea, but don't have any experience with alternatives. I think a hybrid OOP/FP approach (i.e. Scala) can actually work really well, though I don't know if there is a perfect reference implementation.


He didn't say it's sufficient, he said OOP can do the work (lots of examples) while FP can't (he asked for examples, since we don't have any).


The data might be very complex, the algorithms too, but the code itself probably has much less branches, corner cases, ifs, and other PM-added mess).

Being able to do complex stuff with less mess sounds like a big win to me. Current users of FP in industry tend to be in the financial sector(though a quick check of haskell in industry shows several CAD users too).

Though I have to agree with you, I haven't seen a really convincing case study yet. For me at least it has wiggled its eyebrows and said look over there convincingly enough for me to take a look.


I guess nobody ever thinks of Excel in these cases? I've heard it's used sometimes for calculations. It doesn't need to do destructive updates to intermediate values.


I think the argument would be that, though Excel is awesome for small data sets and so validates that it is indeed a great way to think about things at a level, it does not scale well. Indeed, one of the first things people suggest is to run away from large scale solutions that were done in a spreadsheet.


Excel strengths come from the fact that the main mode of operation is generally declarative.

Excel's weaknesses for use in large scale systems largely come from the fact that it isn't easily maintainable because, in the same main mode of operation, the actual code is hidden in cells that need to be accessed individually (in terms of usability for someone trying to maintain it, its almost like having one line of code per source file, since you have to open each cell to bring the code in it into the "editor".)

The most common way to circumvent the problem posed by the second feature in Excel is to resort to coding in an imperative language, which negates the benefit of the first.

FP languages share the first feature, but not the second.


I would argue that Excel's sole strength is that it directly features the data to the user. It is not that things are declarative that is nice, it is that the data is all you see. Because that is all a lot of folks care about.

Indeed, the way in which many people populate spreadsheets is far from the functional way. It is typically much more imperative. They literally have a list of commands from someone for how to make the chart or tabulation they are interested in seeing appear.


The fact that spreadsheet regular users are programmed by spreadsheet power users (the ones building charts, formulas, etc.) in an imperative way does not mean that the creation of the spreadsheet by the power user is not declarative, and is really completely irrelevant to what is being discussed.


Especially from a Clojurian's perspective, the tool to watch in this realm is Datomic. It's a fundamentally different approach to databases, based on the functional paradigm, and should it succeed in massive scalable situations (which I suspect it will), it will be a triumphant validation of the particular functional/immutable paradigm Rich Hickey has been promoting.


Jane Street Capital uses OCaml for their trading system. Yaron Minsky explained that one of the main benefits of it was that people could read and understand the code much better than the other languages they tried (including C#).

Yaron's talk: https://www.youtube.com/watch?v=hKcOkWzj0_s


Not taken in a mean way. Was an honest question in my post. :)

Not sure how much I care for effect systems, just yet. Though, I think there is a high chance that is exactly what I want.

My main observation from such things as baking directions seems to be that functional (being a highly declarative style) is great for high level, but that you need a way to carefully logic about the lower level details as well.

To go with my baking example. The declarative would be, "Dinner should be enough pizza for X people..." This is great and all, especially if you are strictly at the management "high level" view of the task at hand. (Especially since this is likely to be part of a whole other set of declared outcomes. "Party room with X tables, etc...") However, one is not going to be able to actually get pizza for the crowd this way.

Now, one might say that at the lowest level one can have relatively "pure" functions such as "dice onions." It certainly seems this is where most blog posts spend there time. At the overall high level declarations and the low level functions. Often pointing out that one could even implement this with the "order from local store" keeping the high level meaningful and completely divorced from the lower level. My problem is most of these posts/papers completely lose track of the area where the actual steps to make the pizza, or arrange the party room, take place.

That is, at some level imperative works because it actually is easier to model in your mind. Why does nobody focus on identifying where that level of abstraction may be? Or do I just have a broken mind? :)


"dice onions" is not a pure function. To be a pure function, you'd need to have the diced onions, but still be able to use the original onions in another context.

The real world doesn't have any pure functions. For one thing, pure functions are timeless; you can view their inputs and outputs as just existing. Of course in the real world it requires computation time to determine what the output is, but that is arguably a characteristic of our universe rather than the function. Trying to understand pure functional programming from a real-world perspective is to start at a grave cognitive disadvantage.

Which rolls nicely into...

"at some level imperative works because it actually is easier to model in your mind. Why does nobody focus on identifying where that level of abstraction may be?"

My opinion is that it certainly is easier to start with imperative, but the problem is that by bringing in all the complexities of the real world you are therefore inevitably building with complicated pieces, and the end result is complicated. Programming with pure functions and trying to isolate the real world is cognitively unnatural... but then again, so is trying to assemble a multi-million line program. That unnatural task is made easier by shearing away the real-world aspects of the function, and deliberately retreating to a much simpler components as much as possible.

My own attempt to elaborate on this theme: http://www.jerf.org/iri/post/2908 The braveclojure.com post grazed my point, but then pretty rapidly left it. My feeling is that people say things like "pure functions are easier to reason about", but still mentally leave that statement classified in "academic brain space" and fail to explore or explain why that's also a very important statement in "practical brain space". (If my mind is broken, it is in that I have refused to build the wall between those two that so many people seem to have.)


Right, I realize it isn't a "pure" function in the direct sense, but at the abstract level it can and often is presented as such. In programming this is trivial to simply return a new one that is diced. (Consider the typical "cons" example, where the trick is to actually return a new list, not modify the existing one. Something that is not exactly intuitive for most folks.)

And, I do strongly believe that declarative works best to start. This is mirrored in the physical world where people say to focus on easily specified goals for tasks. (And why my examples started high level.) Similarly, I suspect once you get down to the nitty gritty, it is again nice to be in the declarative realm. It seems that "somewhere between" these layers is where the imperative really belongs. I just don't know where that is.

I will probably take a while to digest your post. Thanks for sharing it!


"Dinner should be enough pizza for X people" will totally get pizza for the crowd if the system I am interacting with is sufficiently smart. Eventually, the system will say "Sorry, I don't see how to do that." If we're still smarter (in relevant respects) than the system, then we can instruct it further...

I think generally, a move to a declarative specification and operational constraints, plus some operational "here's how you do it" to fill in gaps, would be a good long-term move.


> My main observation from such things as baking directions seems to be that functional (being a highly declarative style) is great for high level, but that you need a way to carefully logic about the lower level details as well.

Seems to me that its the other way around; programs tend to have, at the highest-level, an inherently ordered set of interactions with external stateful interfaces, but at a lower level have logically pure computations on values derived from those interactions which control later interactions.


When I think of functional programming I think of the verbs or actions. So instead of worrying about the onion, I would think about dicing. There is an input, onion and output, diced onion. That is a function.


> My main observation from such things as baking directions seems to be that functional (being a highly declarative style) is great for high level, but that you need a way to carefully logic about the lower level details as well.

To go with my baking example. The declarative would be, "Dinner should be enough pizza for X people..." This is great and all, especially if you are strictly at the management "high level" view of the task at hand. (Especially since this is likely to be part of a whole other set of declared outcomes. "Party room with X tables, etc...") However, one is not going to be able to actually get pizza for the crowd this way.

Functional programming is very declarative, while still being totally specific. It is declarative in the sense that it describes what things are (as opposed to do this and that to get this thing), not in the sense that you give a list of constraints to a program and leave it to the program to make sense of those constraints - such as logic programming and linear programming. Descriptions like 'dinner should be enough pizza for X people' sounds like a constraint, like 'no more than 20 onions, at least 10 potatoes, potatoes are 0.5 currency per potato, you should have less cabbage than cod; maximize the nutritional value according to (linear function)'... Of course languages like Prolog has an understandable way of finding if a program satisfies all your constraints, but it is different from the execution model of an imperative language. Functional languages have the same execution model (well, from those that I've seen), only with a more functional style (and tools to do the functional style in a non-horrible looking way). The mathematical way of defining factorial is very declarative, yet it is also as specific as any imperative program you'd want to write to define factorial.

So I don't see what you mean about low/high level here.


I was referring to level of abstraction. At an abstract level, many functions simply describe what something is, not how to get it. Easiest example for me is X to the Y. The naive implementation of calculating that is terrible compared to a good way. Yet both are mathematically the same.

Essentially, the difference between a descriptive equation and an algorithm. :)


> I was referring to level of abstraction. At an abstract level, many functions simply describe what something is, not how to get it.

All functional programs describe what something is and how to get it (the what is the semantics of how you get it).

> Easiest example for me is X to the Y. The naive implementation of calculating that is terrible compared to a good way. Yet both are mathematically the same.

"X to the Y"? Are you talking about time complexity? That is just as much a problem in imperative programming as in functional programming. Or are you talking about some constant inefficiencies, such as using recursion instead of a loop? Even though you might write the algorithm in a functional manner, that doesn't necessarily mean that it is all cons lists and recursion all the way down. Those things might have been translated to a more efficient, imperative form by the compiler (GHC for Haskell will do that). The compiler can do more aggressive optimizations for a pure function than for a method that is restrained by a type signature like "takes a String, returns a Boolean, and can do everything from turning on the kitchen light to blowing up the Universe in between".

> Essentially, the difference between a descriptive equation and an algorithm. :)

All pure functions are algorithms. It's all programming, after all. I don't get the distinction in this context.


Yes, I was referring to the time and space complexities of actually calculating the values. If the pure function were the algorithm, then there would be no separating the two. However, since a pure function can be calculated by several algorithms, then we are really just putting off some of the major points, no? (Hell, even in the simple 8 * 2, we are glossing over what really happens. Few programmers really appreciate the way in which that math happens on the computer. And fewer still are aware of when this changes in their architecture. And for anyone that thinks this is just some elementary exercise, I challenge that you have not seen the effort it takes children to really appreciate how to calculate 8 * 2.)

This is all really missing my main assertion, though. What is your function for getting in your car and driving somewhere? Could this be written in such a way that everything was a "pure" function? Probably. My hunch is that it wouldn't really be that readable, though. Now, if we shift the paradigm such that one of the "primitives" is your current location, suddenly the directions are a lot more readable.

For an easy example on what I mean here, just look elsewhere in the thread where folks were writing the program for cooking things. By far the most readable is the imperative one.


> Yes, I was referring to the time and space complexities of actually calculating the values. If the pure function were the algorithm, then there would be no separating the two. However, since a pure function can be calculated by several algorithms, then we are really just putting off some of the major points, no? (Hell, even in the simple 8 * 2, we are glossing over what really happens.

Again: both imperative and functional algorithms can be implemented in several ways. Both imperative and functional algorithms can solve the same problem, but have different time complexities.

"However, since a pure function can be calculated by several algorithms, then we are really just putting off some of the major points, no?"

What? The semantics of a pure function shouldn't be changed when you actually compute it - there is no "functional algorithm execution algorithm". The actual code that gets evaluated can be pretty different from the actual code you wrote, though - maybe your lazy lists got implemented as for-loops. Maybe you used a list in some way that made it look like you had O(n) space complexity, but it got turned into a loop that didn't store intermediate values so the space complexity is actually O(1). So yes, time complexity can be a little decieving, in some cases, if the compiler is sufficiently smart. However, there is no magic about the actual semantics of a functional algorithm.

> This is all really missing my main assertion, though. What is your function for getting in your car and driving somewhere? Could this be written in such a way that everything was a "pure" function? Probably. My hunch is that it wouldn't really be that readable, though. Now, if we shift the paradigm such that one of the "primitives" is your current location, suddenly the directions are a lot more readable.

Maybe you could. I don't think many care, though, not even functional programmers. The only decently widespread language that I can think of that actually enforces purity is Haskell, and some people even prefer to write imperative code in it over other languages. I don't get this obsession of yours with trying to argue against pure functional programming in the very, very strict sense of the whole program being pure, if that is actually possible. I think there are very few that are upset about having to write imperative code for code that has to do with sequential commands. They might use pure functional programming for the computations that they need, but they'll effectively write imperative programs at the top level.

What they probably do want, though, is to be able to clearly distinguish what parts of their programs are pure and what parts of their programs are potentially effectful.


Ah, I think I see the misunderstanding I am causing. I am not arguing against pure functions as a whole. Just acknowledging the weakness that they can not often be the whole. Many of the arguments for pure functions seem to be based on the "if your application is nothing but a collection of pure functions, then it will be easier to logic about." My assertion is "use that which is easier to reason about where you can." In some cases, this will actually be mutable state based entities. In others, it will be pure functions.

I also fully grant that when one is trying to work in a massively shared state application, pure functions have massive benefits. Seems the real win is typically identifying that which can be easily shared and that which can not. And partitioning the program accordingly. (Which, is what you ended with. So, I believe we are actually in fairly violent agreement. :) )


I think you're hitting up against something that is very subtle and it speaks to the fundamental nature of what it means to compute something. I think you can gain a bit of insight into what you're trying to get at if you expand the discussion to the interplay between mathematics and physics.

Let's look at your example of baking. Indeed it seems like an imperative (physical) process. When we mix eggs and flour to form a batter we can't get the eggs and flour back! Indeed even cracking the eggs seems like destructive update to me. Think about why that is. We've performed a non-reversible transformation on our ingredients AND we can't go back in time! In this case time is implicit in our understanding.

Now consider trying to model baking mathematically. We want to build an equation that will tell us the state of our cake. This will be function - that is dependent on time - time is now explicit in our model. Now we can say at t0 our eggs are whole. At some time in the future t1 our eggs are cracked. This doesn't negate the fact at t0 the eggs are whole. We can't go back in time and update what the state of the eggs then - that makes no sense! So here we see that when time is an explicit parameter we have time symmetry. The equations don't care if time goes backwards or forwards.

And we can always do this. If you have some process that has a state that changes through time you can model that process with a static model that includes time as an explicit parameter. This means that our model is a higher dimensional look at the real thing. And our actual computation (or experiment or baking etc.) is a degenerate case of the more general model where the time parameter is actual time.

How does this relate to functional programming versus imperative programming. Well it is the difference between theory and experiment. But in this case we also have to deal with the fact that our program is computed (a physical process happening in time) and so we're using a physical computation which follows the rules of mathematics to model mathematical rules using a degenerate case of those rules. So if it seems like there's an abstraction leaking it's because it kinda is.

I like to think about it in this way because it highlights the very tight knit nature of physics and computation. Indeed a general theory of what it means to compute something could lead to a unified theory of physics and vice-versa.


That's an insightful comment and an important point. Because the underlying computation is physical, there's no such thing as side-effect-free computation. Any attempt to treat programming as purely mathematical, i.e. pure FP, is going to run up against this physical substrate. You can't abstract it away completely, and it gets complicated if you try. So I don't think pure FP is likely to be an ultimate solution to the core problems of minimizing complexity and building scalable abstractions. It would be, if programming were math, but math is not executable.

I believe one can even see this in the OP. Some of those examples seem more complicated than the side-effecty, loopy code that they're replacing. Maybe it is all habit and my mind has been warped by years of exposure to side effects and loops. But what if those things are more "native" to programming than pure FP can easily allow, and we need a more physical model of computation?

I've often thought it would be interesting to try teaching programming as a kind of mechanics in which values are physical commodities that get moved around and modified—to see whether it would be more accessible to beginners that way. I also wonder what sort of programming languages one might come up with out of such a model. It would be an interesting experiment. One would use mathematical abstractions to reason about such a system, but would not think of the system itself as a mathematical one.


> That's an insightful comment and an important point. Because the underlying computation is physical, there's no such thing as side-effect-free computation. Any attempt to treat programming as purely mathematical, i.e. pure FP, is going to run up against this physical substrate. You can't abstract it away completely, and it gets complicated if you try. So I don't think pure FP is likely to be an ultimate solution to the core problems of minimizing complexity and building scalable abstractions. It would be, if programming were math, but math is not executable.

Oh come on. Is the fact that your processor emits slightly more heat from computing your pure functions, and once in a while a cosmic ray may shift a bit or two, really such a big hindrance? You sound like a die-hard purist, ironically. We might as well ditch the whole idea of applied mathematics since there is always going to be a mismatch between the real world and mathematics. Triangulate? You can't do that, because the degrees you measure isn't going to be measured to the 'infinitely' precise decimal point. The whole theory behind geometry is based on practically unatainable ideals; surely Plato would be turning in his grave if he knew that cartographers had been using geometry to make those (fairly inaccurate) maps. shudder


I think that is an uncharitable interpretation of gruseom's comment. I assumed he was referring to the fact that some programming tasks are inherently stateful, such as writing OS kernels and device drivers.

However, I think that we should be able to, in principle, provide abstractions on top of inherently stateful things that hides their nature. In the same vein, we should be able to architect programs so that we can separate the inherently stateful stuff from the functionally pure stuff. For some applications, this will help. For others, not so much.


> I think that is an uncharitable interpretation of gruseom's comment. I assumed he was referring to the fact that some programming tasks are inherently stateful, such as writing OS kernels and device drivers.

No. The underlying computation is physical is just as true for any computer program, OS kernel or not. What was referred to was clearly the fact that it's silicon at the bottom level of any computation, just as his parent was talking about ("leaky abstraction" and all that).


I agree, mathematics is still the best tool we have for actually understanding these things.


Guy Lewis Steele, Jr. and Gerald Jay Sussman. "The Art of the Interpreter of, the Modularity Complex (Parts Zero, One, and Two)". MIT AI Lab. AI Lab Memo AIM-453. May 1978

http://repository.readscheme.org/ftp/papers/ai-lab-pubs/AIM-...


Thanks for the link to that paper!

I recommend reading the section on "Top Level vs Referential Transparency" after reading this article. It argues that the trade-offs for absolute transparency require the programmer to define the entire world ahead of time. This trade-off is not acceptable by the authors of the paper for reasons elucidated in earlier sections and in SICP and so they demonstrate a version of EVAL whose environment allows for forward-references to top-level forms which we know is useful to writing dynamically compiled programs and decoupled modules.

Every pure-FP language implementation has to make trade-offs with impure, mutable state and data. Haskell had to add the IO monad. Clojure has Var and Ref. Engineering isn't a discipline of absolutes.

http://this-plt-life.tumblr.com/post/44462204757/simon-peyto...


Holy crap, just from the abstract I can't help but think many today would balk at the claim "More general side effects are also found to be necessary to promote modular style."

Thank you very much for this link! I should have known to search for work by these authors. I can only dream of a world where I could have found their works more prominent in something like infoq.


Or, my favorite category, cookbooks. Look at traditional baking directions: "Begin heating oven to XXX, mix dry ingredients in bowl, add butter, whip, ..." Doesn't get any more imperative than that, and yet people around the world often have great success replicating the desired results.

After living with a chef for a couple of years, I came to believe that recipes would be much more effective if less imperative and more focussed on exposing the contribution and relative importance of each ingredient/process for the final result. In cooking there is much variation on the inputs: I want to know if the lemon juice is there for taste or acidity so I can tell if I have a bad lemon and for what I can replace it.

It's like GPS navigation: the step by step instructions will often lead you off track. Let me see final destination on the map.


But aren't you just trying to get recipes to answer a question they weren't made to answer? That is, a recipe is often "how do I make biscuits?" The result is a known place. Not, "why do these ingredients mix together to make biscuits." That is an entirely different question that will require a fairly different answer.

Now, a question that can be a bit of a hybrid is "why does this recipe make good tasting biscuits?" Starts with a limited scope and provides ingredient by ingredient level answers.


Recipes try to micro-manage you instead of giving you the right information. This works in the same cases where micro-management works.

You had plenty of biscuits before, you know how you like them, and if you have experience baking them you know what can go wrong. So for biscuits the recipe model is fine. But for that lobster bisque soufflé you just picked off Joel Robuchon's cookbook? Not so much.


I'm not sure I follow. There are folks out there good enough at following recipes that they pretty much can just pick one up and run with it. Similarly, there are recipes that are so poorly done that they couldn't be followed in any reasonable sense.

None of this really addresses the imperative/declarative point, though. If you were to put together a recipe that was nothing but the declarations of all of the interactions that must take place to make the desired food, I'm sure it could be done. At this point, "executing" the program would really just be finding the correct order to make the interactions happen. However, my fundamental challenge is that this program would be no more approachable for other programmers than alternatives. Less so, actually.

As an example, SQL is a wonderful language for describing the data that one wants to get out of a database. It practically provides zero indication for how that fetching is actually done. For that, one creates an execution plan to logic about the process of getting the data.


Oh, but in what regards programming I fundamentally agree with you. And in what regards cooking, I'd go for an hybrid model as you mentioned, rather than a purely declarative one. I got side-tracked by your reference of one of my pet subjects and wasn't at all thinking about your real argument on programming, sorry for that!

Coming back to cooking: the best guys won't be much following the recipe but reading between the lines to get to the real information, mostly ignoring the imperative steps (which are usually declinations of techniques they already know by heart). Taking the SQL example: the recipe model gives you something like the result of an EXPLAIN command, often without the benefit of having the actual SQL statement. Sort of.


:) Completely agreed on the best cooks.

And no apologies necessary! I feel I owe a giant thank you to all involved in this entire thread. Has been a joy trying to digest all of the facets covered. In this subthread, it comes back to what is the program trying to accomplish? Communicating exacting steps to the computer to perform (as few computers will "ignore imperative steps"[1]), or communicating to the next programmer what was intended. Or both? Seems we both feel it is both, and hence needs a hybrid approach.

[1] I realize this isn't even strictly true. Computers are free to reorder some instructions, for example.


Has there been any exploration into an idea where the problem with side effects is not that they make functions impure, but they are often against the metaphor of the instructions being given?

There are lots of posts on blogs, newsgroups, etc. about the aesthetic value of having one's code be math-like, but the working programmer doesn't necessarily care about that. That programmer probably does care about avoiding spooky action at a distance. Conforming to a "traditional math is [beautiful|divine|the true Dao]" ideology does not matter to that programmer as much as knowing what data a function depends on or affects.


Right, this is essentially my question. Most of these posts start with the assumed position that one's code should be "math like." At least, that is how it seems to me. Yet we often find ourselves working with items such as a DOM which are far removed from how many of us think about "math like" items.

My question is essentially have there been any approachable explorations of this topic? In my case, at least, preferably not starting from the perspective of category theory. :)


Which is your question? "Why is this useful?" or "How do I use this with something like the DOM?" or both or something else?


My question is if there are explorations of where "the aesthetic value of having one's code be math-like" is explored at a more objective level. Aesthetics are nice, but they do little to explore the ultimate goal of getting things done.

I confess I really like the exploration in SICP of the value and place of imperative programming versus declarative programming. I do think there is a space for both, but I worry at the gymnastics we go through to never mix our metaphor away from "equation like" and over to "directions like."


Aesthetics is really not my area, so I don't know how aesthetic claims are generally evaluated in a formal or objective way. If the ultimate goal is getting things done, I would suggest ignoring the aesthetics talk.


Right, but most of the posts seem to focus on the aesthetics talk. Where are the ones that delve into more objective or even empirical results?


On the other hand, recipes don't maintain global state, so they expose a functional interface (ingredients->result). A lemon meringue pie for lunch is assumed to not influence the taste of roast pork for dinner.

Yes, functional programming is analogous to cleaning your kitchen. You heard it here first, folks.


Only in a sense of "non-shared" resources do not influence each other. Trying to clean your kitchen while cooking is not the easiest task to consider. Similarly, not realizing you lack the ingredients to make both breakfast and dinner due to few eggs indicates a shared state between them. More directly, if you do not clean your resources, eventually you will not be able to complete a task.

Of course, I think this is the main appeal of functional in today's world. We have so much computing resources that it often does seem we have unlimited memory and such. Essentially we move from cooking area to cooking area such that someone can clean up after us, and we only carry with us the items we are interested in.


Cooking uses linear typing, hence, you can only use your eggs once.

Clean is a purely functional language with linear typing.


I've never heard of that language. Amusingly, I had toyed in my head with the idea of modeling this language. Seems you pretty much have to have some notion of "dynamic" typing, since the type of a variable changes on calls to it.

Thanks for the pointer, here is a link, for those that will also find this interesting. http://wiki.clean.cs.ru.nl/Clean


It's not really dynamic typing..


Yeah, that is why I put it in quotes. I didn't know what it would be. Apologies for not making that clearer. This is all admittedly way over my head. Very fun to explore, though.


By an accident of history, I learned Clean before I heard of Haskell. It's a fun language.


An interesting thought. Recipes seem like a great metaphor to explore.

The main is that a destructive functions can call a non-destructive functions freely but a non-destructive operation, say "find best egg" should not have any destructive effects whereas "beat egg" can call "find best egg freely".

I know SQL separates destructive and non-destructive operations very clearly and it also allows the user to back out of some destructive action sequences if all the conditions aren't satisfied. This makes what to do when you don't have butter more easier solved.


(serve (fry (add-pan kale (fry (add-pan (dice onions)))))


Better is

    (->> onions
      dice add-pan fry (add-pan kale) fry serve))


Amazing. What's the ->> operator called?


A monad ;-)

No, but seriously, you can think of (->> foo bar baz quux)

As a way of saying:

    Within the context of computing one thing and passing 
    that result to the next thing with a common carrier 
    object of "foo", do bar, then baz, then quux within
    that context, each operating on foo.
In VB you could do:

    with someFile
        readIt()
        processIt()
        writeIt()
See the similarity?

By the way, monads are a way to abstract this even further so that not only can you model the context of "do something to a thing then do the next thing then do the next", but almost every other kind of context you could think of.

EDIT: as has been pointed out, this isn't really a monad, but it's an example of something monad-like that helps me grasp the larger concept better. Also, fixed the syntax.


It's not a monad, it's an ordinary macro. Also, you got the syntax wrong. What you wrote expands to (bar baz quux foo).

To GP: I've heard it called "double arrow" or "post-threading operator".


(->>) is not really a monad, though. It's a concatenation of endomorphisms, which sort of has the smell of a monad, but is simpler.


Not necessarily endomorphisms, though, is it?


True, and then it's a category.


Well, hmm, I don't think that's quite right either. There is a category where the objects are clojure types and the arrows are clojure functions, but I don't see how the threading operator embodies it. There could totally be some perspective I'm missing, though.


No, I think I'm just playing more fast and loose than I should. I don't think it fits into any particular semantic mould because it's really a syntactic thing—it is a list processing function that's applied to source code represented as lists. The basic usage pretty much traces out a path in the Clj category, so perhaps if you built Paths(Clj) then (->>) is a forgetful functor from Paths(Clj) -> Clj. In which case it's almost a monad, since if you play with Paths and (->>) you can turn them into a forgetful/free adjunction pair and make a monad.


Hmm, I think that's right. Heh.


a) yes

b) expanding macros for didactic purposes can also aid understanding

but mostly a)


You joke, but I've often thought how would one write a traditional recipe in a programming language in such a way that it would make sense. Same for "do it yourself" furniture.

Ultimately, I always fall back on programs not having a good concept of "consuming" or otherwise changing a variable. This is clearly doable, and elsewise it was referenced as linear typing (new to me).


C++ with references or C with pointers or Fortran with inout variables would accomplish this, just using normal procedural code:

  contents = open(&ramen);
  add_into( &pot, waterfactory() );
  add_into( &pot, contents );
  add_heat( &pot );
  sleep( RAMENCOOKTIME );
  tasty_ramen = open(&pot);


Right. I did not think I was stating anything grand here. I was simply pointing out that "imperative" mixed with "mutable" state is not something that is hard for users to grasp. Especially when limited in scope to single threads of action. The hard part comes in making sure this "type checks." That is, if you had it dependently typed such that you could not "add_into( &pot, contents );" until after you had "add_into( &pot, waterfactory() );" than you could conceivably catch more bugs.

The trick, to me, is that the "type" of "pot" is not fixed in this. Since I consider the "type" to be dependent on its state. I know this can be done with generic and higher order programming, but usually not with the same reference. That is, each call would create a new reference to pot such that the next one fed off of that. Thing is, that doesn't read well. (I mean, it does for toy examples, but doesn't for long ones.)

Make sense?


Sure.


I feel compelled to give kudos for by far the most readable of the "as program" directions here. Don't get me wrong, I can read most of the other examples, but this is the only one that really reads more true to how you would describe the process to someone and really underscores where I think a lot of the functional advocacy falls flat.


    type Recipe = Food -> Food
    serve . fry . addPan kale . addPan (dice onions) :: Recipe


What's the longest recipe you've seen ? 12 steps ? 32 ? are they nested ? do they guarantee the ability to share stove, shelves, ... ? do they guarantee anything at all ?

I wouldn't use the recipe metaphor except to explain what a computer does in less than 60 seconds to someone afraid of electronics.


Of course... how many programs do you have running right now? Do they do anything to guarantee they can each use the main processor in isolation to the others? What about the ram? The keyboard?

That is, the recipe for a single dish could be seen as a subroutine in the entire process pool for preparing a meal. Considered this way, what is the largest dining establishment you have seen? What is a menu, but a declarative listing of the strategies that an establishment can use to satisfy a customer's desire for dinner?

More directly, all of the metaphors of programming have almost direct analogies into the real world. "Worker" threads. "Queues" of "tasks."

As I've said elsewhere, I don't think I'm being particularly grand here. Just find it curious that, though we commonly have recipes and other directions that are mixes of declarative and imperative, the current trend in programming is to be purely declarative/functional. I wish I understood why that was.


Simplicity of recipes (they're blurry, they're not chemistry) , and constant human oversight (your brain takes care of many things not written down) makes imperative instructions feasible for small tasks like a meal. It won't scale, but since it doesn't have to it survives.


But why won't it scale up? This is essentially my question. And, it is not like there are not explorations of cooking in non imperative forms.

When your metaphor is "with these ingredients and devices, perform these transformations on them" an imperative form works really well. This is just as true for any algorithm you will want to write. Consider, write down any algorithm for how to do something. I challenge that key to your algorithm will be the idea of transforming something. Often in a "destructive" way.

An example, with the concept of a browser that has state, the following algorithm will get you logged in to your bank.

    browser = new browser
    browser.location = "www.yourbank.com"
    browser.focusFieldLabeled("username")
    browser.type("your name")
    browser.focusFieldLabeled("password")
    browser.type("your password")
    browser.focusButtonLabeled("login").or("submit")
    browser.click()
Implementing something that does this would be trivial, all things told. Now, do so without the metaphor of a "stateful" browser. Just trying to come up with "pure functional" implementations of each of these is not something that sounds pleasant to me. (I look forward to being surprised. :) )


About the scaling, my take is implicit dependencies expressed by order of statements. It's fragile, not composable, not abstractable.

Your browser example is completely imperative, it's a sequence of mutations. You just don't think that way in FP, you describe the new.

    (def url "http...")

    ;; url -> (html,session)
    (get url)

    ;; (url, form, session) -> (html, session)
    (let ((form {username  ...
    	 	 password  ...}))
	(post url form))
You can thread these calls to pass the http state for you.


Right, my argument is that the imperative form is much more easily reasoned about in this example. It is not as easily decomposed, but the question comes down to how much it needs to be.

To wit, I don't know how I would actually fill out the implementations of what you described such that it really worked. Without actually just manipulating the state of some browser thing.


Have you seen Rich Hickey talks ? he mentions the notion of ease in one, and the notion of mutation in many others. An html browser right now is a large stateful tree, imperative programming so it is easy to think about and write code this way, while FP orientation would need to rethink things, less easy, but probably simpler. In clojure data structure are immutable, you "only" compute new, I think that's how they designed a web app in http://pedestal.io/ (clojure[script] based system). If you really care about accepting small grained input you'd probably write some read eval/validate print loop on the client side before then sending a main request for a http json patch.


Right, but my assertion here is that this really only works because Hickey has basically put forth "mathematics" as the core metaphor for the things he is building. That is, he effectively wants an identity equation for every moment of time in the application.

This idea that "state" is somehow hard to reason about is one that I am growing to reject. Sure, if it is not part of the metaphor that one has built, it can be tough. But this is also true for what many of us mean when we say "mathematics." What is the equation for how you get home every day? How do you calculate what represents you yesterday?

Look at some of the classic algorithms. Is quicksort really that tough to reason about because it is in place (and thus relies on mutation)? Bubble sort? (Any sort...)

Is cleaning your room or office truly difficult to reason about because it involves modifying the room? Fixing dinner, since it involves modifying the ingredients?

This is what I loved about that paper linked up thread. When you build your abstractions around the ways in which things interact by mutations, then the code gets much clearer. This is not because we wall off the mutations, but because they are given names and reasoned about. It is not that we constantly have new instances of a list or collection with the old one hanging around, as that can be just as problematic as otherwise.

Consider, sure this is an obvious error to a casual programmer in Java:

    String x = ...
    x.toLowerCase();
    doSomething(x);
But, why is this a mistake? Wouldn't it be MUCH more intuitive if you wanted to keep both values the code would look like:

    String x = ...
    String original = x;
    x.toUpperCase();
    doSomething(x);
Just from a communication perspective. That there really isn't a "focused" object in languages often strikes me as something that could be addressed. (And, I believe this is the key of stack based languages, where they actually would read like I indicated.)


I think state centric system have been the main metaphor since data is processed through machines. Maybe Hickey and others are living in one side of the universe, but he did use other systems before creating clojure, and does not reject all state, just accidental/unnecessary state.

Sorts are not a good example here I think. QS correctness is uncoupled from its in-place property[1]. And they're encapsulated processes, nobody is supposed to see intermediate internal state. This black-box state is not the problem addressed[2]. All your examples are "single threaded". Two blind people moving chairs around will be a problem when one will try to sit down where there was one. Mutation and sharing is the problem otherwise nobody cares. That's why temporary variables are nasty in imperative friendly languages, any variable became an opportunity to mess up data needed by someone else.

It's also a logical waste, as in the first Java example, you don't need to compute the uppercase variant of a string unless it's needed down the road, if it is required then you pass it as argument to doSomething, that's what function are for. I tried to, but I'm not sure I understand the point of the second code example, because java references means x and original are the same, if you want an in-place uppercase method then both x and original are now uppercase letters, but you want to keep both values ... (Not really communication friendly)

The "focus" object would be a way to denote a place where all methods would accumulate state change ? I never saw this in the light of stack based language, it's interesting.

[1] the FP expression for quicksort is obscure the first time, but when you click you can't "unsee" it [2] many lisp books explicitely say that this is acceptable because it doesn't leak out of the functional interface, referentially transparent as far as calling functions are concerned


The logical waste, to me, is that after I've forced something to uppercase, I probably don't care about the non-transformed version anymore. Making use of it is as likely to be a mistake as otherwise. Consider all of the times you see people modify a list in LISP and accidentally continue to use the previous version.

Yeah, the focus object, to me, follows naturally with the way we do imperatives in natural language. Though, I guess there we have prepositions and such, too. (Are there programming languages with prepositions? I've seen creative use of them in testing libraries.)

And, ultimately, what I want is a system where a reference essentially tracks the state of what it references? Functions on objects, then, would be tracked not just in terms of input/output, but whether they affect the item the are operated on? (This make sense?)

That is, few people would expect the following "program" to work:

    rocket = new rocket()
    rocket.launch()
    rocket.addFuel(fuel)
I have seen fancy usage of generics to have it such that launch could not be called on a Rocket<NoFuel> instance. This makes it possible to possibly do chained calls that would work as expected, but chaining quickly gets cumbersome. Especially if you want to do some things unrelated to the state of the rocket in there at some point.

Do you know of any such systems?

Also, apologies or not addressing the QS stuff. Wrote all of the above and realized I didn't touch it. :( I simply meant that most "cute" QS implementations you see in functional languages (and, hence, readable) are actually not the same algorithm, since they require a ton more memory.

And, yes, all of this is a shared state problem. Though, again, I think if objects had ways of indicating what functions change state and what "state" is required of an object, much of that confusion would go away.

Consider, if you have a team of cooks working, there is no confusion about when the "onion" that must have been diced is added to the pot. Pretty much any assembly line working area with many workers. A worker can do a spot check that the item they have is in the appropriate state for them to do their job.

(Apologies, I would like to clean up this post, but know I will have a hard time getting back to it. Hopefully it is at least mostly intelligible.)


The first paragraph is a non issue, either you care about the original string and thus you have a variable (1) or you don't (2) or compute it from another one (3)

    (1) String focus = "aaaa"
    (2) String focus = "BBBB"
    (3) String focus = oldString.toUpperCase();
I very rarely see list mutation in Lisp, I'd need an example.

The issue with a focus object is that it's cool to thread over one "object", but if you deal with more than one, you'll need distinguishable names, back to square one. What kind of prepositions did you see ? is like using 'it' as a generic variable name in closures/macros?

The first part of your state tracking idea really reminds me of Ref/Atom constructs in clojure.

The generic "subclass" to encode state categories as types is very nice, I toyed with it in college but I then lost interest in OOP (people were too crazy about GoF patterns), I don't know if it's used in production.

The smart cook is akin to prolog goal based programming, here we're quite far from state mutation. I wish there was more systems built on this.

ps: dont worry, my ideas are often blurry, as is my english.


I've seen many procedures that are essentially:

    public Blah foo(String in) {
        in = in.toUpperCase();
In shops where there is an insistence on final parameters, this gets tripped into

    public Blah foo(String in) {
        String upperIn = in.toUpperCase()
And then the mistake is to accidentally use the "in" variable later on. Or, simply in the first to forget to "reassign" the in to itself.

As far as list mutations, I'm mainly referring to intro work. Whenever working with someone that is new to scala/lisp it is common for people to not understand why appending to a list does not "work."

I'm embarrassed to admit I can not find the test library I meant. I believe it was "specs" basically. Looked something like:

    Foo foo = ...
    lookingAt(foo)
    it.shouldEqual(bar)
    it.shouldNotThrow(Exception).onCallofFoo
    ...
Where "it" returns some context style object with basic assertion ideas on it, and some return a "foo" to do calls on it.

Thinking of this in terms of a Prolog style machine did occur to me. Basically define your "transformations" and let the system search for the ones that could be used to go from the ingredients given to the outcome desired.


You do realize that these mistakes are made by people learning the immutable approach ? For people having started with functional programming or other systems (the first computational systems I used were lazy reactive computer graphics) you have close to zero confusion from beginning.

Beside if you want to thread modification in a cuter way, Clojure ->> macro allows

    (upperCase (first-three-letters string)) 
    to be written
    (->> string
         first-three-letters 
         upperCase)
More here http://clojuredocs.org/clojure_core/clojure.core/-%3E%3E.

There was experimentation for the 'it' style, but it's fragile in most code, thinking of with statements:

  - http://stackoverflow.com/questions/1931186/with-keyword-in-javascript
  - http://msdn.microsoft.com/en-us/library/wc500chb.aspx
They had non-intuitive side-effects hard to notice, so nobody use them anymore.

They're present in so-called anaphoric macros (which were popularized by Paul Graham OnLisp, also Doug Hoyte Let Over Lambda), see:

    https://github.com/magnars/dash.el#-map-fn-list
    https://github.com/rolandwalker/anaphora
Here the context is limited to single argument pure functions so a 'it' pronoun won't cause much weirdness.


I have heard claims that if you start people with the "functional style" they are less confused by these tracks. My scepticism goes back to just how many imperative "mutable" instructions people will have learned outside of programming. (That is to say, I'm curious how much research is into this question. Most of the people claiming one way or the other seem to have something to sell.)

Even the example you give, graphics, is somewhat interesting to me. It goes back to my thought where the "metaphor" of what you are doing is more math based already. In those cases, I fully expect a more rigorous "functional" approach to make sense.

And yeah, I actually thought this was sort of like the "with" syntax as I wrote my example. Couldn't remember the actual drawbacks of the with syntax right off. Similarly, I've seen a few times where you can use an import in scala to similar effect. Looks like:

    def foo(x:Bar) {
        import x._
        ...
    }
I thought this could make reading some code a little less cluttered. Haven't seen it in wide use, though. (And... I would expect it should be used with care.)

I can't help but lingering on the thought that this is dynamic typing where the "type" of a variable encodes the state. Seems if the compiler could track the state and provide indications to whether or not the desired state could be achieved, then the code could be clearer.

Consider the directions for driving cross country. The required "actor" of the system is a vehicle with enough gas. If at any time the vehicle does not have enough gas, the system performs the steps to return the vehicle back to the state of having enough gas, then continues the trip.

That make sense?


I'm selling my frustration. I experienced generic uncoupled UI for lazy reactive dataflow around the time I entered college, where I then suffered through C, Java where things quickly become dark magic. The clean thinking process of dataflow looked like a long forgotten dream that was weirdly close to what your code ends up doing, streaming data through transformations. But in imperative code it's obscured. Sure some people have the right view and the right brain (I believe more people before the 80s had this skill on average) and they can compile the idea into algorithmically clean imperative code where things don't step on each others (just like algorithms in Cormen), but I didn't met them often.

Graphics and other domains (sound, ..) had easier time finding an algebra to combine atoms and filters in arbitrary fashion to create complex results. I'm betting all high end (meaning hollywood) graphic software are lazy dataflow systems. But if you look at Photoshop, it's still imperative, and people are required to get skilled at managing dozens of layers to store intermediate results in case they wanna change something. This is pure madness. Find tutorials about Houdini where the artist builds its dependency graph as it fits, and if he something needs to be modified he just tweak the right upstream node and everything recomputes on demand.

Software is different ? not so much, Brett Victor demonstrated how you could develop a platform game in such fashion, lazy reactive source code with different dimensions/views. You can see the output of a trajectory function, change the code and observe the computed difference on the fly..

A lot of projects are trying to reduce the loop between idea / source / result. DevOps with testing/deploy vms (even storage systems are approaching functional idioms like btrfs, zfs, logfs), js client frameworks with databinding .. all the same. It's not everything though; a friend pointed to me that sometimes you need to research an algorithm, it won't be large so this won't help you understanding the problem faster.

The type encoded state is not imperative programming anymore, as I said, to me it's akin to prolog.


> As I've said elsewhere, I don't think I'm being particularly grand here. Just find it curious that, though we commonly have recipes and other directions that are mixes of declarative and imperative, the current trend in programming is to be purely declarative/functional. I wish I understood why that was.

Ever read a recipe designed for concurrency and/or parallelism? Functional idioms are becoming in vogue at the same time as people are meeting walls when it comes to single threaded applications.


Are you kidding me? Of course I have. Hell, the example I gave was. It begins with "begin heating oven..." This is not a task you do and wait for it to finish before you do the next step. Then there is the entire concept of fixing not just a dish, but an entire meal. If you have ever seen a recipe with "while the onions are cooking..." you have seen a recipe with parallelism.

Seriously, sometimes the blindness of our field to just how much we share with other fields is very troublesome.


Touché.


Rereading this thread, apologies for my tone on that post. I do hold my assertion that recipes have both concurrency and parallelism, but there was no reason for me to be so glib about it.


Often reference books end up suggesting practices that amount to what function based programming is. (thread safe C or Java)


Obviously at some point you need to get something done. The question is how you organize your data and your actions.


Not sure I follow. I'm essentially asking how this question is addressed with the single word answer of "functionally."

I think a lot of this comes from the terrible division of "layers" that we have introduced in most common metaphors. Often we have to go through gymnastics to get all "mutations" back to a base layer where we can view them as "events" and then have pure functions that operate on these events.

Consider, the common division of "Storage, Logic, Display" that we try and hammer out in some form or fashion. It becomes all too often that one can not come up with a good way of describing these systems where events only propagate along a single direction in these layers. As such, we often find small silos of "storage" in the logic and display area. (Similarly, we have a small silo of "logic" in both storage and display, quite often.) Coming up with a mechanism where there is a single "storage" location for an entire application often introduces complicated logic.

Carmack had an interesting take on this idea for games programming, where the current logic "state" would be held immutable such that all calculations were only allowed to work on the current state. Kept those calculations easier, but introduced a new layer of effort to make that possible. Now, I think his argument was that this layer of effort could be seen as almost a constant factor that has shrunk in cost compared to the scaling cost of the number of actors involved in a modern system.


Well, take that Logo procedure. If all I want to do with that list of commands is execute them, then a fully imperative language (like, as best I recall, Logo) is fine. Once I want to do slightly more complicated things (say, repeat) you need to move beyond "simple list of things". Of course, for a lot of things you might want to do to a list of commands (obviously including repeat) there is built in support. But what about when you want to define new ones? "Do each thing in the following list twice"? In Haskell, if you're passing around [IO ()], you can run it through (concatMap (\ x -> [x,x])) before passing it to sequence, and bam - things are doubled.


Not sure I really follow. Every example you gave is just as intuitive in an imperative form as it is otherwise. Likely more. You want to teach someone how to do a dance? Teach them the few basic steps, and then start showing them how to interlace said steps.

Hell, ever looked at the instructions for making your clothes? They speak in the language of seams and stitchings, yet are able to put together a whole clothing quite nicely. Even have room to adjust to things going wrong such as a thread or needle breaking during the process. (This is one area where exceptions have always seemed to me that they should be the continuation use case. Keep going until you hit an exception, fix the cause, then continue from where you left off.)


I don't think you did follow. Nothing stops you from listing actions, in any language. In a language where actions are executed just by being listed unless particular magic constructs are used, you are more limited with what you can do with sequences of actions. Again, try to write something in logo that takes a list of arbitrary actions, and runs through the list executing each action twice before moving on to the next.


But isn't this already solved in an OOP world? So long as your "list of actions" is against a target (the "metaphor" in my original post), you can accomplish what you are referring to by manipulating that target.

To go with my dance suggestion:

    function dance(dancer) {
      dancer.stepForward
      dancer.crossStepForward
      dancer.crossStepForward
    }
Now, before the dancer is passed to this function, it is first passed to a "goOnPoint" function, such that now those directions are taken while on point. Or a "do all steps twice" function, for your direct example.

The cooking metaphor is a little more directly applicable here. The cook is just configured to double (or half, etc) all ingredients used. Heck, the cook can even be configured such that substitute ingredients are known. Consider, it is trivial to use soy milk instead of regular milk for many "recipes".

I actually like that example, because it doesn't always work. In a sense, it doesn't "type check." Some recipes have to basically be flagged as "milk substitutes not applicable." I fail to see how a monad really helps there.


Sure, you can go around and implement each special handling in every object that accepts actions. That potentially a lot of boiler plate, though. The functional approach is to view the actions as first class objects and operate on them before sequencing them together, whether that sequencing is done by an object in IO.

> The cooking metaphor is a little more directly applicable here. The cook is just configured to double (or half, etc) all ingredients used. Heck, the cook can even be configured such that substitute ingredients are known. Consider, it is trivial to use soy milk instead of regular milk for many "recipes".

> I actually like that example, because it doesn't always work. In a sense, it doesn't "type check." Some recipes have to basically be flagged as "milk substitutes not applicable." I fail to see how a monad really helps there.

Again, the problem there is that you have to write your cook such that it can perform whatever transformations might be needed, rather than having the ability to apply arbitrary transformations.

Regarding the type checking, I don't see how OO helps you there. In Haskell, monads (with type inference and typeclasses more generally) are absolutely a solution:

    instance Monad RecipeCanSubSoymilk where
        ...

    instance Monad RecipeNoSubSoymilk where
        ...

    instance Recipe RecipeCanSubSoymilk where
        ...

    instance Recipe RecipeNoSubSoymilk where
        ...

    noSoy :: RecipeNoSubSoymilk ()

    milk :: Recipe r => Quantity -> r ()

    flour :: Recipe r => Quantity -> r ()

    subSoymilk :: Recipe r => RecipeCanSubSoymilk a -> r a

This lets us say:

    subSoymilk (milk 10 >> flour 5)
but provides a type error when we say:

    subSoymilk (milk 10 >> noSoy >> flour 5)
and this will propagate through any depth of functions &c that we use to build our recipe.

Though monads are overkill in this case: a list will do just fine, since we never need the result of a step to determine what to do next. But it's nice to have the flexibility when needed.


Sorry, when I said I fail to see how a monad helps here, I meant more in the "keeping the algorithm clear" sense. Simply put, there is a reason cookbooks do not resort to the idea of monads to convey directions.

Thinking more on this, I am beginning to think a multiple phase "type checking" would make the most sense. Essentially, have the declarative ingredients list, the imperative commands, and optionally any declarative constraints. I strongly feel that the use of nominative subtyping is a very bad idea for the "constraints" of what goes into an algorithm leads to hardly readable stuff.

I grant, though, that it may be more fruitful to train one's self to read these "hardly readable" things than it is to opine the lack of a better alternative.


You're conflating and confusing a whole ton of things, to the point where I don't really know where to begin...

There is "monad as a mathematical concept", which cookbooks don't "resort to" discussion of because a rigorous treatment of their internal logic isn't what cookbooks are about. A logician sitting down to describe and analyze the processes in a cookbook may well make reference to monads. Cookbooks don't care much about such description and analysis, because recipes are tremendously simple (compared to software), they rely on a ton of implicit knowledge, and they can be exhaustively tested.

There is "monad as an interface as it is used in Haskell", which cookbooks don't "resort to" because they are one particular DSL and the sole relevant context is implicit by the fact you opened a cookbook and are looking at something laid out like a recipe. It's not terribly difficult to characterize a recipe as a monad, though if you wanted to do so precisely you'd probably also want linear types (since you can't chop the same onion twice).

Regarding your later points of division of "imperative commands" and denotational constraints, I actually agree with you that splitting these but allowing some specification of both (I would actually broaden "imperative commands" to operational constraints generally) is going to be a worthwhile avenue in the medium to long term. I think, though, that it will look more like writing things out denotationally based on your actual goals (plus some operational constraints regarding time/space/resource bounds) and then filling in operational details as the compiler is unable to come up with how to do something on its own in a way that meets the requirements, as opposed to detailing how to do things and checking constraints.


When I say "resort to" it just strikes me how in programming today we are going out of our way to throw these constructs up in the face of the programmer. That they could be used to describe what is happening, I do not argue. That they are more rigorous, I also do not argue. That they universally help people reason about things, I do argue.

And this is the second time that linear types have come up in this thread. They do seem like what I want, but I want more "state." That you can only dice an onion once, does not prevent me from still referring to that as "the onion." Ideally, I would want a variable where the specific type is altered and checked at each occurence. In the imperative world, the progression of time would make dictate what state/type is relevant at any given point.


I think a part of it is that you're restricting the discussion to examples too small to be meaningful.

Does the fact that IO forms a monad help us, when all we're doing is sequencing actions in a static order without interesting interactions? No, not really. It also doesn't hurt.

    main = do
        let dry_ingredients =
                [ (Flour, 10)
                , (Sugar, 5)
                ]
            butter = (Butter, 1)
            eggs = (Egg, 2)
            pan = CakePan

        oven_heating_task <- heatOven XXX
        bowl_contents <- withBowl $ do
            mapM_ add dry_ingredients
            mix
            add butter
            whip
            add eggs

        waitFor oven_heating

        bakeInPan pan bowl_contents

You don't even really have to understand monads to follow the above code. They also don't do much to improve it over ordinary imperative code. (They arguably do quite a bit to improve it over the "lazy streams of requests and responses" approach that early Haskell took...)

Where functional style and a robust type system becomes a huge win is in larger programs - and, in particular, significant refactors of larger programs. If you've done a few of these in a few contexts, you should have a good sense of why.


Oh, certainly there is a large aspect of "the problem is simply the examples." I have yet to see a compelling system built in the functional style, is the problem.

It is tempting to feel that there is this holy grail where this becomes a big win. I even can "intuitively" see how this would be true. However, what I also experience is that this 'win' seems countered by the 'loss' in just how much cognitive overhead goes in to building said large system. That is, sure, it can be modified with more confidence, but was it ever fully built? (I think this comes from the caveat shown in upthread paper where, to stay truly transparent the developer winds up having to account for the entire state of the system at some layer.)

Even looking at my own examples where I was somewhat proud of the amount of information that was in the type system, I can not honestly claim that the product was more bug free than loosely typed version, and it certainly wasn't more clearly communicated to my peers. (Including my future self.)

In the end, smaller more directed pieces using whichever styling makes the most sense for that section seems to be the clearest win.

Edit: Apologies for the quick edit, but I am also struck by just how well the "literate" programs I have ever read actually worked at communicating what was going on. Shame that seems to have stalled to death.


Well, I'm saying that I've very much seen it, even in the moderately sized projects I've been working on recently. And yes, they get fully built regardless of which way I'm building them.

"Even looking at my own examples where I was somewhat proud of the amount of information that was in the type system, I can not honestly claim that the product was more bug free than loosely typed version, and it certainly wasn't more clearly communicated to my peers. (Including my future self.)"

I find even in my C code that setting things up to better leverage the type system (even as limited as it is) has been a tremendous win. I'm a much stronger proponent of strong and expressive types than I am of functional programming (though I think that both provide wins in most cases).

"In the end, smaller more directed pieces using whichever styling makes the most sense for that section seems to be the clearest win."

No argument there.

"I am also struck by just how well the "literate" programs I have ever read actually worked at communicating what was going on. Shame that seems to have stalled to death."

Yeah, genuine literate programming tools are fascinating, and it's a little sad how a lot of the ideas have kind of dropped away from a core of "automatically generate docs from inline comments", which seems to have been a small part of the original. I've been toying with the notion of adding some similar functionality to some tooling I've been building, but no concrete plans yet...


I think the hard line to see is between "better leveraging the type system" and "trying to fit it all in the type system."


No one suggests the latter; at the limit, it's provably impossible.


Most decently popular posts about functional programming seems to have a good deal of meta-comments about functional programming such as "is functional programming even useful?", "it will take over the world!", "My father didn't need it, my brother didn't need it; I don't need it". Sometimes, the discussion thread is almost only meta-discussion of functional programming. Just an observation.


Note, that I am decidedly not saying functional is bad. Just questioning why we do not focus on mixed usage of it and imperative. We used to. We don't any more.


Rich Hickey has a great description of how to think about working with immutable values - what we usually want when we change a variable is simply the next value and so long as we are getting the value we expected, there's no need to name it.

In other words if our current position in an array is 2, what we need to access the next position is the value 3. Creating a variable int i=2; and then mutating it i++; introduces the possibility of side effects. This not to say that at an abstraction layer below our programming language a register won't get incremented, only that our brains don't need to worry about the mechanisms most of the time if we use a functional language.


Or a language with a foreach construct...


Exactly.

There's nothing magical about functional programming - it is no more than syntactic sugar to help programmers control the state of computing devices.

Foreach is a higher level abstraction that makes reasoning about our code easier. The bits still change.


Functional programming has deep semantic implications. Calling it "syntactic sugar" is incorrect. Foreach in particular is arguably syntatic sugar, though, to be sure - functional or not.


> There's nothing magical about functional programming - it is no more than syntactic sugar to help programmers control the state of computing devices.

In the same way that objects in Java are just syntactic sugar for encapsulating state. In the same way that a compiler is just the machine that takes a language of the syntactic sugar of language A to the actual language A.

> Foreach is a higher level abstraction that makes reasoning about our code easier. The bits still change.

So how do you write a Foreach loop from a regular for-loop in your typical imperative language? How do you compose that Foreach loop to make even higher level abstractions like map, filter and fold/reduce?


> So how do you write a Foreach loop from a regular for-loop in your typical imperative language?

By walking through the collection in the update step of the for loop (for languages with less-general version of for than C and friends, this may actually require a while loop rather than a for loop.)

> How do you compose that Foreach loop to make even higher level abstractions like map, filter and fold/reduce?

By abstracting it behind a function call that takes a function (or function reference/pointer, or in particularly limited languages a location to jump the current instruction pointer to) as an argument.

Yes, languages that focus on the functional paradigm (or which have been influenced by it even if it isn't their central paradigm) have syntax which makes this more straightforward than it might be in some other languages,


In the case of foreach, it's simple to be sure, and I'm pretty fine with calling foreach "syntactic sugar". Even something as simple as function composition breaks down a bit in C. You can manage it, but you're either not really using C anymore (writing out machine code in memory and treating it as a function, which is going to be tremendously compiler and architecture specific) or you're not really dealing with C functions anymore (passing around a structure that stores the things to compose, "calling" that function with a separate "apply" function).

Yes, you can ultimately write all of Haskell in C and code at the higher level, exceedingly verbosely, but to say that's "just syntactic sugar" would be absurd - "fundamentally all these languages are Turing equivalent" already encapsulates that observation, there's nothing new the notion of "just syntactic sugar" is adding.

Typically I restrict "just syntactic sugar" for things that are simple transformations that can be done locally. The archetypical example being array syntax in C, which can be described more precisely by a syntactic transformation than a semantic operation:

Given:

    char *c = "abcd";
    int i = 2;
It turns out that:

    a[i] = *(a + i)
but that's equivalent, by commutativity of +, to:

    *(i + a)
and so, counter-intuitively if you're thinking of [] as a semantic "array indexing" operation, you get the same results with:

    i[a]


> In the case of foreach, it's simple to be sure, and I'm pretty fine with calling foreach "syntactic sugar".

Yeah, I was responding specifically to the "foreach" and specific things layered on top of foreach, which are fairly straightforward in most popular non-FP languages.

> Even something as simple as function composition breaks down a bit in C.

Quite. No argument there. At least with standard C. (I think Clang and GNU C both have extensions -- but not the same ones -- that make this reasonably straightforward in simple cases, but still much less elegant than, say, Haskell.)


Or a language with the ability to create your own such constructs!


For sure - and that is the strength of functional languages. Citing one example of functionality that, nowadays, most languages have built in doesn't make much of a point.


As someone who still doesn't get functional programming, I would like to see a real-world example of how to deal with side effects. Eg: write something into a file or a DB. Because of course methods/functions with no side effects are easier to deal with, it sounds good, but I don't think it would be easy nor convenient to separate every side effect in a real-world program (as opposed to academic programs, where you just write a Fibonacci or whatever). Even in good OOP, you can have lots of side effects in a method.

Halp!


The idea is to generate side effects that don't effect program flow. This keyboard generates side effects as I type because it changes the state of the display. But pressing the "B" to start this sentence did not change the way the keyboard operates - each key press produced the same results as it would had I pressed "H". And had I pressed "H" and begun the previous sentence with "However," the screen layout would have been somewhat different, but not in a way I typically care about when writing (as opposed to doing typography).

Now this is not to say that my keyboard is purely functional. The shift key creates state. But that state is isolated and short lived to the point where we can treat it as functional across two key combinations. It is the caps lock key which creates an unpredictable state - I do not know what output I will get without checking its status - the problem with state might be illustrated by the results of hitting capslock when I meant to hit tab while typing a program.


So how should you program the capslock? You (the functional guys) keep telling me what's wrong, but you don't tell me how to do it right. Not having a capslock key is not an option, people need it.


If it's necessary, program it as implicit state in a recursive loop, or as an FRP signal, or as one of several varieties of mutable cells which have automatic nice multithreading stories.

FP has mutable state.

People keep saying it doesn't because it's the first chance they've had to play with a keyboard that doesn't have a caps lock and they've realized that the number of times they accidentally TYPED A WHOLE PARAGRAPH IN UPCAPS has gone to zero and that tiny mental burden of tracking the state of the keyboard has vanished.

Removing mutable state improves trust.


Because the purpose of capslock is specifically to create stateful behavior, you program capslock exactly as it is now (or you eliminate it from the keyboard as is advocated from time to time).

It's no different than reading from a file - the state is out in the world and to be useful, a functional program must deal with it.

But note that the key difference is that the programmer does not create the state - it comes from the data. Probably also worth noting that the keyboard is used as an illustrative analogy not as example code.


Actually a lot of people have decided they don't need it. They remap caps lock to something like spotlight search. The alternative to the caps lock key is the shift key. And this is a really bad metaphor for functional programming. Sorry GP.

I suppose the important point here is it's not possible for something going wrong with your monitor or speakers to suddenly remap your keyboard without warning.


I'm not saying it's a great analogy, but among it's strengths is handiness.

Among it's other strengths is that functional programs can be built bottom up from lots of little pieces and when we get unexpected results, we can track them down. If suddenly my keyboard's "b" key stops working, the problem is almost certainly with the switch beneath it and not because of something the "h" key is doing.

Another strength of the analogy is that the most disorienting behaviors of a keyboard are tied to state: the worst one for me is numlock off where the behavior shifts to dependence on the state of the display.


You may enjoy Jef Raskin's "The Humane Interface" assuming you haven't already read it. It expounds at length about this.


Unfortunately, in most keyboard, the `h' key might affect the `b' key. See n-key rollover.



In the end, there is always some kind of side effect. The value that functional programming adds is you keep the part of the program that does the side effect in one spot, doing one specific well defined thoroughly debugged thing and you limit its scope so it can't screw up the rest of your program's operation no matter how bad it goes wrong. It only goes "out". it doesn't cause any side effect inside your program.

One example of a way to structure a functional program is categorising functions according to whether they are a "source" a "filter" or a "sink", (terms stolen from circuit design). The source being your inputs (not pure), filters doing some transformation on the input, over time (pure) and handing off to a sink that causes some side effect (not pure).

In the "real world" You've probably made a lot of simple programs that mostly follow this structure if you've used unix pipes.

If you go along with this, you find that once you start making more sophisticated programs, you need some kind of "memory" so you can progress. Strictly speaking, memory, as we typically use it in imperative languages, is a kind of side effect. In the model above, though, you can make a special kind of "source" which is something like a feedback loop- a source that contains the output of the previous iteration of the program- or some sub portion of the program. Then maintaining a memory becomes a process of continually taking some input, transforming it, creating an output, and then using that output as an input again. (what you might call recursion)

Why would you do this? Because each part of a program like this is self contained, and its ability to do damage to some distant part of a program is extremely limited. It becomes much easier to reason about such a program. When you find a bug (and these are still possible) you can trace the origin from output to source, or source to output. There is no possibility of some phantom distant subroutine screwing up the dataflow.

And this kind of structure is very useful for games, interactive multimedia programs, and other such graphical interactive program.

A commonly cited "real world" example is xmonad. What I find more insightful is this article on replicating pacman in a functional language. http://prog21.dadgum.com/23.html


"Side-effect free" doesn't mean there are no side-effects, it simply means that their existence is controlled, separated, and minimized. For instance, in Haskell, you have the pure function "unwords . map upcase . words" which, reading right to left as is the convention for function composition, breaks a string on whitespace, upcases each chunk, then glues them back together with spaces again. It's net signature is "String -> String".

By itself, this function is useless, but "interact" is a function with signature "(String -> String) -> IO ()" which means that it turns our "unwords . map upcase . words" into an interaction with the environment. There are whole debate here as to how interact is actually still pure, but for the purposes of this argument, interact is an impure effect.

The net program however is very simple: "interact (unwords . map upcase . words)". The pure part is decomposed into many other pure pieces, each with simple, small contracts and independent testability. The impure part also has a simple contract which is described in terms of pure computation.

Another great example is some of the streaming libraries available in Haskell today. Pipes for instance will let you compose a pipeline of pure and impure computations and have them work together nicely.

"fibsPipe >-> Pipes.take 20 >-> Pipes.print"

The first two segments are pure, but have empty "placeholder" slots for impure effects (identity monadic layers). These get merged seamlessly with the "Pipes.print" segment which has impure effects. All together, you get an infinite producer "fibsPipe" producing an impure effect "Pipes.print" while having its resource utilization and termination controlled by another pure computation "Pipes.take 20" which is actually maintaining its own implicit state (the current count of numbers it's let through).

But then you can decompose each piece and understand it on its own in a pure or simple impure fashion.


One thing I find myself doing over and over is taking as much logic as possible out of side effecting functions and placing that in a pure function, then passing the return value to the side effecting function.


I'm not a Haskeller, nor do I write large amounts of software, but the one thing I took away from my very short foray into Haskell is that you want all of your state management and IO in one place. Clojure doesn't enforce this on you at all, but ever since learning that concept, my Clojure code has become an order of magnitude cleaner and less buggy.


I suggest to dig in some web framework for functional programming. One example would be Snap for Haskell. http://snapframework.com/


Does it make sense to have to learn Haskell and a Haskell framework just to understand Clojure? Because I'm already sold on the idea that Clojure is the best language™, being a lisp, portable, with a great community, already with a mature library for everything (because it has access to the JVM), and with some dirty shortcuts for convenience.

But the frameworks are lacking. The only web framework available got dissolved, and I don't have the energy to wire everything myself in a new language and paradigm that I don't even fully understand).


I don't get it; you say you're sold to Clojure for it (among other reasons) "being a lisp" but at the same time you "still don't get functional programming"? If you don't get functional programming, why is it being a Lisp a good thing to your eyes?


I can relate to being frustrated with the lack of "real" io-examples when learning lisp, while at the same time seeing the benefits of functional programming, macros etc.

With something like clojure, you at least have a pretty clear deployment story (make a jar, drop jar in a container) -- but it's still not clear how you would best go about writing something like berklydb in clojure - never mind in SBCL (come to think of it, even implementing a couple of text handling utilities like grep or cat is pretty hard to wrap your mind around when working with "old school" lisps).

All that said, I'm pretty excited about racket, and somewhat hopeful that I'll eventually get around to doing some "real" functional programming in the not too distant future.


I don't yet "get" functional programming, but the reason I am reading this thread is that for years I have read about Lisp being a good thing (a language I have always intended to learn when I get the time). I think there was some (famous?) essay that talked about an enlightenment when you finally "get it" as a language.

Clojure seems to be a modern Lisp implementation. Runs on the JVM, has lots of libraries available.

How else will you "get it" without learning a functional language?

That's why I want to learn Clojure without "getting functional programming" yet.


> Does it make sense to have to learn Haskell and a Haskell framework just to understand Clojure?

I have a strong feeling that once you understand either Haskell or Clojure, it will be a breeze to learn other.

My point was to get framework for functional programming that deals with a lot of I/O and side-effects and see docs for real-life examples of usage.


In my personal experience having learned Clojure before diving deeply into Haskell, it helps but there is long path between them still. Hs types are incredible and completely missing in Clj.


> I have a strong feeling that once you understand either Haskell or Clojure, it will be a breeze to learn other.

Does Clojure use lazy evaluation?


It also isn't statically typed.


Not everywhere, but most of the sequence functions like map/filter return lazy sequences.


> The only web framework available got dissolved,

Rather, turned from a framework into a library. I only recently started with Clojure and found Luminus pretty easy to pick up. It just generates a simple scaffolding project which cherry-picks some good libraries into a project.clj file and gives you a basic "Hello world" web page to get started. Primarily it uses lib-noir on top of Compojure, so pretty much anything that was in Noir, you get here (I guess, I never used Noir directly). Luminus has a pretty good tutorial to cover basic notions of handlers, databases, etc.

No, it's not Rails or Django, and no it doesn't do anything you could do yourself from scratch pretty easily. It is, however, a nice starting point and may help you get over the "I have to wire everything together myself" hump.

http://www.luminusweb.net


Does it make sense to have to learn Haskell and a Haskell framework just to understand Clojure?

Haskell is a very different language from Clojure and learning it will teach you different lessons. Haskell takes an uncompromising approach to purity which can sometimes be frustrating to manage. Haskell's type system is an incredible piece of work, one which some may dearly miss in other languages (Clojure's core.typed does help a lot in this regard).

But the frameworks are lacking.

From what I gather, the Clojure community (and the functional style and community in general) abhors frameworks. Small, reusable, composable libraries are the order of the day.


>Does it make sense to have to learn Haskell and a Haskell framework just to understand Clojure?

No, but it makes sense to learn haskell to understand functional programming, which is what you asked. Clojure's emphasis on purity is essentially useless without a type system to go with it. The type system is what separates the pure functions from the impure ones.


bullshit. Referential transparency is the only requirement. Purity has nothing to do with the type system.


I don't understand what you are trying to say.


Do you not understand what referential transparency is?

You can write "pure" functions in imperative languages. Any function which returns the same result for the same arguments is a pure function. You could write an entire pure functional program in C if you wanted.


>Do you not understand what referential transparency is?

Yes. I also understand what a petulant child throwing a tantrum is. Nothing you have said is relevant to my post, it is just misguided anger.


> As someone who still doesn't get functional programming, I would like to see a real-world example of how to deal with side effects. Eg: write something into a file or a DB.

Plenty of that here: http://book.realworldhaskell.org/read/


Compiler is a good example of big program that's suitable for functional programming. You have specified input and output, no need for interactivity, and you can localize all mutations of state in one place (writing generated files to disk).

Other batch processing programs works as well.


This is true. I'm currently working on a research compiler targeting GPUs. It's 7500 lines of Haskell with all comments and blank lines removed, and the only impure part is the command line interface. Avoiding IO is not really painful for these kinds of programs.


Using side effects in Haskell is easy. The only thing is that they need to be explicit and you aren't allowed to hide them inside pure functions.

For a database example, here is the chapter from Real World Haskell about databases (a bit old but still relevant):

http://book.realworldhaskell.org/read/using-databases.html

And here is some example code:

    do
      conn <- connectSqlite3 "test1.db"
      stmt <- prepare conn "INSERT INTO test VALUES (?, ?)"
      execute stmt [toSql 1, toSql "one"]
      execute stmt [toSql 2, toSql "two"]
      execute stmt [toSql 3, toSql "three"]
      execute stmt [toSql 4, SqlNull]
      commit conn
      disconnect conn
As you can see, it looks very similar to how you would write it in a regular imperative language. The main difference is that since they database operations are IO operations then they need to be in the special do-notation block and cannot be mixed with regular code.

As for the question of wanting to mix pure and impure code that much I don't think it actually comes up that much. First of all, I find that I don't often find myself with an impure function that I want to add a side effect in the middle seamlessly - firstly, side-effecting stuff tend to be side-effecting from the beggining and secondly if I do want to turn a pure function into an impure one then forcing me to change the interface helps make sure that I am not breaking any code that used to work because it assumed it was working with pure code. Additionally, Haskell is really good at abstracting code so its not that hard to take a big chunk of logic and split it into a pure and an impure part. Finally, converting code from pure to impure is not that bad - sure, you need to change the syntax a bit but the type checker helps you find all the places that need to change and also helps you check all the code that calles the method you changed so you can be sure that you aren't breaking those.

side effecting operations in the IO monad so you can't run them in the middle of any function and instead must run them inside a special "do-notation block" like I did.

You can have impure code call both other impure code and pure functions and all you can't do is call impure functions from inside pure functions. Sure, its a bit annoying if you ever have to convert a pure function to an impure one (you need to change some of the syntax and all the places that call tha tfunction need to start treating it as impure) but its not that bad: first of all the type checker tells you all the spots where you need to update your code and secondly, Haskell is super good at abstracting code so its not hard to take a big chunk of code with some side effects in the middle and split it into a pure and an impure part.

> to academic program

Its a little pet peeve of mine but it seems that nowadays "academic" doesn't have a real meaning and just tends to refer to whatever people don't like. :)


"do" is just syntax. You could write the above as:

    connectSqlite3 "test1.db" >>= \ conn ->
        prepare conn "INSERT INTO test VALUES (?, ?)" >>= \ stmt -> 
          >> execute stmt [toSql 1, toSql "one"]
          >> execute stmt [toSql 2, toSql "two"]
          >> execute stmt [toSql 3, toSql "three"]
          >> execute stmt [toSql 4, SqlNull]
          >> commit conn
          >> disconnect conn
and it's "just" a bunch of nested lambdas with no "do" in sight. What's different from "regular" code is that those lambdas 1) return IO actions, and 2) are strung together with monadic bind (>>=) to build one big IO action.

To be sure, in this case the do version is much easier to read (that's why "do notation" exists). It can be used with any monad, though - nothing ties it particularly to IO.


Maybe I should have been clearer but the basic point is that monadic code has a different interface

    bar (foo x) --pure Haskell
vs

    foo x >>= bar --monadic Haskell
vs

    bar(foo(x)) -- in C you write both cases like this.


You're close, but it's not really so clear cut.

foo x ++ bar -- "pure" or "monadic"?

foo x >>= baz -- "pure" or "monadic"?

What if (foo x) is a list in both cases?

(>>=) is just an operator. The values that it operates on are anything with a Monad instance, just like the values that (+) operates on are anything with a Num instance. There is nothing voodoo about monads or about bind (>>=); such voodoo as there is lies solely in IO, which is interacted with the same way as other values (though certainly what you can do with (IO a) is more limited than what you can do with (Maybe a)).


I used to think that Monad was some alternative realm in Haskell, a realm where purity didn't exist and everything was written in an alternative, built-in language (do-notation). That's because people tend to refer to using Monads as "working in the X-Monad", as if it is some... place. But then it turned out... Oh, so it's basically just a signature/interface/type class.


True! But one neat thing is that, if you have a black-box abstract data type then you can force people to use the provided Monad interface if they want to do stuff with it. The IO monad does exactly this to create its "alternative realm"; there is no way to get "out" of IO once you get in , the only way to run an IO computation is to have it be directly or indirectly called by "main" and the only primitive way to compose IO computations is with the monadic combinators.


I don't know... I think both perspectives (monad as context vs. monad as data) are valid and useful.

That said, the additional cruft (purity doesn't exist, do notation is mandatory, &c) is well worth shedding.


You probably don't need Yet Another Reply to your question, but I can't resist.

As others have noted, there's really no such thing as a "purely functional program" unless all you're looking for is turning your CPU into a space heater. That said, there's a very real sense in which the parts of your program that ARE pure are much safer and easier to reason about.

The best way to visualize this is that you can theoretically turn an impure function into a pure one, watch:

   void printAGreeting(string greeting)
   {
       IO.printLn(greeting);
   }
That's impure. The string goes into a black hole and does nothing from the program's perspective, but changes the state of the world from everyone else's perspective (it probably makes pixels change on a display somewhere).

So how do you make that function pure? Like this:

    TheEntireUniverse printGreeting(string greeting, TheEntireUniverse u)
    {
        return u.makeSomeAtomsMoveAroundBobsConsole(greeting);
    }
This function takes not only the string greeting, but everything else in the universe. You, me, everyone who's ever lived, bob's console, everything. It performs the same function by returning a new universe with everything the same except for the atoms moved to caused the greeting to appear on Bob's console.

Obviously we can't do this, but (in Haskell, say) the IO monad is a context in which we understand that anything done inside of it has the affect of changing the "real world".

If you pretend that you really had to do the whole "universe dragging" thing around for anything in the universe that WASN'T part of your program (we're assuming it itself is in some black hole somewhere to avoid paradoxes and such), then you would darn well make sure you kept this pain-in-the-ass operation as "thin" as possible. Why? Well, you're lazy!

When you look at things that way, there's an analogy to writing stuff on bare metal hardware. You know you're going to have to write some assembly language (or maybe even direct machine code if you don't have an assembler). What you want is to get to the nice cozy world of C as soon as possible, so what you're going to do is use assembly to code only the smallest surface area you need so that writing in C makes sense, and everything else that doesn't need to be in assembly for performance sake you do in C.

So it is in a purely functional language. The idea is to "get out of IO land" as fast as possible. To use a DB as an example, you'd like to live in a world where your little view of the DB could be expressed in purely functional terms (writing functions that result in a modified version of that view) and only at the VERY LAST MINUTE would you send that DB view to an IO function that actually sent the view down the wire to the DB.

The same goes for files, sockets, gamepad IO, you name it.

And you'd be surprised how little you need to actually interact with the world between getting your data and pushing out state changes. A game, for example, can do a WHOLE LOT OF STUFF between reading the input state and writing graphics commands.


I want to take a leap and talk about 2 core concepts that I got after spending last week learning Clojure. I'd love some comments about them.

I've been writing javascript for years but functional programming didn't make sense until I realized:

1) It's essentially inverted callbacks. You can read and reason about your code by working from the inside out. The results of a function are passed up to the outside.

2) When you stop modifying variables and just pass them around, it becomes a lot easier to think about code in abstract ways. There are of course some situations where modifying a variable is needed, but it really feels icky and you think twice about doing it.

The problem I found trying to learn this stuff is that once you learn to think functional its hard to think like someone who doesn't get it. That makes it hard to translate concepts in ways that non-functional people can get easier. I'm keeping mental tabs on ideas as I understand them to hopefully help with this problem.

Also, macros. Clojure people love talking about them. They aren't explained well most of the time though.

Simplest explanation is that you can write a bunch of your own functions, even ones that will override native functions, i.e macros. Since clojure is a compiled language, at compile time all the source code gets read in and pre-processed. When the compiler reaches some code that was defined in a macro, it keeps rewriting that code until all the macros are processed and the code has been re-written. Then everything is compiled to bytecode for the jvm.


I think inverted callbacks is a strange way to describe just standard function calls. Unless you also call 'a.b' a callback, I don't think 'f a' could be called an inverted callback. It's just a standard function call without any special treatment of it's arguments (like self in most OO languages).

I agree with you that it's hard to remember not thinking functionally. I've only been doing it for maybe 3 years and it's hard to remember what it felt like when I was first trying to wrap my head around it.

With macro's, I think you mean to say your own special forms, not functions. They allow you to take in code as arguments, and determine when that code will get evaluated, letting you write things like "or" as macros, when it most languages they have to be implemented as built-in primitives (special forms).


See, the way you think and try to describe the problem is right. But the point I am making is that often giving someone the correct answer doesn't help them make the mental connections they need to get the Eureka! moment of understanding about the fundamental nature of the topic.

Just reiterating the same definitions over and over to someone is somewhat like talking to someone in Farsi who grew up in rural China and only speaks a local dialect of Mandarin. Sometimes we have to point at things and make funny baby noises so people can ramp up their learning.

So when I said functions instead of forms, I did that because a) I don't understand the difference yet and b) that's not the point. I'm documenting how I, as someone new to both clojure and a stricter sense of functional programming, am starting to learn these concepts.

Eventually I'll grok the Matrix and all of this will seem hopelessly simpleminded. But for me right now, this is where I am on the road.


Your use of the language "passed up to the outside" is interesting... Up and outside imply some sort of intrinsic directionality. Considering that functional programming seems to capture the mathematically inclined, can anyone speak to the concept of topology applied to code structure or interpretation? For instance, does a closure imply some sort of filtration structure on the "code space?" (by filtration here, I mean the concept from algebraic topology)


Oh man, you lost me. I'm definitely not one of the mathematically inclined. My analogies are rough and largely wrong and the only thing I'm trying to relate is the particular way of thinking that at a critical moment helped me understand a little more. I wouldn't put too much credence on the veracity of "up to the outside". If it does hit on some underlying important point, it's entirely by accident, I swear.


I think yes probably if you look at the parts of the AST subject to substitution of a particular name.


I found LightTable* as a great way to grok functional programming - you can actually see how data moves around. Really brilliant.

*Lighttable IDE: http://www.chris-granger.com/lighttable/


Just watched this decent intro released yesterday... (OSCON 2013: "Functional Thinking" - Neal Ford) http://www.youtube.com/watch?v=7aYS9PcAITQ


> It always returns the same result given the same arguments. This is call "referential transparency" and you can add it to your list of five-dollar programming terms.

> It doesn't cause any side effects, e.g. it doesn't "change the external world" by changing external mutable objects or outputting to i/o.

These have little to do with one another. "sort :: [a] -> [a]" sorts a list, perhaps using quicksort. If you want to randomize the pivots, it's "sort :: [a] -> IO [a]", and everything in your program that sorts things has a different type, even though it produces the same value.


I have been tempted by functional programming, and it is interesting, but I'm not sure how would be the result for highly complex code.


It is especially suited to highly complex tasks. (Your code should stay as simple as possible.)


Another great post, Daniel... keep 'em coming!


Thanks!


The third example has an error: ((rand) > 0.5) should say (> (rand) 0.5)


Thanks, fixed!


I like your post, and think we need more people like you doing this. One critique: I think you need to do a much better job explaining your first part on recursion.

You need to walk through exactly what you are doing in your sum function, because it is not at all clear to the uninitiated where in the hell the "acc" variable comes from. Instead of explaining any of this part, which is perhaps the most difficult part for a non-FP person to grasp, you jumped straight to "by the way, use loop when there's a lot of numbers."

My coworker's comments when I sent him the link:

"Ok, I get it...... what's going on with the sum?..... whaaat?"

Thanks for doing what you're doing. I look forward to reading more of your work.


OK - I've updated that section. I hope it helps!


Yes, that is a fantastic explanation. My coworker immediately got it, and was simultaneously blown away by the concept of "arity."

Please keep it up.

One area you may be interested in: I've recently been playing around with Incanter (a library for data analysis, similar to R or Python Pandas).

There is a distinct lack of tutorials for using it, and the documentation isn't exactly friendly....

The reason I mention it is because I think data analysis is a place where Clojure absolutely shines, and someone with your expertise could probably work it into useful examples.


Thanks for the suggestion! I know next to nothing about data analysis so I might not ever get around to using or writing about Incanter, but I've seen it in action and it looks super cool. Thanks again :)


Thanks, this is exactly the kind of feedback I need. I'll work on clearing that up!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: