This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-09-04
Channels
- # announcements (9)
- # babashka (5)
- # beginners (50)
- # calva (13)
- # clj-kondo (11)
- # clojure (30)
- # clojure-australia (1)
- # clojure-china (1)
- # clojure-europe (4)
- # clojure-filipino (1)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-norway (2)
- # clojure-sg (1)
- # clojure-taiwan (1)
- # clojurescript (32)
- # conjure (2)
- # honeysql (1)
- # hyperfiddle (8)
- # jobs-discuss (9)
- # leiningen (2)
- # malli (3)
- # off-topic (21)
- # reitit (4)
- # shadow-cljs (10)
- # sql (3)
- # squint (17)
- # tools-deps (14)
Hi, is it possible to override map accessors for a specific tagged map (hope I got my terminology correct)?
(def my-map #some-tag{:a 1 :b 2})
(my-map :a) => some specific logic
(:a my-map) => the same specific logic
(get-in my-map [:a]) => the same specific logic
#some-tag
invokes a data reader for some-tag
which should return an instance of some data type.
What you can and cannot do with that data type then depends on that particular type.
I would say that you should look suspiciously on desires to do so
If you need custom logic, make a function
I want to create some kind of lazy map that evaluates (and also access itself) on access. I realize I don't want to override the core map, hence the tag, to separate it and treat it as a special type, but I do want as less deviation as possible from the core map API. I'm still a beginner so I might be using the wrong instruments for the task...
I'm with @U064X3EF3 on this one. I would ask why you need this facility? What you've presented is a solution to a problem you may have. Without understanding why you think you need to specialise map in the first place it's hard to suggest a more idiomatic approach. Normally you would write a function that takes a map as an argument and does whatever you require.
Well, I wrote about it here: https://clojurians.slack.com/archives/C03S1L9DN/p1661716278630149 The gist is that on our company we work extensively with a data structure the requires self-ref and evaluate on access and be open to the std map api (get-in, assoc, merge, etc...).
That would be awesome 🙏 😊
Sounds like you want to define your own type that behaves like a map but is not really a map.
You can deftype
your own type and implement all ClojureScript protocols that PersistentHashMap
does:
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L7995
Yeah, that's probably what I want. Is it possible to only override the access method and inherit all other implementations? Or must I specify each and every method?
You must provide all protocols for the behavior you want. There is no inheritance.
You can probably leave out quite a few, such as IMeta
if you don’t require your type to carry metadata.
I don't want it to bite me back when someone will use some functionality that is supposed to work with a map but won't work for my map, so I guess I'll have to implement the entire interface...
So I'm trying to experiment with it (at worst I'll just earn a better understanding of cljs type & protocols system).
(def ^:private lookup-sentinel (js-obj))
(deftype LazySelfrefMap [meta cnt root ^boolean has-nil? nil-val ^:mutable __hash]
Object
(toString [coll]
(pr-str* coll))
(equiv [this other]
(-equiv this other))
;; EXPERIMENTAL: subject to change
(keys [coll]
(es6-iterator (keys coll)))
(entries [coll]
(es6-entries-iterator (seq coll)))
(values [coll]
(es6-iterator (vals coll)))
(has [coll k]
(contains? coll k))
(get [coll k not-found]
(-lookup coll k not-found))
(forEach [coll f]
(doseq [[k v] coll]
(f v k)))
ICloneable
(-clone [_] (PersistentHashMap. meta cnt root has-nil? nil-val __hash))
IIterable
(-iterator [coll]
(let [root-iter (if ^boolean root (-iterator root) (nil-iter))]
(if has-nil?
(HashMapIter. nil-val root-iter false)
root-iter)))
IWithMeta
(-with-meta [coll new-meta]
(if (identical? new-meta meta)
coll
(PersistentHashMap. new-meta cnt root has-nil? nil-val __hash)))
IMeta
(-meta [coll] meta)
ICollection
(-conj [coll entry]
(if (vector? entry)
(-assoc coll (-nth entry 0) (-nth entry 1))
(loop [ret coll es (seq entry)]
(if (nil? es)
ret
(let [e (first es)]
(if (vector? e)
(recur (-assoc ret (-nth e 0) (-nth e 1))
(next es))
(throw (js/Error. "conj on a map takes map entries or seqables of map entries"))))))))
IEmptyableCollection
(-empty [coll] (-with-meta (.-EMPTY PersistentHashMap) meta))
IEquiv
(-equiv [coll other] (equiv-map coll other))
IHash
(-hash [coll] (caching-hash coll hash-unordered-coll __hash))
ISeqable
(-seq [coll]
(when (pos? cnt)
(let [s (if-not (nil? root) (.inode-seq root))]
(if has-nil?
(cons (MapEntry. nil nil-val nil) s)
s))))
ICounted
(-count [coll] cnt)
ILookup
(-lookup [coll k]
(-lookup coll k nil))
(-lookup [coll k not-found]
(cond (nil? k) (if has-nil?
nil-val
not-found)
(nil? root) not-found
:else (.inode-lookup root 0 (hash k) k not-found)))
IAssociative
(-assoc [coll k v]
(if (nil? k)
(if (and has-nil? (identical? v nil-val))
coll
(PersistentHashMap. meta (if has-nil? cnt (inc cnt)) root true v nil))
(let [added-leaf? (Box. false)
new-root (-> (if (nil? root)
(.-EMPTY BitmapIndexedNode)
root)
(.inode-assoc 0 (hash k) k v added-leaf?))]
(if (identical? new-root root)
coll
(PersistentHashMap. meta (if ^boolean (.-val added-leaf?) (inc cnt) cnt) new-root has-nil? nil-val nil)))))
(-contains-key? [coll k]
(cond (nil? k) has-nil?
(nil? root) false
:else (not (identical? (.inode-lookup root 0 (hash k) k lookup-sentinel)
lookup-sentinel))))
IFind
(-find [coll k]
(cond
(nil? k) (when has-nil? (MapEntry. nil nil-val nil))
(nil? root) nil
:else (.inode-find root 0 (hash k) k nil)))
IMap
(-dissoc [coll k]
(cond (nil? k) (if has-nil?
(PersistentHashMap. meta (dec cnt) root false nil nil)
coll)
(nil? root) coll
:else
(let [new-root (.inode-without root 0 (hash k) k)]
(if (identical? new-root root)
coll
(PersistentHashMap. meta (dec cnt) new-root has-nil? nil-val nil)))))
IKVReduce
(-kv-reduce [coll f init]
(let [init (if has-nil? (f init nil nil-val) init)]
(cond
(reduced? init) @init
(not (nil? root)) (unreduced (.kv-reduce root f init))
:else init)))
IFn
(-invoke [coll k]
(-lookup coll k))
(-invoke [coll k not-found]
(-lookup coll k not-found))
IEditableCollection
(-as-transient [coll]
(TransientHashMap. (js-obj) root cnt has-nil? nil-val)))
(defn lazy-selfref-map
"keyval => key val
Returns a new hash map with supplied mappings."
[& keyvals]
(loop [in (seq keyvals), out (transient (.-EMPTY LazySelfrefMap))]
(if in
(let [in' (next in)]
(if (nil? in')
(throw (js/Error. (str "No value supplied for key: " (first in))))
(recur (next in') (assoc! out (first in) (first in')))))
(persistent! out))))
I copied the PersistentHashMap
Implementation and just renamed it for starter to see if I get the same behavior under my type, but I failed pretty fast:
(.-EMPTY LazySelfrefMap) => nil
(transient (.-EMPTY LazySelfrefMap)) =>
:repl/exception!
;
; Execution error (Error) at (<cljs repl>:1).
No protocol method IEditableCollection.-as-transient defined for type undefined:
(lazy-selfref-map :a 1) =>
:repl/exception!
;
; Execution error (Error) at (<cljs repl>:1).
No protocol method IEditableCollection.-as-transient defined for type undefined:
.-EMPTY
returned nil and transient threw an exception (because it didn't get any valid type).
I tried to understand where's .-EMPTY
implementation is but couldn't figure it out, can someone please help me shed some light, or even point me to some guide that will help me get started?the lib i mentionned: https://github.com/Malabarba/lazy-map-clojure (no self ref though)
Thank you, maybe I can learn how to implement a map from there 😄
Hi frontend amateur here, building a small app for friend. Frontend question: giving that the dynamic/lazy import exist, and it's posible to "load js as you go", what would be the advantage of building a Multi Page App? For context I been playing around with next.js and now I'm looking for something similar in cljs, I see shadowjs allows to do codespliting, if on top I could pre-render re-frame/reagent I would achieve the same functionality as next.js , also I don't see a point for SSR besides SEO. I know that if the app is small enough, SPA would do just fine 🙂, but this is a theoretical question, in case I ever need to build something bigger.
Hello all, I am trying to use dvingo/cljs-styled-components {:mvn/version "0.1.11"}
in a Reagent app. I added this to the deps.edn
, and the docs say to include the following:
(:require [cljs-styled-components.reagent
:refer [defstyled]])
However when running clj
, it gives the following error message:
Unexpected error (ExceptionInfo) compiling at (REPL:1).
No such namespace: cljs-styled-components.reagent, could not locate cljs_styled_components/reagent.cljs, cljs_styled_components/reagent.cljc, or JavaScript source providing "cljs-styled-components.reagent" (Please check that namespaces with dashes use underscores in the ClojureScript file name) in file
Does anyone know what i may be doing wrong?The group ID of that dependency is its name, not dvingo
. So it should be cljs-styled-components/cljs-styled-components {:mvn/version "0.1.11"}
in deps.edn
.
My bad, that was something I tried to fix the error but it actually doesn’t change anything. My deps.edn now reads cljs-styled-components/cljs-styled-components {:mvn/version "0.1.11"}
and still the same error message.
FWIW, with shadow-cljs I get a different error:
The required JS dependency "styled-components" is not available, it was required by "cljs_styled_components/common.cljc".
Dependency Trace:
app/core.cljs
cljs_styled_components/reagent.cljc
cljs_styled_components/common.cljc
And after running npm i styled-components
, the build got fixed.
No clue what's going on on your side.Are you sure you e.g. added the dependency to the root :deps
or to an alias that you 100% specify during the UI build?
Oh hmm. Yeah I am using clj -M -m cljs.main -co build.edn -v -c -r
so no shadow-cljs
Shouldn't really matter in this case - as long as that jar is on the classpath of the CLJS build process.
I think so… this is my deps.edn file: https://github.com/philip-peterson/clojure-reagent-starter-kit/blob/master/deps.edn
’sok, thanks anyway!
Wow, great find, thank you. That’s crazy, so was it treating it as a sub-dependency of reagent/reagent or something?
Oh, I guess it was just getting swallowed up? Curious, did you find this by staring at the source code or did you run a command to help debug?
It was placed as the reagent dep configuration, but ignored because it didn't match any configuration key