Rich Hickey: "Simple Made Easy" from Strange Loop 2011

1,420 views
Skip to first unread message

Alex Miller

unread,
Oct 20, 2011, 12:01:28 PM10/20/11
to Clojure

daly

unread,
Oct 20, 2011, 2:01:21 PM10/20/11
to clo...@googlegroups.com
In the Simple-Made-Easy talk Rich raises the question
of long term use. In particular, he mentions the issue
of maintenance and change.

In order to change software you need to understand the
program. Unfortunately most people equate "understanding
the program" as being equivalent to "what the function does".
What it also has to mean is "why the function does it".

In order to write a program that "lives", that is, one
that can be maintained and changed you need to capture
why the code exists and why it is written that way.

The best solution I have found is called Literate Programming.
The LP idea is that you write the program for the programmer
rather than the machine. You should be able to sit and read
a book that explains the program, including the "why". The
real code is in the document but the text explaining the
program is the focus.

I would encourage you to look at Lisp in Small Pieces.
It is a literate program, a book, that contains a complete
lisp system with the interpreter and compiler but it is
written to be read.

Tim Daly
"The hardest part of literate programming is the documentation"

Timothy Baldridge

unread,
Oct 20, 2011, 2:57:29 PM10/20/11
to clo...@googlegroups.com
As a person currently suffering under the load of a highly entangled
system here at work, I really appreciated this talk. The software I'm
currently working on is so entangled, so strongly typed, that we have
some parts of our app even our Architect doesn't want to touch. It's
almost as if Rich looked at our software, then wrote a talk on how not
to do what we do here.

Iif nothing else, at least my current job is teaching me the value of
using Clojure at my next job.

So for now, C# at work....Clojure at home, for everything else, there
is beer.....


Timothy

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

--
“One of the main causes of the fall of the Roman Empire was
that–lacking zero–they had no way to indicate successful termination
of their C programs.”
(Robert Firth)

mmwaikar

unread,
Oct 20, 2011, 11:14:57 PM10/20/11
to clo...@googlegroups.com
I can understand your situation because I've seen a C# code where most of the classes in some 5-6 different assemblies had all (or 90%) static methods :)

Folcon

unread,
Oct 21, 2011, 8:07:06 AM10/21/11
to clo...@googlegroups.com
Ok I watched the talk, and I picked up a few things. But I have questions, which hopefully some kind member in the community can answer.

There are several instances of libraries Rich mentions that provide simple constructs so what clojure libraries provide, set functions and rules (is it core.logic?).

Is there any good examples I can use to improve my knowledge on using the simple constructs Rich presents? (Such as use of datalog?)

And how would one structure something as stateful as a web app using these techniques? Hopefully someone can point to a pre-existing example that follows all or many of these ideas.

Regards,
Folcon

Timothy Baldridge

unread,
Oct 21, 2011, 9:35:31 AM10/21/11
to clo...@googlegroups.com
> And how would one structure something as stateful as a web app using these
> techniques? Hopefully someone can point to a pre-existing example that
> follows all or many of these ideas.

I currently work on a thick-client system. But our back-end is quite
stateless. One thing that irks me the most about our system in C# is
that we've intertwined business logic with business data. So, in our
application, let's say we have a person class. We will have the
following classes:

tPerson (data class generated by our ORM)
PersonDTO (data transfer object)
Person (business object)

So the idea is that SQL will load data from the tPerson table and
place it into a tPerson C# object. Now, the last thing we want, is to
have a hard dependency on tPerson from our Person object. that is, we
never want to have Person take tPerson as an argument. Because then
those two objects are tightly coupled. Where one goes, the other must
follow.

So instead we have a PersonDTO object that transfers the data between
the objects. The Person object the contains business logic (user must
have a last name...user must be over 18, etc.). The sad thing is, that
business logic is now built directly into Person. This complicates the
whole system.

What Rich is advocating is this: throw all the data into a hashmap.
Suddenly, my SQL driver can just dump data in to the map, I can throw
that map around without introducing dependencies,
and I can pass that map through the web to the client. In addition to
all this, I should break my rules out of the Person object and into a
set of validator functions. Now I have real power! Suddenly my rules
apply to any and every object that has a last name property and a age
property! So instead of 3 tightly coupled objects, I will be left with
a map, and a set of highly generic functions that can be reused again
and again.

