
Functional programming explained in Clojure with a terminal-based game - yawz
http://www.braveclojure.com/functional-programming/?again
======
platz
I'm sure this is great walkthrough. Although I'd like it if there weren't
headlines like "FP explained".

I realize this may be pedantic, but If I've learned anything about FP, the
term means different things things to different people, and a statement which
makes universal claim about FP _In General_ then will misrepresent the other
flavors of FP.

At least qualify it somehow. Actually, why not just say something like
"Terminal-Based Game Walkthrough in Clojure"?

~~~
phren0logy
Maybe I'm a "glass is half full" kind of guy, but explaining one kind of FP
(rather than all of them) still counts as "FP explained" in my book.

~~~
ch4s3
I agree. I think of it like 'x' explained (from 'y' perspective or with
reference to 'y'). e.g. 'Woodworking Explained, master the lathe"

------
thinkpad20
How does Clojure handle piping state through a series of functions? Is there
anything like monads in Haskell to abstract it away? Haskell makes immutable
data a little bit more tenable because you don't need to explicitly thread
your state. How does Clojure handle this? Does it provide escape hatches for
mutable state, perhaps restricted to using Java libraries?

~~~
oskarkv
For very simple cases there are the threading macros, `->` et al., that you
can use like this:

    
    
      (-> state 
          (assoc :a val) 
          (update-in [:b :c] f args) 
          (dissoc :d)
          (some-other-fn args))
    

where :a - :d are keys.

Or you could use monads in much the same way as you would in Haskell; there
are libs for that.

Or you could write a new macro (or maybe even function) that does exactly what
you want. I did that for a game. It looked like this:

    
    
      (call-update-fns game-state hook
        (update-monster-positions)
        (update-monster-ai)
        (let-characters-attack)
        (some-other-stuff))
    

It would thread the game-state (immutable) through all the functions, and call
hook (a function) in between all the functions to send network messages and
stuff (to minimize latency), and also collect and return new events that the
functions created that I needed to handle (e.g. someone died when the
characters attacked). It's almost like the state monad but not quite.

~~~
muhuk
And there's synthread[1] for more complex cases:

    
    
       (-> {:a 1 :b 2}
         (->/assoc :a inc)
         (->/as {:keys [b]}             
           (->/when (> b 10)
             (->/assoc :large-b true))))
    

