Is there a shorthand for adding an entire namespace to sci opts? I thought that was possible but I don't see it here: https://github.com/borkdude/sci/blob/1dd8b77062f328f5cd87435858cf91f165bcadd4/src/sci/core.cljc#L168-L170
There isn't and there hasn't been one. You can use a combination of ns-publics
and sci/copy-var
Not sure why I thought there was :thinking_face: But this will work as well, thanks
I'm not too keen on automatically doing this since I always feel there has to be some curation to keep GraalVM binary size in check :)
That or maybe problems with macros or other stuff. I usually build it up var by var
It seems like multimethods are shared between the host and the SCI environment. Is this intentional?
Not sure? Can you give an example?
(defmethod ig/init-key :some/key [_ _opts] 123)
(sci/eval-string
"(require '[integrant.core :as ig])
(ig/init-key :some/key {})"
{:namespaces {'integrant.core {'init-key ig/init-key}}})
;; => 123
I assume it works for any multimethod that you pass in
well, you are sharing the multimethod which gets information from your host environment. this is the same with any other closure
Interesting. I was expecting the state to be isolated. But if the state is stored in the mutlimethod var, then it makes sense it gets passed along
it might be different for defmulti's you define inside sci
yeah if it's created inside of sci, then it shouldn't leak out to the host
But defmethods are modified
I think that is true: https://github.com/borkdude/sci/blob/1dd8b77062f328f5cd87435858cf91f165bcadd4/src/sci/impl/multimethods.cljc#L72
the "global hierarchy" in sci is per ctx
but if you are passing in an external multimethod impl then it has access to your host's global hierarchy
I see in that code also that it closes over an atom which belongs to that multimethod only
so it's effectively just a closure you're passing into sci
That's troublesome for me then. I actually want to use sci to isolate code. So I might have to think of a different strategy
A different strategy would be to redefine those multi's in sci code itself
or create another defmulti in your host with a local hierarchy: > By default Clojure's multimethods dispatch off of a global hierarchy map.
like here: https://clojuredocs.org/clojure.core/defmulti#example-543d75b3e4b0a3cf052fe476
I want to use the multimethod of Integrant. I'm not sure if it's possible to override that
I'd probably have to create my own implementation
hmm, maybe the global hierarchy can be re-bound to some dynamic var? I'm not sure
no, it's not dynamic. https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L1725 but you could patch that :P
haha
Sounds like a weird feature. being able to rebind the global hierarchy
Might as well define your own. Sadly Integrant doesn't support that
it should then really be called the default hierarchy
you could propose it to the core team
I honestly don't know if I like that idea myself
with-redefs might work here
note that this is not thread safe
I think I'd rather fork integrant to actually do what I want it to do
maybe you could also make just a shim function that calls defmethod but registers the value with a unique prefix for that sci ctx
The "value" being the keyword?
Or do you mean the function name?
Something like this:
(defmulti foo identity)
(defmacro reg-method [sci-ctx & body]
`(defmethod foo ~sci-ctx [sci-ctx#]
~@body))
(reg-method "sci-ctx-1" (+ 1 2 3))
(reg-method "sci-ctx-2" (* 2 4))
(foo "sci-ctx-1") ;=> 6
(foo "sci-ctx-2") ;=> 8
but then the body of the reg-method macro will wrap your integrant calls
and you will pass in a function which wraps reg-method to sci so it seems to the sci program as if you're calling integrant normally
so integrant only supports one global system, like mount basically?
integrant is not defining “global systems” at all with integrant it is definitely possible to init multiple configs and run systems from them
It doesn’t take a local hierarchy so all multimethod defs are global, which is @kevin.van.rooijens issue
Component definitions are global. System definitions are local.
Exactly so when you want an isolated sci environment with defs that are only valid there, you will need a custom hierarchy
If this was my problem, I would probably just fork integrant and patch it up. I've done the same with other libs, but mostly for GraalVM purposes
I suppose protocols have the same problem
multimethods support a local hierarchy, protocols don't
it's just that as a library author you should also support local hierarchies
should/can/expose
Do multi methods even support swapping that out?
Nope. I guess you could provide your own multi methods...
You can add a custom hierarchy when defining the multimethod with defmulti
Right. But that doesn't really help here because sci would still inherit that hierarchy rather than the one you'd like it to
That would help. You could map the sci function to call into a function which uses a local hierarchy for your ctx.
Imagine if you could bind the integrant hierarchy to a custom one using dynamic var, then it would become trivial
Right, but I don't think multi methods support that.
of course they do:
(def ^:dynamic *h* (make-hierarchy))
(defmulti f identity :hierarchy #'*h*)
(defmethod f :default [_] "default")
(prn (f 1)) ;;=> default
(prn (binding [*h* (make-hierarchy)]
(defmethod f :default [_] "local-default")
(f 1))) ;;=> local default
you could have h point to the global hierarchy by default
so it's only a small tweak to integrant to support this
Crazy. I'm not sure they officially support that.
Interesting none the less.
Yes, they do. It's well documented.
Really? I think it's actually a side effect of how the implementation works. It calls the hierarchy (a map) like a function rather than using get
. Vars are functions, so that works. You can't call get on a var though.
Just look at the docs of defmulti
.
> Multimethods expect the value of the hierarchy option to be supplied as > a reference type e.g. a var (i.e. via the Var-quote dispatch macro #' > or the var special form).
OH. I apparently looked at the wrong thing there then. You're right, very clear 😁
Full disclosure: I actually just found this yesterday :P
But I suspected such a thing should be possible, given the name of global-hierarchy
. I mean, why have a global one if you can't have local ones. If only one was supported, then that thing should have been private.
hm, it is private
oh well
so a patch for integrant should be really easy
Well, I think it's supposed to be local to the defmulti
rather than local to the caller.
This does seem like it would impact every multimethod, not just the one in integrant
(def ^:dynamic *h* @#'clojure.core/global-hierarchy)
(defmulti f identity :hierarchy #'*h*)
(defmethod f :default [_] "default")
(prn (f 1))
(prn (binding [*h* (make-hierarchy)]
(defmethod f :default [_] "local-default")
(f 1)))
Using the above example, you can support local hierarchies in the binding only for your defmulti, so your defs are localbut when you don't bind, it works like normal
Yeah, there was an issue about adding an optional hierarchy, but that was never realized
Also Integrant development has stalled quite a bit
I see, so yeah, that would be the way to get out of this, an optional hierarchy or some hierarchy you can bind via a dynamic var, defaulting to the global one.
I'll go think about what would be the best option here. Thanks for the suggestions
Whenever I play around with SCI I always get fun problems to solve haha
The definitions are global, but you can run multiple systems no problem.
yeah right, you can run multiple instances of a global system
but not multiple different systems?