This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-05-27
Channels
- # bangalore-clj (1)
- # beginners (50)
- # boot (23)
- # cider (19)
- # clara (28)
- # cljsrn (4)
- # clojure (93)
- # clojure-greece (67)
- # clojure-spec (14)
- # clojure-uk (11)
- # clojurebridge (1)
- # clojurescript (151)
- # community-development (1)
- # core-async (19)
- # core-matrix (1)
- # cursive (3)
- # emacs (1)
- # klipse (5)
- # leiningen (5)
- # luminus (3)
- # lumo (22)
- # nyc (1)
- # off-topic (17)
- # om (12)
- # onyx (16)
- # pedestal (5)
- # re-frame (20)
- # reagent (5)
- # spacemacs (10)
- # uncomplicate (2)
- # unrepl (11)
- # untangled (8)
- # vim (1)
Why is it that some of the methods I add to a deftype work and others don't? I have a deftype with this in it:
IHash
(-hash [this] (goog/getUid this))
and doing (-hash thing)
returns a value.
But I also have my own IDable
protocol defined, with this in the deftype:
IDable
(-id [this] (do-stuff this))
And (-id thing)
errors out in Figwheel with "Use of undeclared Var my_ns/-id"
What am I missing about protocols and deftypes?However, if I make a function within that namespace: (defn get-id [thing] (-id thing))
then calling (get-id thing)
works.
Oh, the methods are only resolving when I'm in that namespace that the deftype was defined in...
because the namespace that defines IHash is already in scope?
what is cljs.core/NONE
and does it need to be public?
If I have this in one namespace:
(defprotocol IThingable
(-thing [t]))
(defn thing-impl [t]
(str "thing " t))
(deftype Thing [thing]
Object
(toString [_] (pr-str thing))
IThingable
(-thing [t] (thing-impl t)))
(defn thing [t] (-thing t))
And this in another namespace:
(defn custom-thing-impl [t]
(str "custom " (-thing t)))
(deftype CustomThing [cthing]
Object
(toString [_] (pr-str cthing))
IThingable
(-thing [_] (custom-thing-impl cthing)))
(thing (CustomThing. (Thing. 1)))
;=> "custom thing 1"
(thing (CustomThing. (Thing. 2)))
;=> "custom thing 2"
(thing (CustomThing. (Thing. 3)))
;=> "custom thing 3"
I'm having to require/:refer all four of IThingable Thing -thing thing
, whereas I thought I'd need only Thing
and thing
.
Is that how it's suppose to work? Is there a way to automatically bring in the methods associated with an object?@nathanmarz sorry, didn't mean to bump your question off the page 🙂
what is cljs.core/NONE
and does it need to be public?
@john why not just namespace the functions instead of refer them?
if you use the method syntax, you aren't using the functions, and you don't need to namespace or refer it
but the function syntax is calling a function, the function needs to somehow be in scope
@noisesmith so, th/Thing
?
-thing is a function that belongs to the namespace that defines IThingable
and Thing is an object in the package corresponding to that namespace, but it's constructor functions are functions in that namespace (deftype auto-creates constructors right?)
so th/->Thing is a function
and some.package.Thing is a class in a package that looks a lot like your namespace but isn't exactly it
you mentioned needing to refer -thing
th/-thing is a function
(.-thing x) is a method call
you need to somehow scope functions, method calls are looked up when called
@nathanmarz it’s not public https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L3883
@john the defprotocol defines both the method and the function, and the function follows the same rules any other function you use does
and the method follows the method rules etc.
@noisesmith but the deftype doesn't appear to be creating .-thing
method calls
deftype doesn't create method calls
what I'm saying is that to call a method on an object that implements a protocol, you have two choices
a) call the method, b) call the function with the same name as the method
a means follow the rules for java interop (you get reflection if the type isn't clear in context etc.), b means follow the rules for clojure functions (the compiler complains if you don't scope things properly)
it doesn't call them
defprotocol creates them
deftype implements them
+user=> (defprotocol Frobable (baz [this]))
Frobable
+user=> (deftype Thing [] Frobable (baz [_] "bz"))
user.Thing
+user=> (ns other.ns)
nil
+other.ns=> (.baz (user.Thing.))
"bz"
notice I did this all via basic interop, no function or namespace features used
after making an ns alias, I can use the functions defined to do the same thing
+other.ns=> (alias 'u 'user)
nil
+other.ns=> (u/baz (user/->Thing))
"bz"
alias is what gets called when you put :as in an ns form btw
it's just that you can't require user :as u
@john oh - then you need to use the function interfaces and rules I guess?
sorry if I was dropping clj-vm only knowledge there
funny - (Thing.) works in cljs, but not .baz
sorry for the wild goose chase
I just really didn't think you needed to refer in the methods/functions associated with a particular deftype.
that's a common misconception - in jvm clj it is a messy space because protocols are two things in parallel, an interface with methods on it (no ns operations needed to access) and a clojure protocol with protocol-methods (actually functions, accessed the same way you would access any other function). So in a haphazard way I was trying to clarify the root of what I think causes the confusion.
@anmonteiro hmm, that's odd
asking because of https://github.com/nathanmarz/specter/issues/210
don't have access to a computer for a few days so will look into it more then
did the compiler stop complaining about circular dependencies in namespaces at some point?
my cljs started hanging and the only thing i can see that could have caused it is some ns changes 😕
ok, yep, was definitely a circular dependency
version 1.9.521
is not giving me any feedback, so i had to resolve it by walking through ns by hand 😞
Heyyy, I've seen the same problem before with an earlier version, but I didn't ever have time to create a repro that I could share.
@thedavidmeister it should work, but note we do not detect implicit circular deps
https://login.amazon.com/website <-- are there any cljs bindings for this?
@nathanmarz that’s probably just a bug we never noticed before around core names & private vars
anyone here at all familiar with datasync
?
going through the tutorial and I see this --
(ns your-app
(:require [dat.sync.client]
[dat.remote]
[dat.remote.impl.sente :as sente-remote]
[datascript.core :as d]
[dat.reactor.dispatcher :as dispatcher]))
(def conn (d/create-conn dat.sync.client/base-schema))
(defn new-system []
(-> (component/system-map
...
but I don't see component
declared anywhere
system-map is defined by stuartsierra/component
it's weird that they left that out of their require
ah, I see ... typo then I guess. I just noticed in the sample code what you're talking about
(ns dat.sys.app
(:require-macros [cljs.core.async.macros :refer [go-loop]]
[reagent.ratom :refer [reaction]])
(:require [dat.view]
[dat.reactor :as reactor]
[dat.remote]
[dat.remote.impl.sente :as sente]
;; TODO Chacge over to new ns
[dat.sync.client :as dat.sync]
[dat.sys.views :as views]
[dat.sys.events]
[dat.reactor.dispatcher :as dispatcher]
[datascript.core :as d]
[taoensso.timbre :as log :include-macros true]
[reagent.core :as r]
[com.stuartsierra.component :as component] ;; <--- oh hey there it is!
[posh.core :as posh]))
@miikka but if you have an explicit circular dep in your ns forms we should always be able to catch that
Yeah, I had that and there was no message and the compiler got stuck (no CPU usage, nothing would happen). Or maybe it was boot-cljs that got stuck. Didn't have time to investigate back then.
So, if I want to use -thing
of Thing
from ns thing-ns
(`:as tns`) in ns custom-thing
, should I use use tns/-thing
or tns/thing
and then provide a new def of thing
in ns custom-thing
that checks for instanceof? CustomThing
to provide it's own implementation, otherwise call tns/thing
on the passed in thing?
I thought the idea was that I'd never have to redefine or provide new implementations thing
tns/-thing is the normal way to do it - you pull in the namespace with the protocol to call the methods, you pull in the namespace with the type to create an instance
the idea is that code that is written using the protocol methods never has to know anything about your actual type
(only the code constructing an instance needs to know)
@noisesmith I'm pulling in IThingable, Thing and thing, and when I use -thing I get Use of undeclared Var custom-thing/-thing
. It just seems surprising to have to use thing-ns/-thing with thing-ns/Thing.
that's intentional
you should be able to write code that uses a protocol's methods, without knowing about the code that implements the data type
the methods belong to the ns defining the protocol
So, suppose I want to create a MultiThing that can take either a ThingOne or a ThingTwo. I want to implement the -thing of MultiThing that handles -thing of both ThingOne and ThingTwo. I'd like to implement against the interface, without caring about the particular implementation of ThingOne or ThingTwo.
I didn't think, for -thing of MultiThing, I'd need to handle both -thing of ThingOne and -thing of ThingTwo.
If I have use namespaced versions of -thing, it seems I'll have to handle each new Thing type individually.
@miikka there was definitely a :parallel-build
case where that could happen but I believe it’s been addressed and I haven’t heard much about that since
@dnolen, if you have circular deps, optimisations are enabled and you use :parallel-build, the compiler gets stuck. It's not a race condition, it should happen always. Try this: https://github.com/miikka/cljs-circular-deps-repro
All of the clojurescript examples of defprotocol/deftype I can find out there tend to do things all in one namespace. Hard to find examples of where polymorphism is leveraged across namespaces.
@dnolen, the parallel compiler threads wait until all their dependencies are compiled before proceeding, but of course with circular deps this never happens. Before this it probably worked because this was run before the parallel compiling starts: https://github.com/clojure/clojurescript/commit/9ad6d5d61cb96a9f8552489c6811a479b93f864c
@miika ah ok a regression. File an issue in JIRA thanks will look at when I have time.
So I have a reagent/reframe app and I have a JSON file of data that i would like to load into application state. Is there any way to read a static JSON file in CLJS? In ES6 i would just do import "./data.json"
with an appropriate webpack loader.
@colbydehart you could use a macro for this
;; macro
(defmacro defjson [name path]
`(def ~name (js/JSON.parse ~(slurp path))))
;; usage
(defjson data "data.json")
oh cool.
thanks, I’ll play around.
@colbydehart don’t know if you are familiar with macros in CLJS, but keep in mind that they should be declared in a separate *.clj
namespace
so I can put that in my core.clj
and then use it in my regular cljs files?
yes, just require it as usual function and use where needed
here’s more detailed guide https://github.com/clojure/clojurescript/wiki/Differences-from-Clojure#macros
oh awesome, thanks so much.
you are welcome 🙂
Like, in core, deref
is simply defined as a (-deref o)
(https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L1358)
Which will polymorphically use the -deref as defined on o. But is that only working because all of the relevant -deref definitions are occurring in the same namespace? If object/deftype methods don't survive across namespace boundaries, I'm unsure of how to implement the advertised polymorphism.
the method belongs to the protocol, code that wants to be polymorphic needs to utilize the namespace defining the protocol
Right, and it seems as though because most code has core protocols and deftypes in scope, it seems quite easy to just make another object that has a -deref method and your downstream code can just be used by deref and @
transparently. But if different implementations of -deref exist in different namespaces, I'm just not grokking how to maintain that polymorphism.
you don't need to care about the implementation namespace
all that matters is the namespace with the protocol
well, you need the implementation namespace in order to make an instance of the thing... but that's it
That's my intuition as well. But I'm just too dense to translate that intuition into something that works.
if your code uses the protocol methods, call them based on the namespace with the protocol definition
that's it
ignore the namespace where the object was defined, it doesn't matter
@noisesmith Okay I think I'm starting to understand...
(ns thing)
(defprotocol IThingable
(-thing [t]))
(deftype Thing [thing]
Object
(toString [_] (pr-str thing))
IThingable
(-thing [t] (str "thing " t)))
(defn thing [t] (-thing t))
(ns thing-one
(:require [thing :as thing]))
(deftype ThingOne [t]
Object
(toString [_] (pr-str t))
thing/IThingable
(-thing [_] (str "one " (thing/-thing t))))
(ns thing-two
(:require [thing :as thing]))
(deftype ThingTwo [t]
Object
(toString [_] (pr-str t))
thing/IThingable
(-thing [_] (str "two " (thing/-thing t))))
(ns things
(:require [thing :as thing]
[thing-one :as one]
[thing-two :as two]))
(deftype Things [t]
Object
(toString [_] (pr-str t))
thing/IThingable
(-thing [_] (str "things " (thing/-thing t))))
(thing/thing (Things. (thing/Thing. 0)))
;=> "things thing 1"
(thing/thing (one/ThingOne. (thing/Thing. 1)))
;=> "one thing 1"
(thing/thing (Things. (one/ThingOne. (thing/Thing. 1))))
;=> "things one thing 1"
(thing/thing (Things. (two/ThingTwo. (thing/Thing. 2))))
;=> "things two thing 2"
But that slightly surprises me, since Things
's -thing is is calling the -thing of Thing, rather than -thing of ThingOne or ThingTwo, BUT the correct -thing of those deftypes are actually being called.
Is that because thing/-thing
in the Things
definition above is actually refering to the -thing
of the prototype, not the deftype, in the thing namespace?
In which case, I may not need to require thing-one and thing-two in things ns (if I'm working with them in another ns) and it'll just do the right thing... I'll try that
Right.
Thing doesn't own the method, I think it's clearer if the protocol namespace had no deftype in it
can ajax only load xml/json, or can I also GET pngs, like GET https://the-site.com/static/img20.png
@noisesmith okay it's finally working. Thank you SO much for helping me finally understand this piece.
After generating the a basic luminus template with reagent, when I remove the call to render the navbar in core.cljs mount-components function, the navbar still gets rendered
I can see Figwheel reloading the js, though
I don't see any error messages anywhere, but maybe not looking in the right place
can someone help me troubleshoot?
(I'm new to cljs dev)
How do I install and include Bootstrap into my project without relying on cdnjs for development?
you can npm install the package and the css and js will be under the node_modules folder
you can either add node_modules to your src paths or copy them to your current source paths
sorry, i think you want resource-paths (assuming you're using lein) https://github.com/technomancy/leiningen/blob/stable/sample.project.clj#L298
so you can add 'node_modules' to resource-paths and then the url would be /bootstrap/css/bootstrap.css
@doubleagent that's probably because an explicit call to unmount the node is needed in that case because that's a root level react render
yeah, I wouldn't expect figwheel to unmount things on code reload - that sounds right now that you mention it