Giter Club home page Giter Club logo

primitive-math's Introduction

Clojars Project cljdoc badge CircleCI

About

Clojure's numeric tower is useful, but it can put a lot of steps between you and simple arithmetic. Unfortunately, while Clojure will warn you when reflection is required to invoke a function, it will not warn you when reflection is required to perform math. The only reliable way to discover whether you're calling clojure.lang.Number.add(Object, Object) or clojure.lang.Number.add(long, long) is to use a profiler or decompiler.

Or, you can just bypass Clojure's math operators altogether.

In the clj-commons.primitive-math namespace, there are equivalents for every arithmetic operator and comparator that will give a reflection warning if it cannot compile down to a simple, predictable, unboxed mathematical operation.

clj-commons.primitive-math> (set! *warn-on-reflection* true)
true
clj-commons.primitive-math> (+ 3 3)
6
clj-commons.primitive-math> (defn adder [x] (+ 1 x))
;; gives a reflection warning
clj-commons.primitive-math> (defn adder [^long x] (+ 1 x))
;; no reflection warning
clj-commons.primitive-math> (+ 3.0 3)
;; gives a reflection warning AND throws an exception

To support operations on both long and double types without any reflection, these operators are defined as macros. This means they cannot be used as higher-order functions:

clj-commons.primitive-math> (apply + [1 2 3])
;; throws a 'cannot take value of macro' exception

In practice, it's usually preferable to import the namespace with a prefix, and use p/+ and p/== operators alongside the normal Clojure functions. However, if in a particular namespace you never need to use higher-order operators, you can call (primitive-math/use-primitive-operators) to swap out the Clojure operators for their primitive equivalents. This can be reversed, using (primitive-math/unuse-primitive-operators).

Notes

Pre-1.0.0 versions of primitive-math used a single-segment namespace. This causes problems for Graal and clj-easy. For 1.0.0, everything was copied under the clj-commons namespace. The code is effectively identical, however, so unless you are using Graal, you don't need to make any changes. If you are using Graal, make sure you only require the clj-commons.* namespaces to avoid issues.

Usage

;;; Lein
[org.clj-commons/primitive-math "1.0.1"]

;;; deps.edn
org.clj-commons/primitive-math {:mvn/version "1.0.1"}

An exhaustive list of operators

+
-
*
/  ;; aliased as 'div'
inc
dec
rem
==
not==
zero?
<=
>=
<
>
min
max
bool-and
bool-or
bool-not
bool-xor
true?
false?
bit-and
bit-or
bit-xor
bit-not
bit-shift-left   ;; aliased as '<<'
bit-shift-right  ;; aliased as '>>'
unsigned-bit-shift-right  ;; aliased as '>>>'
byte
short
int
float
long
double
byte->ubyte
ubyte->byte
short->ushort
ushort->short
int->uint
uint->int
long->ulong
ulong->long
reverse-short
reverse-int
reverse-long

Full documentation can be found at cljdoc.

License

Copyright © 2016 Zachary Tellman

Distributed under the MIT License.

primitive-math's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

primitive-math's Issues

Interesting behavior when missing typehints

While working with custom types I noticed some interesting and unexpected behavior when using primitive-math:

(deftype Foo [^short n])

(def foo (Foo. 42))

(p/inc (.n foo))

This results in the following exception:
IllegalArgumentException No matching method found: inc clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)

If we hint foo as ^Foo, i.e. (p/inc (.n ^Foo foo)) things work as expected. I'm not really sure why this is happening--I'm guessing that macro expansion is at least part of the issue?

Reflection warning

Reflection warning: file:/Users/erik/Documents/github.com/primitive-math/src/primitive_math.clj:266:3 - call to java.math.BigInteger ctor cannot be resolved

Adding float support

I've been using primitive-math for awhile now and found it quite helpful inside the hot loops in my code, but recently came across the need for floating point support, manipulation due to working with some graphics libraries that have to communicate with the GPU. Would a PR adding support for float (adding at a minimum support for eq gt neq and other comparisons) be of interest and useful, or are floats intentionally being held out?

Still relevant/difference with :warn-on-boxed?

Since Clojure 1.7, they added: (set! unchecked-math :warn-on-boxed) to warn you of unchecked math. I was curious to know if that renders primitive-math mostly irrelevant, or if there are still situations where it can provide better value.

Thanks.

Please Wrap Eval in Unit Tests with a Binding

At my employer, when attempting to import the source for your lib, I hit an "interesting" quirk of the compiler/runtime, which is only (mostly?) relevant when running your unit tests under a non-Lein non-Boot toolchain (specifically one which does not give a binding for *ns*):

Your use-primitive-operators and unuse-primitive-operators functions use the local value of *ns* to decide which functions to ns-unmap. At the REPL, or in a top-level form in a new ns, this is fine and works as expected.

However, within a testing apparatus, *ns* may default to the root value of the clojure.lang.Var, which is clojure.core. In such cases, these macros will mangle the clojure.core namespace interns. This is an issue because your test code calls eval at runtime. If and when clojure.core's namespace mappings get mangled as such, you cannot finish evaling because the == references will be gone from clojure.core.

I guess this is a bug report for the test suite, because it doesn't make sense to call those functions within user-space application code. Could you please add a (binding [*ns* *ns*] (in-ns 'other) (refer-clojure) ...) block (or the like) to the eval block as a matter of hygiene? Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.