You should treat rand and friends like i/o. Don't bury them deep in your code. Write your pure functions so that they take a seed value (or sequence). Generate the random values from outside of the important functions, maybe providing a convenience wrapper function around your main logic.
(defn foo-pure [val seed] ...) (defn foo-uses-rand [val] (foo-pure val (rand))) On Dec 5, 2012, at 1:48 PM, JonC <[email protected]> wrote: > Ok: first of all, Clojure has outstandingly the best core library I've seen. > I am awed by how wonderfully the protocol approach makes data structures, and > the good taste applied to getting an API that's reasonably minimal and > wonderfully complete. I've not posted before because I've had nothing to ask > - everything is super-clear, especially now I have a copy of JoC. But I would > suggest that there is one easily corrected area where things are imperfect: - > > One of the big advantages of functional coding to me is rock solid > testability - same inputs, same outputs; easy to test, easy to debug. > > Obviously psuedo-random numbers and io present problems with this. Now, the > normal way for an API to discourage something problematic is to make it > difficult, and to encourage correct behavoiur by making it easy. So list's > lack of nth but possession of first, second rest and last shout out "Do NOT > use for random access!" while vector's api says "Use me instead!" > > Now we come to rand, rand-int, rand-nth. They're the easiest things to use > for their purpose, so they shout "Use me!" But.. they break the repeatability > paradigm. Because they use Java's default random number generator instance, > which has a private and unsettable seed, you can't get repeatabilty. In Java, > this isn't actually too awful because you can instantiate an rng of your own > and use it in just the same way - but if you do this in clojure, bang goes > your rand-nth. Yes, the problem is one you can easily solve with a few > minutes coding. But an api should gently lure into doing things the right way > rather than wrong one - so wouldn't it be a good idea to add change the > standard api to include a function that takes a seed? A random that allows > you to see the last "seed" would be even better. That way when you get > > => (foo-uses-rand "bar') > ..crazy result *sometimes* > > then > > => (def wrong (get-last-rand-seed)) > > would give you the seed needed to repeat the last call to any random fn. So > you could fix your code and verify by re-seeding with "wrong" and repeating > the function call. > > To me this seems a nice low-impact solution. It wouldn't make repeatability > bullet proof, but I'd suggest it would be an easily implemented mild > improvement for a future version of clojure. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to [email protected] Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/clojure?hl=en
