I must say that I find Richard's suggestions to be very reasonable,
but I'd like to add my own take... This is meant as a step-by-step
account of my thinking process, so it starts out with a statement of
the basics. Skim & skip if you prefer.

The problem to be solved is basically a huge calculation. When written
out -- for the purpose of defining the problem -- in the usual
mathematical notation, it turns out to depend on multiple free
variables (inflation rate, interest on the mortgage etc.); then
there's a bunch of named parameters / dependent variables whose values
are derived from those free variables.

So, the natural thing to do is what you do in that .pdf you posted:
you break it down into a bunch of equations defining the dependent
variables in the above in terms of the free variables and / or
previously defined dependent variables plus one inequality of the
form, say, rent-profit <= sale-profit, whose truth or falsity is to be
established.

Now, an imperative programme to calculate whether rent-profit does or
does not exceed sale-profit would proceed as follows:

free-variable-1 = some-value-1
free-variable-2 = some-value-2
...
dependent-variable-1 = some-function-of(free-variable-k,
free-variable-l, free-variable-n)
...
answer = rent-profit-function(...some-arguments...) <=
sale-profit-function(...some-arguments...)

A functional programme, on the other hand, would contain no section to
correspond to the free-variable-n assignments at all. Then in place of
all the dependent-variable-m assignments it would contain function
definitions:

(defn calculate-dependent-variable-1 [free-var-k, free-var-l ...]
  ...)

When you start composing those, you do things like

(defn calculate-problematic-quantity [...all free vars needed in side...]
  (let [dep-var-1 (calculate-dependent-variable-1 ...the arguments...)
        dep-var-2 (calculate-dependent-variable-2 ...the arguments...)
        ...]
    ...combine the free-vars received as arguments
    with the dep-vars calculated in the bindings portion of the
    let form to produce the return value...))

Feel free to receive dependent variables as arguments if you're likely
to have multiple functions depend on them in some way:

(defn f1 [dep-var-1 ...] ...)
(defn f2 [dep-var-1 ...] ...)
(defn function-using-f1-f2 [...args...]
  (let [dep-var-1 (calculate-dependent-variable-1 ...args...)
         f1-val (f1 dep-var-1 ...)
         f2-val (f2 dep-var-1 ...)
    ...))

Then once you do have all the building blocks in place, you'd write
one monster entry function taking some values for the free variables
-- perhaps in the form of a map -- and have it call the
building-block-functions, perhaps using the sequential binding
facitility of the let special form to name intermediate values for use
in further computational steps.

This process strikes me as incredibly similar to what you would do if
you were to solve the equations by hand: you break them down into
well-defined intermediate steps, write down recipes defining the exact
nature of those steps, then write down formulae combining the results
of those intermediate steps. You strive to reduce the number of free
variables any single equation depends on, preferring to break down
huge equations into smaller ones which depend on parameters defined by
further small equations. Etc.

In Haskell you can even write the "main equations" on top, followed by
the helper equations lower down, possibly limiting the scope of the
latter:

calculateFoo x y z = fooComponent1 + fooComponent2
  where
    fooComponent1 = (some expression in x, y, z)
    fooComponent2 = (some expression in x, y, z)

In Clojure you'd use a let form for that, which may be a blessing or a
curse both syntactically (the issue of what you put on the top) and
semantically (because "where" and "let" bindings are mutually
recursive in Haskell). The basic idea stays the same, though, with
Haskell's syntax perhaps making it slightly more obvious how the
Haskell functions involved are almost direct transliterations of the
mathematical, purely declarative statements of the nature of the
calculational subproblems. That syntax is something you'd actually
have to learn the (considerable) quirks of, so some find the Lisp
syntax-less approach ultimately more manageable (myself included).

Anyway, I'd say that your project is the perfect dream use case for
functional programming... I wonder if I've managed to expose some of
my reasons for feeling this way? :-)

An additional note: given the purpose of the calculations involved, I
guess you'll want to be well-assured of the correctness of the result.
Using maps to pass around named values may help with that; I'd even go
as far as using assertions and / or preconditions to make sure that
each function does receive all the values it expects in its argument
map.

What I *would not* do, however, is to mass around one huge map all the
time; I'd still only give each function the exact arguments it must
receive, putting them in a map only to avoid silly bugs like (f
interest inflation) instead of (f inflation interest). The latter may
be hard to catch; a misdefined map like {:interest inflation
:inflation interest} is easy to catch in code review (and not very
likely to be written in the first place).

All best,
Michał

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