Getting back to your question, I think it's just good to sit down with
your web app and start asking yourself. "What assumptions am I making
about this code? Am I assuming function a will always call function b?
Could there be a case where I would want a to call c? Well in that
case, maybe I should account for that...". The
SQLObject->DTO->BusinessObject pattern is fairly common in the real
world. So perhaps that is something to re-evaluate in your designs.

Timothy

Chris Perkins

unread,
Oct 21, 2011, 10:01:20 AM10/21/11
to clo...@googlegroups.com
Wow. Easily the best conference talk I have seen... well, ever.

Executive summary: "Mutability is bad for your complection."  :)

- Chris

Jozef Wagner

unread,
Oct 21, 2011, 10:15:31 AM10/21/11
to clo...@googlegroups.com
Great talk!

I got lost a bit in the Abstraction for Simplicity. Could anybody provide me some concrete examples for Who When Where Why slides?


Folcon

unread,
Oct 21, 2011, 12:30:33 PM10/21/11
to clo...@googlegroups.com
Hey Timothy,

Thanks for the response, I currently perform some of these steps. My data is taken out of mongodb and converted into straight clojure maps. I pass these around in my application, calling validation functions on them etc. Having said that, this talk will push me to take a good look at my code to see what kind of implicit assumptions that I am making. 

However I'm still not clear what a set function is, unless it's this http://en.wikipedia.org/wiki/Set_function. Which is odd because the compares them to looping constructs and Haskell's fold.

And in the talk he mentions that rules are more simple than conditionals, which I (maybe incorrectly) take to mean that rules can replace conditionals. So what exactly do we mean by rules? Core.logic? And how do they take the place of conditionals?

Regards,
Folcon

Mark Engelberg

unread,
Oct 21, 2011, 12:50:27 PM10/21/11
to clo...@googlegroups.com
I've always felt that Clojure's treatment of nil was somehow inconsistent with the elegance of many other features of Clojure.  Now I can finally articulate why:  nil complects non-existence, false, and empty.

The choice to make nil represent so many concepts was an "easy" choice, because it saves a few characters when you can write things like (when seq ....) vs. (when (empty? seq) ...) and Clojure implements sequences in a way that the former expression is also a bit more performant.  It is also easy in the sense that it is more similar to what Lisp users (as opposed to Scheme) are used to from past experience.  But it is decidedly less simple to have these ideas complected.

Over the past couple of years, I've seen numerous bugs along the lines of "Your function usually works great, but it breaks when the list/map/vector you pass in contains nil values."  It seems clear to me that nil's complexity makes programs harder to analyze, because when you're writing your code, you might be thinking of nil as only representing non-existence or emptiness, for example, and forgetting that the system will treat nil as a false value as well.

daly

unread,
Oct 21, 2011, 1:38:47 PM10/21/11
to clo...@googlegroups.com
If I understand your post correctly you feel that nil should
ONLY represent the concept of a missing value. It should not
represent false and empty.

Having used lisp in many different forms over the last 40 years
I think that the "complecting" of nil to represent all three
concepts is one of the most brilliant aspects of the language.
In fact it is one of the key flaws of scheme, in my opinion,
that they added true and false.

There is a fourth use of nil that is also very convenient.
Lisp functions return it as a default value. This makes it
possible to wrap functions with functions which other languages
like C++ make very difficult.

(e.g. if we have a C++ function
void foo()
we cannot wrap it with another
bar(foo())
well, we can but we have to use the comma hack as in
bar((foo(),1))
)

Java code is littered with checks for null before every
iterator construct where the code could be so much cleaner
if iterators just "did the right thing" with null, that is,
end the iteration.

The use of nil as a unified value for the many meanings leads
to a lot of useful features. The context of the nil value
completely defines the intended meaning.

Tim Daly

Mark Engelberg

unread,
Oct 21, 2011, 1:54:00 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 10:38 AM, daly <da...@axiom-developer.org> wrote:
> If I understand your post correctly you feel that nil should
> ONLY represent the concept of a missing value. It should not
> represent false and empty.

Yes, you correctly interpreted my post.  That is my opinion.

