Fork me on GitHub
#clojure
<
2022-03-21
>
Maxime Devos15:03:49

Does someone know what <https://clojurians-log.clojureverse.org/tools-deps/2022-01-15/1642211588.065700> is about --- how could AOT'ing a library cause problems for the users of the library?

Maxime Devos15:03:45

I mean, it will have to compiled to .class anyway, so what does it matter when it is compiled?

hiredman15:03:27

aot compilation is transitive, meaning you will end up generating class files for other libraries your lib depends on

hiredman15:03:05

Then you have the choice of either shipping with those class files in the jar, so your library jar is effectively an uberjar, or you try and strip those out, and hope that any classes your library's aot code depends on are regenerated

1
hiredman15:03:15

All terrible options

Alex Miller (Clojure team)15:03:16

that's not necessarily a problem in itself

Alex Miller (Clojure team)15:03:42

the bigger problem imo is that if the library compiles, it is making a choice of Clojure compiler version and library context

👍 2
Alex Miller (Clojure team)15:03:15

at the time of use, the application may want to use a different Clojure compiler and library set and it will not have that choice

Alex Miller (Clojure team)15:03:20

we work hard to minimize possible forward (and backward!) compatibility for compiled classes (compiled Clojure code calls into the Clojure runtime, macros are expanded at compile time, etc), but generally libraries pay less attention to this stuff so it's not hard to make a library compiled against a dep that breaks at runtime when used in combination with an older or newer version of the dep

1
Maxime Devos18:03:07

https://clojurians.slack.com/archives/C03S1KBA2/p1647876867046429?thread_ts=1647876649.816469&amp;cid=C03S1KBA2: Good point. I'll check if it AOT's the dependencies or if AOT'ing the dependencies can be disabled.

Maxime Devos18:03:29

https://clojurians.slack.com/archives/C03S1KBA2/p1647877220398979?thread_ts=1647876649.816469&amp;cid=C03S1KBA2: not a problem for me, because due to how Guix works, the dependents are automatically recompiled when the Clojure packaged in Guix is updated, but good point

Maxime Devos18:03:51

I think I understand now.

hiredman18:03:04

clojure compilation is generally not repeatable, so if you aot compile library A, and it transitively aot compiles library B, the classfiles for library A may refer to classes from B, that won't be emitted or will be compiled with different names the next time B is compiled

hiredman18:03:40

so you aot compile A, remove the classfiles that were emitted for B, then distribute A, and someone loads up A, which means B gets loaded and compiled, but the classfiles B generates this time have a different name, then the files emitted for A cannot be linked and boom

hiredman18:03:34

in general, things like guix, nix, etc, are basically built around C's compilation model (which plenty of other languages also have), the ability to have separate compilation, compile a shared library once and loading it into different processes, etc

hiredman18:03:27

if you are exceedingly careful you may be able to do that with clojure, but it is far from the model out of the box, and chat logs, jira, and github issues, are littered with the broken drreams of those who thought they could do it and it would be fine

😆 1
mars0i23:03:25

Sometimes, when you're checking for equality of floating point numbers, you don't care about a small difference. For example, I want 10.000000000002 and 10 to be treated as the same, but I don't want to just round them, because 10.001 should be different from 10. It's easy to write an equality-within-epsilon test, and I've done it before. Just wondering whether there exists some library that for some reason would be better than rolling my own when I need this.

noisesmith00:03:43

I don't know of a lib for this and am pretty confident there's nothing in core, but I'd base my solution on Math/ulp, which tells you the difference between the number and the next higher magnitude number

(ins)mp3munge.load=> (clojure.pprint/print-table
                        (map (fn [x]
                               {:x x
                                :ulp (Math/ulp x)})
                             (take 10 (iterate (partial * 100) 0.001))))

|       :x |                   :ulp |
|----------+------------------------|
|    0.001 | 2.1684043449710089E-19 |
|      0.1 | 1.3877787807814457E-17 |
|     10.0 | 1.7763568394002505E-15 |
|   1000.0 | 1.1368683772161603E-13 |
| 100000.0 | 1.4551915228366852E-11 |
|    1.0E7 |   1.862645149230957E-9 |
|    1.0E9 |  1.1920928955078125E-7 |
|   1.0E11 |       1.52587890625E-5 |
|   1.0E13 |            0.001953125 |
|   1.0E15 |                  0.125 |
nil
mp3munge.load=>

noisesmith00:03:20

I'd want the epsilon to be some multiple of the ulp of the number

mars0i00:03:52

Oh, that's nice. Thanks @U051SS2EU. I thought there might be a better idea than whatever occurs to me.

mars0i03:03:05

The particular effect I'm getting is from java.lang.Math trigonometric functions. The documentation for those functions specifies that they are allowed to return a value 1 ulp away from the correct value. That allows me to figure out exactly what the tolerance should be. So your tip is particularly appropriate. I'd never heard of ulp's.

Sam Ritchie09:03:08

what’s nice here is it has a protocol-based approach that means you can compare containers etc with ish? that have floating point values inside

mars0i20:03:00

Ah, excellent. Thanks @UQY3M3F6D, @U017QJZ9M7W.

mars0i21:03:32

I'm already using apache.commons for something else--so using that sounds appealing--but also I like the more general Clojure solution. (I have not seen this explained, but as far as I can tell, there's a reorganization of the math classes underway, and numbers seems to be one of the new pathways, while in the past all of the math code was under math3, where an earlier version of Precision lives.)