-
-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retire value protocol in favor of multimethods #149
Retire value protocol in favor of multimethods #149
Conversation
Next step (after cljs level-up) is to rename the remaining one-method Value protocol to IKind.
…with cljs numeric tower, but this puts things back into semi-usable state for the night)
some of these are interesting... how were they working before?
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #149 +/- ##
==========================================
+ Coverage 86.05% 87.34% +1.29%
==========================================
Files 98 98
Lines 15335 15383 +48
Branches 834 845 +11
==========================================
+ Hits 13197 13437 +240
+ Misses 1304 1101 -203
- Partials 834 845 +11
☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still reviewing but here is my first batch of comments, @littleredcomputer !
of the exponent; now it returns the one-like of the base, which is | ||
correct. | ||
|
||
- The generic `negative?` required Numbers instead of Comparables; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious to see how this is done, as I introduced this bug, not knowing you could do it another way... do you compare now to the zero-like of the type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup! I was inspired by similar examples you had designed.
src/deps.cljs
Outdated
@@ -1,4 +1,4 @@ | |||
{:npm-deps | |||
{"complex.js" "^2.1.1" | |||
"fraction.js" "^4.2.0" | |||
"fraction.js" "^4.3.6" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried that your patch won't come along for the ride and derivative projects like emmy-viewers will see a failure. Would we fix this by staying on an older version? We could also pull bigfraction.js into our project with proper credit in the header... I haven't done this but there must be a way to pull JS out of resources in a way that doesn't affect downstream folks? hmmm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good comment, and the developers of Fraction.js do suggest just "vendoring" the bigfraction.js if that's the API you want. I'm not sure quite where to put it.
On the other hand, the patches get swept in by the post install hook in package.json, so this feels like a principled approach...but if it does frustrate consumers of Emmy then it has to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I experimented using a local "vendored" version of bigfraction.js: doing that is easy enough but it needs a package.json to go with it. Fine, but that seems morally equivalent to the patch-package technique I've used. Since CLJS compilation can't happen without nom i
, and that will take care of applying the patch, are you still worried?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern is about downstream libraries like Emmy-viewers - will all dependents now have to configure this patch as well? I would be fine pinning to an older version of Fraction as well to avoid this!
src/emmy/abstract/function.cljc
Outdated
@@ -230,9 +220,9 @@ | |||
litfns))) | |||
|
|||
(u/sci-macro with-literal-functions [litfns & body] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We definitely want this indentation to match defmacro
... can you try adding
{:clj-kondo/lint-as 'clojure.core/defn}
to emmy.util/sci-macro
to see if vscode picks it up?
Of course
{:clj-kondo/lint-as 'clojure.core/defmacro}
makes more sense but I think defn
might be the only supported option...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixing by adding a .cljfmt-vscode.edn file. (vs-code reads this file for indentation formatting, but the one that exists has :indentation false
so it's not suitable for the editor. So the new one will rectify that as well as supplying the indentation rule needed here
@@ -235,33 +220,23 @@ | |||
(cs/union a b)) | |||
|
|||
(doseq [klass [PersistentHashSet PersistentTreeSet]] | |||
(defmethod g/zero? [klass] [s] (empty? s)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm counting on our tests to catch my tired eyes as I read these implementations... this is heroic work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Refactor in the presence of running tests" has never felt so relevant!!
@@ -19,6 +19,7 @@ | |||
an expression with respect to an analyzer is therefore effected by a | |||
round-trip to and from the canonical form." | |||
(:require [emmy.expression :as x] | |||
[emmy.generic :as g] | |||
[emmy.numsymb :as sym] | |||
[emmy.util :as u] | |||
[emmy.value :as v])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this import go away or no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's a ref. to g/freeze
and v/integral?
Some of the utility level stuff in v/ could move to g/, and then the import of v would occur less often. Willing to consider that
@@ -12,7 +12,7 @@ | |||
(:refer-clojure :exclude [ratio? numerator denominator rationalize]) | |||
(:require #?(:clj [clojure.core :as core]) | |||
#?(:clj [clojure.edn] :cljs [cljs.reader]) | |||
#?(:cljs ["fraction.js/bigfraction.js$default" :as Fraction]) | |||
#?(:cljs ["fraction.js/bigfraction.js" :as Fraction]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting, why do JS people keep messing with their builds...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did notice that (1) in the current version, there's an exports block in fraction.js's package.json, (2) bigfraction.js is not listed there, and (3) I still don't know why this was ever a problem on GitHub, where the older version had no exports clause and I guess was relying on a permissive interpretation.
I wanted to get "everyone onto the same page" and so experimented with adding and removing $default
. I don't even know what it's really for, I was just trying to shake things into place.
I'm quite open to the idea of just stashing bigfraction.js somewhere in the tree and forgetting about it
@@ -74,7 +74,7 @@ | |||
expr | |||
(let [canonicalized-expr (canonicalize new-expr)] | |||
(cond (= canonicalized-expr expr) expr | |||
(v/zero? | |||
(g/numeric-zero? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a change!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the one where what's being tested for zero is a lazy seq returned from simplify that got me wondering about this... I wanted to avoid the idea that a realized empty list could spoof this. I can put it back if you prefer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, I think what you had originally written is perfectly safe, in hindsight
(freeze [v] (:name (meta v))) | ||
(exact? [_] false) | ||
(kind [v] (type v)) | ||
;; Var |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious what you're thinking here, are these caught by any other line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we just have kind
for nil (returning nil). Since nil no longer gets any of the other formerly-Value methods, I thought, should it have a kind
?
If nil has no kind, i.e., it's not an Emmy value at all, a few things break. We lose this:
(defmethod g/partial-derivative [t nil] [f _]
(multivariate f [])))
Although I think that could be fixed by having the (kind nil)
be ::v/seqtype
. There are a few other cases of error that we could fix.
But the experiment of removing IKind
from nil
has kind of convinced me that we want the kind
function to be a total function, safe to call on any Clojure(Script) value, so that a defmethod could in principle be written for any object.
So I am comfortable with the status quo. Not having the other Value methods like zero?
defends against unfortunate nil-puns.
@@ -331,7 +246,7 @@ | |||
|
|||
IPrintWithWriter | |||
(-pr-writer [x writer _] | |||
(let [rep (if (<= x (.-MAX_SAFE_INTEGER js/Number)) | |||
(let [rep (if (< (if (< x 0) (- x) x) (.-MAX_SAFE_INTEGER js/Number)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch! I might recommend a test for this if it didn't make it in yet, which it might have, apologies for my code nav on this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something definitely breaks w/o this :)
What has happened, I think, is that there were some "overlapping" cases in the Value regime that have been clarified by the transition to defmulti, which focused my attention on the (very few) bugs that I found
@littleredcomputer looking good pending the few comments that I left... thank you for doing this!! |
I'm really happy to do this |
Finally have enough coverage. Now on to fixing the fraction.js thing and we're done. |
Value
is now renamedIKind
sincekind
is now the only method that inhabits the protocol.The other methods have been made into Emmy generics with
defmulti
.