> The context of the nil value
> completely defines the intended meaning.

This is a point I disagree with. The context defines the meaning of
nil intended by the person coding that function. It does nothing to
ensure that the coder has thought about what the function will do if
nil is used with another meaning, and it does nothing to ensure that
consumers of that function will use nil in the way the coder intended.
I have found this to be a relatively common source of bugs that pass
test cases (because test cases are written by the coder who has a
specific intention in mind) but show up in the field.

>
> Having used lisp in many different forms over the last 40 years
> I think that the "complecting" of nil to represent all three
> concepts is one of the most brilliant aspects of the language.

That may be. If so, it undermines one of the messages in the video
that complecting=bad. If this particular complection is brilliant, it
naturally leads to a lot of deeper questions: When is complecting
brilliant rather than bad? How does one tell the difference?

Thorsten Wilms

unread,
Oct 21, 2011, 2:08:13 PM10/21/11
to clo...@googlegroups.com
On 10/21/2011 06:50 PM, Mark Engelberg wrote:
> Now I can finally articulate why: nil complects non-existence, false,
> and empty.

How does nil represent empty? '() does not equal nil.


> It is also easy in the sense that it is more similar to what Lisp users (as
> opposed to Scheme) are used to from past experience. But it is
> decidedly less simple to have these ideas complected.

AFAIK, Common Lisp does treat nil and empty lists as equivalent. Looks
like a clear difference, not "more similar" to me.


--
Thorsten Wilms

thorwil's design for free software:
http://thorwil.wordpress.com/

David Nolen

unread,
Oct 21, 2011, 2:22:02 PM10/21/11
to clo...@googlegroups.com
Collections often include false as a value. You will have to handle it by using some other value like ::not-found.

David

--

Michael Fogus

unread,
Oct 21, 2011, 2:42:45 PM10/21/11
to clo...@googlegroups.com
> nil complects non-existence, false, and empty.

Let's explore that a little further:

* Non-existence
- Accessing a local or var that has never been declared
* False
- (if nil :never-here :but-here)
* Empty
- (seq [])

And maybe there is another?

* Not set
- (def x)
- (:x {:a 1})

But which one should nil actually mean? Given a green-field scenario that is.

I can definitely see where you're going, but I wonder if the use of
nil in Clojure is the result of a cost/benefit analysis in relation to
Java interop?

Mark Engelberg

unread,
Oct 21, 2011, 2:43:57 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 11:08 AM, Thorsten Wilms <t_...@freenet.de> wrote:
> On 10/21/2011 06:50 PM, Mark Engelberg wrote:
>>
>> Now I can finally articulate why:  nil complects non-existence, false,
>> and empty.
>
> How does nil represent empty? '() does not equal nil.

(cons 1 nil) is one obvious example.

The pattern of using first/next/nil? as a more efficient/compact
alternative to first/rest/empty? is arguably another.

Mark Engelberg

unread,
Oct 21, 2011, 2:56:17 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 11:22 AM, David Nolen <dnolen...@gmail.com> wrote:
> Collections often include false as a value. You will have to handle it by
> using some other value like ::not-found.
>
> David

True, but the multiple meanings of nil creates additional complexity.
Contrast, for example, (filter identity s) and (keep identity s). One
strips nil and false values, the other just strips nil values. Why do
we need both? Because sometimes we mean nil and false to be the same,
and sometimes we don't. Have you ever gotten this wrong? I have, and
I understand these issues pretty well. Maybe you haven't, maybe you
can "juggle more balls" than I can. But as Rich pointed out in the
video, simplicity is about respecting the fact that all of our brains
have limitations and can get tripped up when things are complected.

Mark Engelberg

unread,
Oct 21, 2011, 3:36:51 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 11:43 AM, Mark Engelberg
<mark.en...@gmail.com> wrote:
>> How does nil represent empty? '() does not equal nil.
>
> (cons 1 nil) is one obvious example.
>
> The pattern of using first/next/nil? as a more efficient/compact
> alternative to first/rest/empty? is arguably another.
>

One more anecdote about this.

One time, I wrote a function that looked like this:
(defn f [s]
(when s .....))

At the time I wrote the function, I did the analysis, and realized
that my function was always being called with sequences (specifically,
sequences that had already been "seq-ified" at some prior point), so
it was safe to use "when" as a way to screen out the empty things. So
I opted for this easy, efficient way to express this.

Somewhere along the line, as my application grew more complex, I
needed to reuse f in another context, and when looking at the docs for
f (which said something like "consumes a sequence and does ...", I
thought I could safely pass in a lazy sequence. But I couldn't
because when a lazy sequence is "empty" it is not "nil". My program
was buggy and it took a while track down the source of the problem.

Yes, it was my fault. In retrospect, I see that my program would have
been more robust had I not made assumptions about s, and written it as
(when (seq s) ...)
or perhaps
(when (not (empty? s)) ...)

But I do think it's fair to pin at least some of the blame on the
complexity of nil. Since nil can be used interchangeably with the
concept of emptiness in so many circumstances, and was interchangeable
in the initial context of my function, it was all too easy to rely on
that behavior.

David Nolen

unread,
Oct 21, 2011, 3:41:14 PM10/21/11
to clo...@googlegroups.com
Just because we have dynamic types does not give us the freedom to not consider them.

(when s
   ...)

Does not communicate anything about collections - only nil, false or something else.

(when (seq s)
  ...)

(when (empty? s)
  ...)

Clearly express a consideration about the types at play.

David

Mark Engelberg

unread,
Oct 21, 2011, 4:02:29 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 12:41 PM, David Nolen <dnolen...@gmail.com> wrote:
> Just because we have dynamic types does not give us the freedom to not
> consider them.

Oh, I definitely considered the types when I wrote the function. It's
just that at the time I wrote it, I was confident the input would
already be seq-ified. nil, among its many purposes, is a key part of
the "seq interface", and testing for nil is how you are expected to
interact with seqs to determine emptiness. As my program grew, the
assumption that the input would definitely be a seq was invalidated.
This is exactly the inherent challenge of making evolving,
maintainable programs that Rich speaks of in his video.

If the only way to test for a seq to be empty were a general-purpose
function like empty? that applied to all collections, my code would
have worked in the new context. If the only to test for a seq to be
empty were a seq-specific function like seq-empty? then when placed in
the new context, my code would have broken in a very clear, easily
diagnosable way. But because we test for seqs to be empty using nil,
an object with many other purposes, my code appeared to work, but was
semantically wrong -- always a hard thing to track down.

I suppose one could also place some small portion of the blame on the
problem of assigning clear linguistic labels to Clojure's types. Is
something a sequence? A seq? A seqable? A collection? It's always
been difficult to come up with clear definitions for these categories,
and to specify the appropriate precondition with perfect clarity in a
docstring.

--Mark

daly

unread,
Oct 21, 2011, 4:03:25 PM10/21/11
to clo...@googlegroups.com
On Fri, 2011-10-21 at 12:36 -0700, Mark Engelberg wrote:
> On Fri, Oct 21, 2011 at 11:43 AM, Mark Engelberg
> <mark.en...@gmail.com> wrote:
> >> How does nil represent empty? '() does not equal nil.
> >
> > (cons 1 nil) is one obvious example.
> >
> > The pattern of using first/next/nil? as a more efficient/compact
> > alternative to first/rest/empty? is arguably another.
> >
>
> One more anecdote about this.
>
> One time, I wrote a function that looked like this:
> (defn f [s]
> (when s .....))
>
> At the time I wrote the function, I did the analysis, and realized
> that my function was always being called with sequences (specifically,
> sequences that had already been "seq-ified" at some prior point), so
> it was safe to use "when" as a way to screen out the empty things. So
> I opted for this easy, efficient way to express this.
>
> Somewhere along the line, as my application grew more complex, I
> needed to reuse f in another context, and when looking at the docs for
> f (which said something like "consumes a sequence and does ...", I
> thought I could safely pass in a lazy sequence. But I couldn't
> because when a lazy sequence is "empty" it is not "nil". My program
> was buggy and it took a while track down the source of the problem.

THIS is where the multiple meaning of nil in traditional lisp is
brilliant. I believe that Clojure "got it wrong" in the design
decision to make (seq s) and (not (empty? s)) have different
semantics. This is the same mindset that causes (me) so much grief
in Java... looping and iteration does the wrong thing with NULL and
I have to check for NULL every time. Yet everyone, if given an empty
list of things to shop for, will know NOT to go shopping.

>
> Yes, it was my fault. In retrospect, I see that my program would have
> been more robust had I not made assumptions about s, and written it as
> (when (seq s) ...)
> or perhaps
> (when (not (empty? s)) ...)

Actually I don't think this is entirely your fault (modulo the fact
that we need to understand our language semantics). I believe that
this is due to a deep design flaw. You're not the only person to
mis-handle an empty sequence.

>
> But I do think it's fair to pin at least some of the blame on the
> complexity of nil. Since nil can be used interchangeably with the
> concept of emptiness in so many circumstances, and was interchangeable
> in the initial context of my function, it was all too easy to rely on
> that behavior.
>

Tim Daly
Literate Software

Armando Blancas

unread,
Oct 21, 2011, 4:07:39 PM10/21/11
to Clojure
> (because test cases are written by the coder who has a
> specific intention in mind)
>

Good observation. When I see figures of tests coverage I wonder how
many flow paths aren't being covered simply because they don't exists
but they should.

David Nolen

unread,
Oct 21, 2011, 4:13:18 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 4:02 PM, Mark Engelberg <mark.en...@gmail.com> wrote:
On Fri, Oct 21, 2011 at 12:41 PM, David Nolen <dnolen...@gmail.com> wrote:
> Just because we have dynamic types does not give us the freedom to not
> consider them.

Oh, I definitely considered the types when I wrote the function.  It's
just that at the time I wrote it, I was confident the input would
already be seq-ified.  nil, among its many purposes, is a key part of
the "seq interface", and testing for nil is how you are expected to
interact with seqs to determine emptiness.  As my program grew, the
assumption that the input would definitely be a seq was invalidated.
This is exactly the inherent challenge of making evolving,
maintainable programs that Rich speaks of in his video.

Testing for nil is not how you determine emptiness unless locally you are using next.

(rest '()) -> ()
(next '()) -> nil

David 

daly

unread,
Oct 21, 2011, 4:25:46 PM10/21/11
to clo...@googlegroups.com
On Fri, 2011-10-21 at 15:41 -0400, David Nolen wrote:
> Just because we have dynamic types does not give us the freedom to not
> consider them.

If all of these dynamics types and all of the tests "respected nil"
in its many meanings then
(when s ...,
(when (seq s)...,
(when (empty? s)...,
would not be an issue. (when s...) would "just work".

>
>
> Clearly express a consideration about the types at play.

Clojure was supposed to transparently substitute things like sequences
and vectors everywhere that lisp used lists. That would be true if nil
was respected but is not true now and this complicates the code without
apparent benefit, in my opinion.

In lisp you can ask what the type is (e.g. by calling consp, vectorp,
etc) but these type-specific predicates are relatively rarely used.
In fact, when they are used then you are struggling with data-level
issue that could probably be abstracted away (e.g. a code smell).

Clojure is a great language but the nil handling is, in my opinion,
a design flaw. It forces the introduction of (empty?...) and an
awareness of the data types into view unnecessarily.

Tim Daly
Literate Software


Rich Hickey

unread,
Oct 21, 2011, 5:03:13 PM10/21/11
to clo...@googlegroups.com
This message is not specifically in reply to Tim, but to the thread in general.

It can be very difficult to enumerate (or even remember :) all of the contending tradeoffs around something like Clojure's nil handling.

The is no doubt nil punning is a form of complecting. But you don't completely remove all issues merely by using empty collections and empty?, you need something like Maybe and then things get really gross (IMO, for a concise dynamic language).

I like nil punning, and find it to be a great source of generalization and reduction of edge cases overall, while admitting the introduction of edges in specific cases. I am with Tim in preferring CL's approach over Scheme's, and will admit to personal bias and a certain comfort level with its (albeit small) complexity.

However, it couldn't be retained everywhere. In particular, two things conspire against it. One is laziness. You can't actually return nil on rest without forcing ahead. Clojure old timers will remember when this was different and the problems it caused. I disagree with Mark that this is remains significantly complected, nil is not an empty collection, nil is nothing.

Second, unlike in CL where the only 'type' of empty collection is nil and cons is not polymorphic, in Clojure conj *is* polymorphic and there can only be one data type created for (conj nil ...), thus we have [], {}, and empty?. Were data structures to collapse to nil on emptying, they could not be refilled and retain type.

At this point, this discussion is academic as nothing could possibly change in this area.

The easiest way to think about is is that nil means nothing, and an empty collection is not nothing. The sequence functions are functions of collection to (possibly lazy) collection, and seq/next is forcing out of laziness. No one is stopping you from using rest and empty?, nor your friend from using next and conditionals. Peace!

Rich

Michael Jaaka

unread,
Oct 21, 2011, 5:10:24 PM10/21/11
to Clojure
Bravo, bravo! Great speech, I'm already looking for such esseys. I'm
already learning haskell and erlang for great good, because all things
told about lisp has been already read.

I'm also designing system. Because it has some well defined
functionality, my first tought was, hey man I will use obect oriented
approach to creat model of that system. It is easy to document in UML
since I only concerned on fundamental operations. Complexity of
diffrent configurations I wanted to hide in poliformism of java oo
programing. But i have found it corrupted, nevertheless i will still
document it in terms of objects. OO is broken because it require from
me to build tree structure of impl. objects depending on types of
processing, I would make it simpler by message dispatching but, I have
choosed protocols. Still don't know if it will work, because never
used them before, but from read materials that is its purpose. I don't
need state of obj. because it is about processing input and spitting
output.

Btw. from built-in data structures I'm missing trees.

daly

unread,
Oct 21, 2011, 5:37:32 PM10/21/11
to clo...@googlegroups.com
Rich,

My apologies that what I have said about nil punning came across
as criticism directed at you. That was not intentional. I have
the highest respect for your design work. You're doing an amazing
job and I continue to learn from you.

I understand the lazy vs empty issue and I think you made a good
tradeoff. I'm just bemoaning the fact that nil-punning is really
vital in keeping data-type issues out of my lisp life and that
won't work in Clojure.

Ultimately this is cured by deep learning of the language semantics.
I still have a way to go.

Tim Daly
Literate Software

Rich Hickey

unread,
Oct 21, 2011, 9:20:15 PM10/21/11
to clo...@googlegroups.com

On Oct 21, 2011, at 5:37 PM, daly wrote:

> Rich,
>
> My apologies that what I have said about nil punning came across
> as criticism directed at you.

It certainly didn't come across that way - no worries :-)

Rich

Message has been deleted

Michael Jaaka

unread,
Oct 22, 2011, 5:45:40 AM10/22/11
to Clojure
And one more thing to say about polymorphism.
In functional programing protocols are OO polymorphism inside out.
It is possible because FP doesn't care of state, its operates only on
its input and gives outputs (it is by definition - functional).
We are taught from very beginning how to function in a poly way.
For example on math classes we are taught how to operate on numbers
depending on its classification.

And other story about being simple and Monads.
I want to learn Monads, because I want to solve problems in
declarative way.
Unfortunately all tutorials on Monads in Clojure gives me a fish
instead of fishing rod.
Because of that now I'm learning Haskell. I believe that they are made
there right.
The funny thing is that after 5 days of reading of "Learn Haskell for
Great Good" (for 45 mins. a day) and not programming at all
(especially in Clojure),
I wanted to implement some concepts from book in Clojure and guess
what.
I found my self saying "man it is so wordy when doing it in Clojure" -
destructions, functions composition, lambda calculus, pattern matching
done via library, imperative let statement, partials.
But I think that one thing justifies it - homoiconicity and as a
result its macros.
Learn by that experience I believe that the source of inability to
learn monads is that they are made hard in Clojure in terms of Rich
speech.
Funny thing is that I believe that I already know the purpose of
monads and use cases where I could use them - I have even wrote code
in Clojure which is missing monads as glue for my declarative
specified statements in a algorithm - and believe that monads already
should glue them, but they are missing in Clojure - they are made
there via libraries and obscured by homoiconicity law.

The sad story for me is that I have waste so much time of my life in
doing some higher level concepts with wrong tools.
I waste a lot of time on bugs; documenting; and learning cutting-edge,
super hyped, students life cycle driven frameworks.

The good side of this story is that I already knew what Rich is
talking about, because learning about higher level concepts and
expressing them with right tools, gives you ability to judge other
solutions.

Well, Rich don't leave us, you have such bright look on technology
that you would easily support for example SAP with good tools to
theirs domain problems, so its matter of time when you will get
proposition to not reject.
Or you could participate in such firm on your own, all you need to do
is to make PRINCE2, ITIL; find CEO; decide what business problems you
want to solve; get some sponsors; remodel your personal life to not
neglect your family and you are another shark of the IT, of course
hire me for Great Good in your company, I can work for you on eastern
Europe.

Michael Forster

unread,
Oct 21, 2011, 2:26:52 PM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 12:38 PM, daly <da...@axiom-developer.org> wrote:
[...]

> Having used lisp in many different forms over the last 40 years
> I think that the "complecting" of nil to represent all three
> concepts is one of the most brilliant aspects of the language.
> In fact it is one of the key flaws of scheme, in my opinion,
> that they added true and false.

Brilliant or clever, with all the downsides cleverness entails? I'm
as guilty "punning on nil" as any old Common Lisper, but I have to ask
myself, "Is it good or just easy?"


> There is a fourth use of nil that is also very convenient.
> Lisp functions return it as a default value. This makes it
> possible to wrap functions with functions which other languages
> like C++ make very difficult.

[...]

I agree, but that does not justify the (mis)use of nil for other purposes.


> The use of nil as a unified value for the many meanings leads
> to a lot of useful features. The context of the nil value
> completely defines the intended meaning.

And now one _must_ provide the context for each use of nil, or, as
Kent Pitman said, "Several broad classes of bugs and confusions can be
traced to improper attempts to recover intentional type information
from representation types." http://www.nhplace.com/kent/PS/EQUAL.html

So, now, instead of "littering" code with tests for nil (or perhaps
taking that as a cue to eliminate the possibility of nil), one has
complected nil and intentional context throughout.

Cheers,

Mike

Michael Forster

unread,
Oct 21, 2011, 1:05:55 PM10/21/11
to clo...@googlegroups.com

Yes: Was that a nil value for the key :foo in my map or did :foo not
exist? In Common Lisp, some such functions return multiple values,
de-multiplexing (deplecting?) the separate meanings of nil. Then, of
course, you need a multiple-values-bind special form to handle those
return values. Braids begetting braids.

Curious, this, when Clojure already says (not (= nil ())).

Mike

Michael Forster

unread,
Oct 21, 2011, 10:16:47 AM10/21/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 8:35 AM, Timothy Baldridge <tbald...@gmail.com> wrote:
[...]
> What Rich is advocating is this: throw all the data into a hashmap.
> Suddenly, my SQL driver can just dump data in to the map, I can throw
[...]

I suspect he might have meant even more when he said, "... learn SQL, finally."

SQL is more than a convenient wrapper around fopen, fread, and fwrite.
Consider the possibility of expressing as many of your business rules
as possible, declaratively, in SQL. Then, beyond simply "dumping"
data into the map from the DBMS, consider that the validation
functions could be queries asking the DBMS if the user input is valid.

Cheers,

Mike

Sean Corfield

unread,
Oct 24, 2011, 1:19:41 PM10/24/11
to clo...@googlegroups.com
On Fri, Oct 21, 2011 at 10:05 AM, Michael Forster <mi...@sharedlogic.ca> wrote:
> Yes:  Was that a nil value for the key :foo in my map or did :foo not
> exist?

If you need to distinguish between ":foo is missing" and ":foo's value
indicates non-existence", what about:

(get my-map :foo ::missing)
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Ambrose Bonnaire-Sergeant

unread,
Oct 24, 2011, 1:25:48 PM10/24/11
to clo...@googlegroups.com
Or:

(if-let [[_ v] (find my-map key)]
   v
   :something-else)

Brian Marick

unread,
Nov 12, 2011, 5:37:17 PM11/12/11
to clo...@googlegroups.com

On Oct 21, 2011, at 4:03 PM, Rich Hickey wrote:

> I like nil punning, and find it to be a great source of generalization and reduction of edge cases overall, while admitting the introduction of edges in specific cases. I am with Tim in preferring CL's approach over Scheme's, and will admit to personal bias and a certain comfort level with its (albeit small) complexity.

Late to this party, but around 1984, George C. Charrette (sp?) wrote a brilliant post to the common lisp mailing list. He told of a dream in which (he said) he'd suddenly realized Scheme was right about everything where it and Common Lisp differed. So, in a white heat of inspiration, he took a relatively simple CL function and rewrote it, step by step, by removing nasty CL-isms like nil punning. Of course, at each step, the function got wordier, more special-case-ey, and (arguably) harder to understand.

It was a masterpiece of snark. I've never been able to find it since. If anyone has a copy, I'd love to get one.

-----
Brian Marick, Artisanal Labrador
Now working at http://path11.com
Contract programming in Ruby and Clojure
Occasional consulting on Agile


daly

unread,
Nov 29, 2011, 9:15:57 AM11/29/11
to clo...@googlegroups.com
The Clojure community might find this article interesting.
http://www.dalnefre.com/wp/2011/11/fexpr-the-ultimate-lambda

He points out that Fexpr is more primitive (in the sense of
"simple") than Lambda. Fexpr decouples the operand access
from the operand evaluation allowing more detailed control.
(Fexpr is an old MacLisp term.)

Given an s-expression there is always a question of what the
symbols mean. The meaning is supplied by the environment, of
which there are many. For instance, there is a dynamic
environment (runtime call), the static environment (the
value at the time the text is written), the macro environment,
etc. See chapter 2 of Lisp in Small Pieces for a really
in-depth discussion.

It is hair-hurting discussions like this that make lisp so
much more interesting than other languages. There isn't a
way to express the concepts in other languages.

Tim Daly
"There is no such thing as a *simple* job" :-)

daly

unread,
Nov 29, 2011, 9:28:42 AM11/29/11
to clo...@googlegroups.com
In http://www.dalnefre.com/wp/2011/11/fexpr-the-ultimate-lambda
Dale Schumacher writes:

"
One way to look at the difference between functional and object-oriented
algorithms is to consider the relationship between types and operations.
Let’s say I have a small set of types {A, B, C} and operations {f, g, h,
+}.

f
g
h
+
A
f(x:A) → A
g(x:A) → B
h(x:A, y:C) →
B
x:A + y:A → A
B
f(x:B) → B
g(x:B) → C
h(x:B, y:C) →
A
x:B + y:B → B
C
f(x:C) → C
g(x:C) → A
n/a
x:C + y:C → C

Table 1 shows how these operations might be modeled with functions. The
same function name refers to different operations based on the argument
type(s). Operations are grouped by function name, as shown by the
columns of the table."

Consider the table above. You can "walk the type circle" from A->A
by the calls: g(A)->B, g(B)->C, g(C)->A, or equivalently,
g(g(g(A)))->A.

Now consider making the same table for Clojure types and functions.
"A" might be a list, "B" might be a hash-map, etc. The "f" function
might be conj.

Given such a clojure table, the question is: Can we complete the
circle for each data type? Are we missing any functions? Is our
set of chosen functions "orthogonal"? Given the set of types and
a list of functions could we construct the table automatically?

Tim Daly

Craig Brozefsky

unread,
Nov 29, 2011, 12:48:02 PM11/29/11
to clo...@googlegroups.com
daly <da...@axiom-developer.org> writes:

> Given an s-expression there is always a question of what the
> symbols mean. The meaning is supplied by the environment, of
> which there are many. For instance, there is a dynamic
> environment (runtime call), the static environment (the
> value at the time the text is written), the macro environment,
> etc. See chapter 2 of Lisp in Small Pieces for a really
> in-depth discussion.

I lost my copy of Lisp In Small Pieces, and it's prolly the book I miss
the most. Another book that I got alot out of was Concepts, Techniques,
and Models of Computer Programming (Van Roy and Haridi). It's a great
tour of many "paradigms" of programming. If you are enjoying this
topic, you might want to check it out. It is less Lisp specific,
obviously.


--
Craig Brozefsky <cr...@red-bean.com>
Premature reification is the root of all evil

Reply all
Reply to author
Forward
0 new messages