Right, nesting everything inside a single function makes it impossible to
unit test the inner functions--they don't even have names in the global
scope!  In retrospect, I think the solution I posted is more cute than it is
practical.  Passing around a map of intermediate values seems to be the
winner all around, but it still seems a little odd to me.  Idiomatic Clojure
code manipulates maps far more than I've seen in any other language.

On Fri, Feb 19, 2010 at 11:30 AM, Yaron <ygol...@gmail.com> wrote:

> With this approach how would I test the individual functions defined
> inside of the let? Wouldn't they be invisible to me and the test
> framework which would only see "sell-or-rent"?
>
> On Feb 18, 4:27 pm, John Williams <j...@pobox.com> wrote:
> > I'm no Clojure guru myself, but one approach you may want to consider is
> > nest all your auxiliary functions inside the main function, and use
> ordinary
> > let-bindings the same way you've been trying to use global bindings:
> >
> > (defn sell-or-rent [{:keys [supplied-1 supplied-2]}]
> >   (let [derived-1 (+ 1 supplied-1)
> >         derived-2 (* 2 supplied-2)]
> >     (letfn [(f [x y z] ...)
> >             (g [x] ...)]
> >       ...)))
> >
> > This has the disadvantage of indenting all your helper functions much
> more
> > than they would be otherwise, but it gets the job done without any messy
> > global variables or thunks.  If you want to expose more functions than
> just
> > sell-or-rent, you could write some very OO-flavored code:
> >
> > ;; Define a function to create objects.
> > (defn pseudo-constructor [{:keys [supplied-1 supplied-2]}]
> >   (let [member-var-1 (+ 1 supplied-1)
> >         member-var-2 (* 2 supplied-2)]
> >     (letfn [(private-method-1 [] "priv1")
> >             (private-method-2 [] "priv2")]
> >       {:public-method-1 (fn [x y z] "pub1")
> >        :public-method-2 (fn [x y] "pub2")})))
> >
> > ;; Use the object.
> > (let [obj (pseudo-constructor {:supplied-1 1
> >                                :supplied-2 2})]
> >   (println ((:public-method-1 obj) "x" "y" "z"))
> >   (println ((:public-method-2 obj) "x" "y")))
> >
> > --jw
> >
> > On Mon, Feb 15, 2010 at 11:24 AM, Yaron <ygol...@gmail.com> wrote:
> > > I am writing a calculator to figure out if I should sell or rent my
> > > home using Clojure. This is my first Clojure program so I'm about as
> > > wet behind the ears as it gets. So far everything is actually going
> > > really well (reminds me of the fun I had with Scheme in college) but
> > > for one thing. My calculator needs 30+ arguments from the user in
> > > order to run. Furthermore I have a bunch of secondary values that are
> > > derived from the arguments the user submits.
> >
> > > So imagine the user submits a value A. I will have a value B whose
> > > definition will be something like (+ 1 A) (yes, more complex in
> > > reality, but you get the idea).
> >
> > > If I were back in Java or C# I would define a class, submit A (and
> > > it's 29+ friends) in the constructor and then create a property on the
> > > class B.
> >
> > > In Clojure I have taken a different approach. I first create (def *A*
> > > 3) where 3 is a completely bogus value I just made up. Then at run
> > > time I use bindings to re-bind A to the actual value the user passed
> > > in.
> >
> > > But my problem is, what to do about B? I thought of doing something
> > > like (def *B* 3) and then in the binding passing in a function like
> > > (defn B-Gen [] (+ *A* 1)) to create a new binding to B but I quickly
> > > realized this would be a bug inducing nightmare. If I forget to
> > > include one of the derived values in the binding or put them in the
> > > wrong order then I would get the wrong value.
> >
> > > So what I currently do is:
> > > (def *A* 3) ; A bogus value that will later be rebound
> > > (defn B [] (+ *A* 3))
> >
> > > The good news is, that this works and doesn't require any book
> > > keeping.
> >
> > > The bad news is that it's ugly. If I want to do something trivial like
> > > divide B by 2 I have to call B as a function(/ (B) 2) instead of the
> > > more natural (/ B 2). And of course this approach is pretty
> > > unfortunate from a performance perspective as I'm constantly having to
> > > recalculate what are effectively static values. Yes, I could use
> > > memoization but many of these values are pretty trivial (usually just
> > > algebra equations) and I suspect the overhead of memoization exceeds
> > > the perf improvement.
> >
> > > But in any case the whole approach of having to take what really are
> > > static values and turn them into functions feels really hacky. So my
> > > guess is that I'm thinking about this problem the wrong way. I'm stuck
> > > in my old imperative/OO constructor world.
> >
> > > What's the right way to think about primary values that will be
> > > rebound (once) that then have dependent values that need to be
> > > recalculated when that rebinding happens?
> >
> > >      Thanks,
> >
> > >              Yaron
> >
> > > --
> > > 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<clojure%2bunsubscr...@googlegroups.com>
> <clojure%2bunsubscr...@googlegroups.com<clojure%252bunsubscr...@googlegroups.com>
> >
> > > For more options, visit this group at
> > >http://groups.google.com/group/clojure?hl=en
>
> --
> 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<clojure%2bunsubscr...@googlegroups.com>
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
>

-- 
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