Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend numerical protocols in Clojure

I'm trying to use protocols to create an engineering number type (a "knumber"), so I can say (+ "1k" "2Meg") and get something like "2.001Meg". I should be able to get the floating point value from the knumber like so (:val my-knumber), but normally the printer should display the string, which is also accessible like so (:string my-knumber). This number will support all the usual p, n, u, m, k, Meg, G suffixes, and convert as required among them, such as (/ "1Meg" "1G") -> "1m"). I want to be able to pass this to any function which expects a number.

Anyway, Can someone suggest a strategy for this? I think I need to use protocols. I currently have a (defrecord knumber [val string]) but I'm not sure what's next.

What protocols do clojure numbers satsify? I'm thinking I need to extend some existing protocols/interfaces for this.

Thanks

like image 304
Sonicsmooth Avatar asked Aug 04 '12 19:08

Sonicsmooth


1 Answers

I think your strategy should probably be as follows:

  • Define the record KNumber as something like (defrecord knumber [value unit-map])
  • Make unit-map a map of units to integer exponents (you are going to want units like m/s^2 if these are engineering numbers, right?). It might look something like {"m" 1 "s" -2},
  • Have KNumber implement java.lang.Number so that you can use it with other mathematical functions that already exist in Clojure. You'll need to implement doubleValue, longValue etc.
  • Define a protocol NumberWithUnits that you can extend to both KNumbers and normal clojure numbers. At a minimum it should have methods (numeric-value [number]) and (get-units [number])
  • Then define your mathematical functions +, *, - etc. in your own namespace that operate on anything that implements the NumberWithUnits protocol and return a KNumber.
  • Regarding different unit scales (e.g. "m" vs. "km") I would suggest standardising on a single scale for internal representation for each unit type (e.g. "m" for distances) but providing options for conversion to other unit scales for input/output purposes.
like image 80
mikera Avatar answered Nov 14 '22 00:11

mikera