1:
[https://github.com/LonoCloud/synthread](https://github.com/LonoCloud/synthread)

------
coffeeyesplease
This site is an amazing resource. Higly recommend it if you want to learn
Clojure

------
brudgers
I've started diving into Clojure...or rather drinking it's Joy from the
firehose due to a birthday Barnes and Nobel gift card used for Fogus in the
first edition [I'm cheap]. To me, this discussion of pure functions is
distinctly not Clojuresque. Clojure is the _Love Child of Common Lisp and
Java_ ™ both of which are distinctly pragmatic languages and neither of which
is bogged down in a theory of pure anything.

Clojure is structured to _limit_ mutability to allow for reasoning about state
in an environment with concurrent processes, but it expects this reasoning to
be done in the context of professional programming, not the ivory tower of
academics. It's idea of functional programming is in the history of Common
Lisp, and a good description of it is given by Graham in chapter 3 of _On
Lisp_ :

[http://unintelligible.org/onlisp/onlisp.html#SEC23](http://unintelligible.org/onlisp/onlisp.html#SEC23)
or
[http://www.paulgraham.com/onlisptext.html](http://www.paulgraham.com/onlisptext.html)

Essentially, the idea is to isolate mutation - just like Haskell and for
exactly the same reasons but without an explicit proliferation of types.

So long as we have von Neumann machines, we're going to have mutation. The
goal is to abstract as much of it away as possible to facilitate the
management of state. But it never goes away, and we shouldn't imagine or try
to convince ourselves that that it does...nor that it should, completely.

Polemic over; still a good article and worth a read. Certainly got me
thinking.

~~~
nonrecursive
I think you may be misunderstanding a few things, and I hope I can clear them
up :) First, the term "pure function" does not imply "ivory tower" any more
than "recursion" does. Pure functions are tools that allow you to "isolate
mutation". In fact, Paul Graham mentions that experienced lisp programmers
"try to segregate side-effects in a few functions, allowing the greater part
of the program to be written in a purely functional style"
([http://unintelligible.org/onlisp/onlisp.html#SEC27](http://unintelligible.org/onlisp/onlisp.html#SEC27)).
So - don't let the terminology throw you :)

Second, the point of the point of the chapter is to show how much can be
accomplished without mutating data structures. I think that's something
valuable to focus on. Saying "You can do a lot with pure functions and
immutable data structures" is not the same as saying "You'll never need to
mutate anything" and as the author, I apologize if my writing suggests
otherwise :(

Lastly, Clojure definitely places a huge emphasis on limiting mutability by
using pure functions and immutable data structures. I think Rich Hickey even
describes pure functions as stable bricks or stable atoms in one of his talks.
My impression is that it's definitely not the love child of common lisp and
java. Clojure is pragmatic, but that doesn't mean it doesn't place an emphasis
on functional programming. I agree that it's "not bogged down in a theory of
pure anything", but that doesn't mean it wasn't intentionally designed to
support and encourage programming with pure functions and immutable data
structures.

~~~
brudgers
The article well written. It didn't throw me or make me confused. I simply was
bothered by the lack of nuance in regard to mutation. Trillions of pixels have
been burned describing pure functions and giving small examples of
immutability and recursion. Trees have even been killed.

What makes Chapter 3 of _On Lisp_ or _SICP_ useful is they're Third Acts. They
go deeper than pure functions. They're pivotal because that's where we start
talking about the messy world where mutation is necessary. It's where we admit
that there's a rationale for the way Java works even if it's implementation
could stand improvement.

What makes Clojure interesting is the way in which it implements that depth.
Lisp went on a journey to Java and was transformed by the experience. Clojure
is not just another Lisp - that side of the family has always had shared
structure and a propensity for functions. There's `is` and 'testing` and
agents and vars and `.` and `..`. JavaScript the good parts is a book. Java
the good parts are in Clojure. Clojure is an Act V not an Act I.

~~~
nonrecursive
The book chapter aside, I think you might actually have it backwards.
Hopefully I'm not misunderstanding you terribly! The reason why PG has to
spend as much time as he does warning programmers about hidden ways in which
Common Lisp mangles your data structures (nconc, for example) is because
Common Lisp does not come with immutable data structures out of the box. My
impression is that he's telling you how to work as well as you can (strive for
purely functional code) with the tools at hand. Clojure, on the other hand,
was built so that you could code without having to worry about that stuff by
default.

Also, you say that lisps have "always had shared structure." I'm not sure what
you mean by that? Clojure implements _structural sharing_, which is what
allows it to have persistent, immutable data structures. Common Lisp certainly
doesn't do the same.

I'm not sure what you mean by "Lisp went on a journey to Java and was
transformed by the experience." I think you're saying that Rich Hickey
designed Clojure to be a lisp that's more Java-like? Could you explain how? My
impression is that Rich Hickey thinks OO is broken (see the talk "Are we there
yet?"), as is the notion of mutability as implemented by Java. Clojure's
interop with Java is a great convenience, but the emphasis is still on
functional programming. I don't think I understand what you're saying, or
maybe it's just that we disagree?

Finally, from clojure.org: "Clojure is predominantly a functional programming
language, and features a rich set of immutable, persistent data structures.
When mutable state is needed, Clojure offers a software transactional memory
system and reactive Agent system that ensure clean, correct, multithreaded
designs." Maybe it's these state management features you're concerned about?
If so, then I would suggest that vars, atoms, refs, and agents are not as
inspired by Java as you're saying. Or maybe we just disagree that, the
majority of the time, you should be writing pure functions and using immutable
data structures in Clojure?

~~~
brudgers
PG isn't warning any experienced Common Lisp programmer about `nconc`. The [n]
at the front means it's destructive...there's also `nsubst` and `nreverse` and
`nstring-capitalize`. They all exist in Common Lisp to allow a programmer to
avoid consing and the subsequent garbage collection. In other words, they
exist to allow a programmer to optimize their code. They exist as exceptions
to idiomatic Common Lisp practice of returning values in lieu of mutating
locations. Sure it is not as idiomatic in Common Lisp as in Clojure in part
because Common Lisp is largely a product of its time and that time was largely
single threaded.

Avoiding mutation in Common Lisp is even less idiomatic than it is in Scheme -
where there are not destructive versions of various functions by default and
idiomatic practice is mutation is signaled with a bang [!]. But again, the
preferred approach is persistence and writing side-effect free functions.
Indeed at the front edge of Scheme, Racket implements immutable data
structures by default.

Idiomatic functional style and defaulting to immutable data structures are not
what makes Clojure unique as a Lisp - it's knob twiddling, not a new paradigm.
It has some great built in syntax that allows for the best part of Lisp-2
programming - i.e. symbols representing a collection acting as functions over
the collection and the association of a meta-data map with a symbol like a
plist. But it's evolutionary as a Lisp.

On the other hand, though not unique, idiomatic functional style and
defaulting to immutable data structures are unusual in relation to the Java
platform. Its relationship to the Java platform is what makes Clojure unique.
STM is not a product of it's Lisp heritage. Neither is it's implementation of
objects with via interop [largely eschewing objects despite availability is
also idiomatic Lisp].

Understand, I'm writing to think. What you've written is just a starting point
I can use as an excuse for saying what I was primed to say anyway. Like I said
in my original comment, I was already drinking from the Clojure firehose.

So what's the upshot? Well what did your article say that hasn't already been
said before about functional programming? Why not point people to Chapter 3 of
Graham? It's more convincing for those who need convincing and more in depth
for those who don't. Why not point people to _The Value of Values_ , it's
again more convincing for tire-kickers; a comfortable sermon for the newly
converted, and a reference point for those already in the cult.

What really makes Clojure unique?

~~~
nonrecursive
I don't think I read On Lisp the same way. From 3.1: "If a function is
advertised as destructive, that doesn't mean that it's meant to be called for
side-effects. The danger is, some destructive functions give the impression
that they are. For example,(nconc x y) almost always has the same effect as
(setq x (nconc x y))". Calling it dangerous sounds like a warning to me. I
definitely agree, though, that he makes the case that the preferred approach
is writing side-effect free functions. I'm not sure what you mean by
"persistence" though, because Common Lisp doesn't have persistent data
structures.

I don't know if its emphasis on functional programming and values makes
Clojure unique, but that's definitely one of its core design concerns. The
"article" is a chapter from a book on Clojure, and learning to write in a
purely functional style is essential to learning Clojure. The point of the
chapter isn't to get to the heart of what makes the language unique, it's to
offer instruction on how to use it.

~~~
brudgers
Here's the recycled cons at the car of _ANSI Common Lisp_ section 12.4:

 _Common Lisp includes several functions that are allowed to modify list
structure. These functions are destructive for reasons of efficiency. Though
they may receive conses passed to them as arguments, they are not meant to
called for their side-effects._

 _For example,_ delete _is a destructive version of_ remove _. While it is
allowed to trash the list passed to it as an argument, it doesn 't promise to
do anything. This is what happens in most implementations:_

    
    
       > (setf lst '(a r a b i a))
       (A R A B I A)
    
       > (delete 'a lst)
       (R B I)
    
       > lst
       (A R B I)
    

_As with_ remove _, if you want side-effects, you should use_ setf _with the
return value:_

    
    
       (setf lst (delete 'a lst))
    

Because garbage collection and homoiconicity can sometimes feel like magic, it
can be hard to think of Common Lisp running closer to the metal than a
language like C. But it does. Cons cells are locations in memory, and symbols
are pointers and there's nothing in between. A programmer doesn't even get
`free(array)`. Memory locations can be shared willy-nilly because just as in
Clojure, two distinct lists/sequences can share a tail. From _ANSI Common
Lisp_ section 12.8, "Constant Structure":

    
    
       > (defun arith-op (x)
           (member x '(+ - * /)))
       <function:arith-op>
    
       > (setf lst '(as it were)
       (AS IT WERE)
    
       > (nconc (arith-op '*) lst)
       (* / AS IT WERE)
    
       > (arith-op '-)
       (- AS IT WERE)   ;; bad
    
       > (arith-op 'as) 
       (AS IT WERE)     ;; even worse
    

"Oh Shit!" moments like this are why Lispers like Graham developed a
functional programming style. It's a lot of what motivated the design of
Scheme. It's not really what motivated Clojure because Java already solves
this problem. Clojure is designed to solve the problems Java doesn't more
easily.

That fundamental goal is why what makes Clojure unique matters when explaining
Clojure. It's also what makes Clojure a less than ideal vehicle for teaching
functional programming style - it's designed for programmers who are solving
problems on the JVM [or CLR or V8], and not really so much as a general
purpose language. It's designed around interop. As weavejester says in his
linked talk, Clojure is a Java library.

There are better languages for teaching functional programming -
Scheme/Racket. What makes them objectively better is that helping people learn
to program in a functional style is one of the problems they are trying to
solve, and they have almost four decades of development toward that goal.
Education is entirely orthogonal to Clojure, even more so than Java with its
two decades of introductory text development and promotion via vocational
arguments in CS departments. And, Racket/Scheme isn't trying to solve JVM
problems.

Teaching functional programming style is hard, but a largely solved problem
because the internet allows pointers to excellent materials.

[http://learncodethehardway.org/blog/AUG_19_2012.html](http://learncodethehardway.org/blog/AUG_19_2012.html)

~~~
nonrecursive
You're right about being able to point to the same tail from two different
lists in common lisp, of course. I'm not very experienced with scheme so I
can't comment much on that. Clojure is different in the way it implements its
data structures, so "structural sharing" in that context refers to its use of
tree structures to implement vectors, maps, etc.

Still, though, I think you have it wrong about Clojure's design. You mentioned
the talk "the value of values." Immutability and functional programming are
core to Clojure's philosophy. This is pointed out on the clojure.org home
page. I'm not sure why you think it's not?

Also, Clojure is meant to be a general-purpose language. Also from the home
page: "It is designed to be a general-purpose language". So I'm not sure why
you think otherwise? I started learning Clojure with 0 experience with the
JVM, and many others are doing the same.

I'm also not sure what you mean by "education is entirely orthogonal to
Clojure." I think you're commenting on the value of producing an introductory
Clojure book? In any case - you mention that "there are better languages for
teaching functional programming." The point of Clojure for the Brave and True
is not to teach functional programming, it's to teach Clojure.

~~~
brudgers
If the purpose of _Clojure for the Brave_ is not to teach functional
programming, why bother teaching it? Why go through the motions when not fully
committed? Is it better than pointing the student to excellent resources
despite those resources using another form of Lisp?

To put it another way, is anyone really going to know Clojure without knowing
the something about _SICP_? Is there anything out there that more fully
captures Clojure's definition of functional programming than _On Lisp_ or
_Learn You a Haskell for Great Good_?

OK so here's where we differ. It's in our definitions [what could be more
Clojuresque] of "learning Clojure". Sure, Clojure's syntax takes an hour
versus the thirty minutes Ableson takes to cover Lisp syntax including the
substitution model. Then what?

Well I'm saying the two things a person needs to learn Clojure are the
practices already described in four decades of Lisp's literature and the
Clojure interop. Without either piece, I won't know Clojure.

[http://norvig.com/21-days.html](http://norvig.com/21-days.html)

~~~
nonrecursive
The book covers functional programming because Clojure places a heavy emphasis
on functional programming, and learning it is essential to learning Clojure.
The book is not "Functional Programming for the Brave and True, with Clojure
as the Medium." That doesn't mean that I'm merely "going through the motions"
and am "not fully committed" to helping people understand functional
programming, and it's bizarre, presumptuous, and rude that you would suggest
that. At this point, it seems to me like you're merely being argumentative.

------
sgeisenh
Does anyone else find Clojure's syntax a bit obtuse? I feel like basic
recursive definitions are much clearer in Haskell and ML. To sum over a list
using an accumulator shouldn't take more than two or three trivial lines of
code.

~~~
ajuc
Pattern matching would be nice, but destructuring helps.

    
    
        (defn sum-acc
          ([xs] (sum-acc xs 0))
          ([[x & xs] acc]
            (if (nil? x)
              acc
              (recur xs (+ x acc)))))
    

Could you show Haskell equivalent?

BTW there should be a macro that adds argument with default value.

~~~
malisper
I believe it should be something along the lines of:

    
    
      sumacc xs = summer xs 0
        where summer [] n = n
              summer (x:xs) n = summer xs (x+n)
    

I'm sure there are macros in some library somewhere that implement pattern
matching in lisp (regardless of dialect).

------
rabino
Thanks! This was awesome to visualize some abstract concepts I've read about
but never implemented.

