On Wed, Jul 6, 2011 at 3:14 AM, Konrad Hinsen <konrad.hin...@fastmail.net> wrote: > For a Clojure approach to this specific problem, see > > http://clojure.github.com/clojure-contrib/complex-numbers-api.html > > which is based on the generic arithmetic from > > http://clojure.github.com/clojure-contrib/generic.arithmetic-api.html > > and thus ultimately on multimethods. There is a performance penalty to such > approaches, but it lets you formulate mathematical hierarchies much better > than Java's OO style. Note that this approach is not based on types or on > mathematical concepts such as fields, but only on operations: the real and > imaginary parts of a complex number can be any value that supports > arithemetic. This permits complex numbers with nonsensical elements, but I > have not found this to be a problem in real life.
I wonder if generic math should become part of core for 1.4? Multimethods of course are too slow for core arithmetic functions but we have protocols now and when a protocol is implemented inline in a datatype it runs as efficiently as any Java method call. The compiler would still need to special-case +, -, etc. with primitive arguments and those would retain the blinding speed of primitive arithmetic. The biggest issue would be extending the protocol to boxed primitives, BigInteger, and BigDecimal; extending a protocol to a class that doesn't implement it inline isn't as efficient. BigInteger and BigDecimal arithmetic, already slow, would get a tiny bit slower; it's the impact on plain Integer, Double, etc. that could be worrying. Can the compiler generate more efficient calls to protocol methods in the presence of type hints or type inference sufficient to know the correct protocol implementation? So if you (extend-protocol + Integer ...) during bootstrap, (+ some-integer some-other-integer) could be as efficient as the current non-protocol-based + is when the compiler knows the first argument is an Integer, whether from hinting it or by some other means? >> similarly with complex numbers, writing (complex x y) is very long >> compared to xiy, or x + >> iy. Would it be possible to modify the reader so that, eg. 3i5 would >> return a complex type? > > Yes, but I doubt you'd have much success lobbying for such a change. The > tradeoff between cost (increased complexity of the reader) and utility > (limited to a small number of users) doesn't look very promising. Moreover, > my personal experience doing REPL math in Python shows that complex > constants are pretty rare. You can make it as simple as (i 3 5) by appropriately defining a function or a macro i. (You might want to use definline so you can both use i in HOFs, e.g. (map i list-of-real-parts list-of-imaginary-parts), and get slightly more efficient bytecode out of embedded complex values in code, with (i a b) as efficient at generating a complex number as [a b] is at generating a vector when at least one of a and b is not a literal constant; though (i 3 5) would not be competitive with [3 5] for runtime speed without reader and compiler(!) changes to embed complex numbers directly in code. When you really need efficient access to a complex constant you'd thus need to bind a non-dynamic global Var to the constant and then use it, e.g. (defn slowish-math-routine [foo] (frobnicate foo (i 3 5)) might become (def i35 (i 3 5)) (defn faster-math-routine [foo] (frobnicate foo i35)) to get the kind of speed of access you get with embedded vector constants, embedded map constants, and suchlike. (This is one more problem with Clojure's lack of ability to embed arbitrary objects in eval'd sexps: it makes non-built-in data types and even things like sorted-map not quite first class citizens because you have to jump through extra hoops to make it possible to embed constants of these types in code and get the same runtime efficiency as with [x], {x y}, #{x}, etc. With the arbitrary object in code capability efficient literals could be made even without read-table access: #=(i 3 5) would embed a Complex in code given an appropriate function i defined somewhere where it would be available at read time. A bit ugly but not much worse than the existing literal syntax for hashsets.) >> So basically I'm stuck - short of writing a maths library from scratch >> with pluggable types and dispatch by operation name, anyone got any >> ideas? Cheers for your help > > One idea (but not a simple one to implement) is to define a domain-specific > language for maths (standard Clojure syntax but with different semantics) > and compile it into standard Clojure using a set of macros. Indeed; you can define some macro like (with-my-math body-goes-here) that locally redefines some stuff, e.g. (definline make-complex [x y] `(Complex. ~x ~y)) (defprotocol Addable (plus [this other])) ... (defmacro with-my-math ...) ... (with-my-math ...) and the latter expanding to (xlet [i make-complex + plus] - minus ...] ...) But what is "xlet"? Plain let here would seem to work, but I'm not sure the compiler would generate efficient bytecode for (+ x y) and the like in that case versus when + is a non-dynamic global Var. If it would, then xlet can just be let. Otherwise it may need to be some kind of exception-safe temporary alter-var-root! that saves, changes, and at scope exit restores the values of the named Vars. (Obviously "binding" only works for dynamic vars, whose access is less efficient still than a local var + in (+ x y), and we don't want dynamic scope anyway, only lexical.) If you can get a safe and reliable enough macrolet implementation for Clojure, then you can use (macrolet [(i [x y] `(make-complex ~x ~y)) (+ [x y] `(plus ~x ~y)) ...] ...) and get good efficiency with nicely readable code. Otherwise you'd have to resort to the var root thing, and I'm not 100% sure the compiler would handle that ideally and generate for (xlet [+ my-+] (+ x y)) as efficient code as for (my-+ x y) (but it seems much less likely if xlet = let, and certain to perform poorly, have side effects down the line, and not work with non-dynamic vars if xlet = binding). -- Protege: What is this seething mass of parentheses?! Master: Your father's Lisp REPL. This is the language of a true hacker. Not as clumsy or random as C++; a language for a more civilized age. -- 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