I have to think that's preferable to submitting 30+ arguments to rent
and sell.

Or were you suggesting a different approach?

The different approach only works with a different approach :)

The way you've structured run-calculator, using the map is best, because 30 arguments is crazy.

Your need to pass 30 arguments to rent and sell because they need to obtain or compute all of their intermediate values. You have a tree, with rent and sell doing all the work.

The alternative approach is to make your functions take the intermediate values as arguments explicitly, and define those functions much like your equations. You then bind the intermediate values in a let... perhaps best shown by example. Instead of

(defn house-depreciation
"The amount of depreciation that can be claimed on Federal Income Taxes in year y"
        [y args]
        {:pre [(valid-year? y args)]}
        (cond
                (> y 27) 0
                (< y 27) (/ (house-sales-price 0 args) 27.5)
                (= y 27) (* 0.5 (/ (house-sales-price 0 args) 27.5))))

you would write

(defn house-depreciation
"The amount of depreciation that can be claimed on Federal Income Taxes in year y"
        [y house-sale-price]
        {:pre [(valid-year? y args)]}
        (cond
                (> y 27) 0
                (< y 27) (/ house-sale-price 27.5)
                (= y 27) (* 0.5 (/ house-sale-price 27.5))))
                

This transformation applies to all of your functions, so even rent and sell get defined in terms of their intermediate values, just as I showed in a much earlier email.

Your calculator would become

(defn run-calculator
  [args]
  (let [...
        house-sale-price (some-fn ... some-arg)
        ...
        house-depreciation (house-depreciation y house-sale-price)
        ...
        sell-value (sell house-sale-profit-0 rmoc mib)
        ...]
   (- rent sell)))

This way your individual functions become really simple, expressed in terms of their direct named inputs and outputs only, just like your PDF's equations. Your calculator function becomes the place where the web of interconnected values is
realized through a sequence of intermediate values.

You can easily test each individual function, just as you can now, only without
the overhead and syntax of those extra maps (which carry a ton of extra
values and obscure what's happening). Individual functions are less likely to recompute redundant values (I'm sure rent and sell both involve some common terms), and that avoidance doesn't involve jamming intermediate values into the
argument map.

More interestingly, this means you can build calculators for different things (maybe comparing the tax advantages of different mortgages and depreciation tricks...) by using the same functions. I don't know if that's possible with
the map approach as you've written it.

What you've done with the argument map approach is essentially to use a map as
an object: the map is a bunch of fields, your derived-args function is a
constructor (which sets up the derived members), and your individual functions
are methods on that object. It's OO with a mask.

Now, OO is sometimes the right way to do things, but if you really want to see whether a more functional approach has advantages in this situation, you should
consider whether you can invert things a little.

Just a thought.

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