This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-09-29
Channels
- # announcements (6)
- # babashka (23)
- # beginners (15)
- # biff (15)
- # calva (17)
- # clara (5)
- # clj-kondo (41)
- # cljdoc (2)
- # cljs-dev (67)
- # cljsrn (18)
- # clojure (19)
- # clojure-europe (25)
- # clojure-nl (2)
- # clojure-norway (9)
- # clojure-uk (2)
- # clojurescript (26)
- # core-typed (6)
- # cursive (15)
- # data-science (30)
- # datahike (1)
- # datomic (18)
- # docker (6)
- # emacs (10)
- # events (2)
- # graalvm (15)
- # graphql (5)
- # hugsql (4)
- # jobs-discuss (1)
- # joker (7)
- # lsp (36)
- # malli (28)
- # off-topic (46)
- # other-languages (1)
- # pathom (5)
- # pedestal (6)
- # polylith (5)
- # reitit (2)
- # releases (1)
- # rewrite-clj (63)
- # shadow-cljs (7)
- # spacemacs (16)
- # squint (6)
- # tools-deps (6)
- # xtdb (13)
I tried to instrument a CLJS multimethod (the dispatch function), but it does not seem to work. I can call it with wrong input type, and it still works:
(defn my-dispatch
{:malli/schema [:=> [:cat :string] :keyword]}
[s]
(keyword s)
(defmulti a-multi-method
my-dispatch)
(defmethod a-multi-method :default [_]
:the-default)
;; The below does not crash like it should
(a-multi-method 4)
Thanks, that’s an interesting idea. The assumption is that the original function will be cached inside the multimethod storage? Anyway, it did unfortunately not work.
Also, to clarify, calling the my-dispatch
function normally from somewhere else with wrong args triggers validation
It looks like this is due to how multimethods are implemented. https://github.com/clojure/clojurescript/blob/961807166c8cf4d45a225d63416f06464fb27eaf/src/main/cljs/cljs/core.cljs#L11343 the dispatch-fn is passed into a JS object (deftype) and that captures the original function value. Just tried this in JS:
var afn = function afn(){ console.log('FIRST')}
y = {x: afn}
afn = function (){ console.log('SECOND')}
y.x()
// => FIRST
so I think this is what the problem iswe may be able to mutate the MultiFn
's dispatch-fn
property when we instrument, but then the defmulti
would have to have the instrumentation applied, not the function
> Thanks, that’s an interesting idea. The assumption is that the original function will be cached inside the multimethod storage? Anyway, it did unfortunately not work.
Yes, I was also going to suggest #'my-dispatch
but I forgot how vars work in CLJS.
Taking one step back, the most valuable thing for our codebase would be to be able to declare the contract for the multimethod in general. Instrumenting the dispatch function is more like a hack to get at least some of the value. I know that instrumenting defmulti
is probably a large and maybe not attractive subject, just mentioning it.
To illustrate what I mean
(defmulti say-hi
{:malli/schema [:=> [:cat [:map [:nationality :keyword]]] :string]}
(fn [person]
(:nationality person)))
(defmethod say-hi :german
"Guten tag")
I think I remember some earlier discussion indicating that the above is simplistic and probably not a good idea, but did not find it. And @U055XFK8V, just to clarify, this is the thing you are saying is not possible in CLJ?
Yes, I've looked at clojure.lang.MultiFn and there's no obvious way to achieve this.
I managed to do this for defprotocol https://blog.ambrosebs.com/2022/09/08/schema-defprotocol.html
actually, MultiFn isn't a final
class so perhaps a wrapper can be created. I might look into that.
I'm thinking we leave everything as-is, but an instrument!
function when called on a multimethod would set the multimethod var to a thin wrapper around MultiFn.
The tricky part is making sure that wrapper works with existing defmethod
calls. And if we subclass MultiFn, it might work.
This is all about the mechanics of doing the instrumentation, I also seem to remember there are issues with the semantics. Will try to look it up
Maybe forget that last thing. So at least the way we use multimethods, it makes sense to enforce a shared input/output contract for all defmethod
s. The only thing that is not clear is how/where on the defmulti
to define the schema.
Pinging @U055NJ5CC in case he has opinions on this
Thinking something like
public MyMultiFn extends MultiFn {
public MyMultiFn(IFn wrapper, MultiFn inner) { }
public Object invoke(args ...) {
wrapper.invoke(inner, args...);
}
....
}
(defn instrument-multi! [var checking-wrapper]
(alter-var-root var (fn [multi] (MyMultiFn. checking-wrapper multi)))
I managed to prototype defmulti instrumentation successfully for Prismatic Schema, so I take back my assertion that this was impossible.
Any progress on this? Or an issue that I can monitor. Would love support for instrumenting multimethods. Thanks!
I managed to do this for schema with some success, but it wasn't accepted. I lost steam on the malli version https://github.com/plumatic/schema/pull/451
Thanks for sharing