On Sat, 2 Jul 2011 18:26:21 -0700
Mark Engelberg <mark.engelb...@gmail.com> wrote:

> Ideally, I was hoping to start a more in-depth discussion about the
> pros and cons of "programming in the large" in Clojure than just
> waxing poetic about Clojure/Lisp's capabilities in the abstract :)
> 
> Yes, much of the initial excitement around Clojure comes from the
> feeling of "Wow, I can do so much with so little code".  But at some
> point, all projects grow.  I'm thinking that by now, there may be
> enough people using Clojure in large projects and on large teams to
> offer some good feedback about how well that works.
> 
> My Clojure codebase is somewhere around 2-3kloc and I already feel
> like I'm bumping up against some frustration when it comes time to
> refactor, maintain, and extend the code, all while keeping up with
> ongoing changes to libraries, contrib structures, and Clojure
> versions.

We have above 6.5K lines of Clojure (src only) growing and it's all structured 
with name spaces.
We still have a mixed code base here (Java + Clojure + JRuby) and we had already
name spaces to structure the code.
The code base is structured in 10 different projects.
We use Eclipse and CounterClockWise for dev. Dev coding/testing is done in 
Eclipse
by specifying projects in dependencies.

We use leinigen to build these for Q/A and prod.

Moving from 1.0 to 1.2 was not  painful. We did it methodically. With basic 
tests in each
project, we spotted issues quite fast. We rolled this over a week roughly.

> 
> I want to hear war stories from those with even larger code bases than
> mine.  Has it proven to be a major hassle on large projects to avoid
> circular dependencies in the modules?  Are the lack of debugging
> tools, documentation tools, and refactoring tools holding you back?
> Anyone miss static typing?

Again using name spaces/individual projects here is the key to avoid circular 
dependencies.

We do not miss static typing at all, in fact we are in the process of
getting rid of the Java code. The goal is to clear this by next fall.

For debugging when it's serious, we use the Eclipse JVM debugger
and look at the Clojure runtime context when needed.

As far as documentation tool we rely on (doc ...) and document our code 
accordingly.

Since the code ratio versus Java is around one to 10, refactoring is not
a big deal even without the heavy assistance you may get in Java from
your IDE.


> 
> One of my main gripes is that some of Clojure's built-ins return
> nonsensical results (or nil), rather than errors, for certain classes
> of invalid inputs.  To me, one of the main benefits of functional
> programming is that debugging is generally easier, in large part
> because failures usually occur within close proximity of the flaw that
> triggered the failure.  Erlang, in particular, has really promoted the
> idea of "fail fast" as a way to build robust systems.  But Clojure's
> lack of a "fail-fast" philosophy has burned me several times, with
> hard-to-track-down bugs that were far-removed from the actual cause.
> The larger my code grows, the more this annoys me, reminding me too
> much of my days tracking down bugs in imperative programs.

Were did you find the link between functional languages and close proximity of
errors ? That's a language design decision. You may want to use assertions
on your fns to validate inputs. That sould improve your ability to track errors
before they carry things too far from the spotwhere it failed.
I would not trade this for systematic exception reporting.

> 
> One specific example of this is get, which returns nil whenever the
> first input isn't something that supports get.  For example, (get 2 2)
>  produces nil.  This becomes especially problematic when you pass
> something to get that seems like it should support get, but doesn't.
> For example, (get (transient #{1}) 1) produces nil, when there's
> absolutely no reason to think that (get (transient #{1} 1) would
> behave any differently from ((transient #{1}) 1).
> 

The choice was made not to throw exceptions. Agree, it may feel frustrating
at the beginning. That's a choice that accommodate others while frustrating the
other half.

For your specific case, the first arg does not support the interface that get 
expects,
however you may do this:

(get 1 1 "WHATTHE...")

The third parm is the "not found" value. That may shed some light if your code 
starts to carry this value
elsewhere. Or add assertions to your fns or create a wrapper fn.

As for transient sets, pretty sure this is a bug in 1.2.1:

user=> (get (transient {:a 1}) :a)
1
user=> ((transient {:a 1}) :a)
1
user=> (get [1] 0)
1
user=> (get (transient [1]) 0)
1
user=> (get #{1} 1)
1
user=> (get (transient #{1}) 1)
nil     <--- Oups...
user=> 

Dunno if it is fixed in 1.3, no time to play with it these times.



-- 
Luc P.

================
The rabid Muppet

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to