Skip to content
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

Merged
merged 27 commits into from
Oct 28, 2023

Conversation

littleredcomputer
Copy link
Contributor

Value is now renamed IKind since kind is now the only method that inhabits the protocol.

The other methods have been made into Emmy generics with defmulti.

@codecov
Copy link

codecov bot commented Oct 10, 2023

Codecov Report

Attention: 93 lines in your changes are missing coverage. Please review.

Comparison is base (e16b569) 86.05% compared to head (49a92a3) 87.34%.

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     
Files Coverage Δ
src/emmy/abstract/function.cljc 84.72% <100.00%> (+0.80%) ⬆️
src/emmy/calculus/covariant.cljc 88.29% <100.00%> (+2.83%) ⬆️
src/emmy/calculus/form_field.cljc 89.83% <100.00%> (ø)
src/emmy/calculus/map.cljc 92.39% <100.00%> (ø)
src/emmy/calculus/vector_field.cljc 98.09% <100.00%> (ø)
src/emmy/differential.cljc 93.52% <100.00%> (+0.33%) ⬆️
src/emmy/env.cljc 94.80% <ø> (ø)
src/emmy/expression.cljc 74.81% <100.00%> (+1.73%) ⬆️
src/emmy/expression/analyze.cljc 94.16% <100.00%> (ø)
src/emmy/expression/compile.cljc 85.12% <100.00%> (ø)
... and 37 more

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

CHANGELOG.md Show resolved Hide resolved
Copy link
Member

@sritchie sritchie left a 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;
Copy link
Member

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?

Copy link
Contributor Author

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.

package.json Outdated Show resolved Hide resolved
patches/fraction.js+4.3.7.patch Outdated Show resolved Hide resolved
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"
Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?

Copy link
Member

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!

@@ -230,9 +220,9 @@
litfns)))

(u/sci-macro with-literal-functions [litfns & body]
Copy link
Member

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...

Copy link
Contributor Author

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))
Copy link
Member

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!

Copy link
Contributor Author

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!!

src/emmy/differential.cljc Show resolved Hide resolved
@@ -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]))
Copy link
Member

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?

Copy link
Contributor Author

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])
Copy link
Member

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...

Copy link
Contributor Author

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?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a change!!

Copy link
Contributor Author

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

Copy link
Contributor Author

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
Copy link
Member

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?

Copy link
Contributor Author

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))
Copy link
Member

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

Copy link
Contributor Author

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

@sritchie sritchie changed the title Littleredcomputer/retire value protocol Retire value protocol in favor of multimethods Oct 13, 2023
@sritchie
Copy link
Member

@littleredcomputer looking good pending the few comments that I left... thank you for doing this!!

@littleredcomputer
Copy link
Contributor Author

@littleredcomputer looking good pending the few comments that I left... thank you for doing this!!

I'm really happy to do this

I also added a console.log to the fraction patch: we're going to learn more
about how shadow-cljs deals with things like that
@littleredcomputer
Copy link
Contributor Author

Finally have enough coverage. Now on to fixing the fraction.js thing and we're done.

@littleredcomputer littleredcomputer merged commit a3a4954 into main Oct 28, 2023
6 checks passed
@littleredcomputer littleredcomputer deleted the littleredcomputer/retire-value-protocol branch October 28, 2023 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants