This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-09-21
Channels
- # alda (1)
- # bangalore-clj (1)
- # beginners (7)
- # boot (88)
- # carry (2)
- # cider (8)
- # cljs-dev (60)
- # cljsjs (2)
- # cljsrn (45)
- # clojure (255)
- # clojure-belgium (5)
- # clojure-boston (1)
- # clojure-dusseldorf (3)
- # clojure-greece (49)
- # clojure-italy (10)
- # clojure-russia (30)
- # clojure-spec (78)
- # clojure-uk (11)
- # clojurebridge (1)
- # clojurescript (80)
- # cursive (14)
- # datomic (33)
- # defnpodcast (4)
- # devcards (2)
- # dirac (15)
- # editors (23)
- # emacs (5)
- # events (11)
- # funcool (1)
- # hoplon (1)
- # juxt (1)
- # luminus (2)
- # mount (7)
- # off-topic (15)
- # om (152)
- # om-next (2)
- # onyx (17)
- # parinfer (1)
- # proton (38)
- # re-frame (35)
- # reagent (110)
- # rum (3)
- # spacemacs (3)
- # specter (51)
- # test-check (2)
- # testing (5)
- # untangled (234)
@nathanmarz I can see the value, but as far as I can tell this wouldn't help with the precisely symmetric scenario of wanting to disj a single item from a set nested in a data structure .
@nathanmarz In general, I've been thinking about how assoc-in and setval automatically add levels to your data structure if needed, but I am not aware of a convenient way to make the path down to a leaf go away when the leaf becomes empty.
I'm having trouble building up a good mental model of how the complex navigators work. For example, I don't understand why these two navigators behave so differently with setval:
=> (setval [(filterer number?)] [:a :b :c] [:x 2 :y 4 :z 6])
[:x :a :y :b :z :c]
=> (setval [(walker number?)] [:a :b :c] [:x 2 :y 4 :z 6])
[:x [:a :b :c] :y [:a :b :c] :z [:a :b :c]]
@puzzler I think things like removing from a sequence or a map are fairly easy with variants on ALL
or keypath
https://github.com/nathanmarz/specter/issues/117
the trickier case is removing an entire path, such as specifying to remove a nested map when it becomes empty
I've done things like making navigators such as non-empty-keypath
, but I'm not sure having so many variants is the right approach
filterer
navigates to a single value, while walker
navigates to many values
think of filterer
as working just like filter
, sequence -> single sequence, except it also propagates changes back
whereas walker
is like ALL
Is there a way to predict this single value vs. multi value behavior from the info on the doc page?
(setval (subselect (walker number?)) [:a :b :c] [:x 2 :y 4 :z 6])
;; => [:x :a :y :b :z :c]
Similarly, I was confused the first time I encountered that setval on END expected a sequence, not a single value.
filterer
uses subselect
and ALL
the doc for filterer
could be clearer, but I think the doc for END
is pretty clear
"Navigate to the empty subsequence after the last element of the collection."
Yes, END was clear once I looked it up. Just didn't match my intuition, I guess. If these things are just something you have to learn over time, I guess that's the nature of complex libs, but I'm trying to figure out if there's some big-picture way of thinking about it that makes it clear without looking these details up. Sort of like how, in Clojure, once you realize that "sequence functions" put the sequence as the last input, and "collection functions" put the collection as the first input, it's a lot easier to remember.
the only way to do that would be with a naming convention, but that could get unwieldy
like you might want to include in the name whether it navigates to many values or a single value, to a substructure, subvalue, or view, etc
So that list you just typed is very interesting to me, because I realize I don't have a clear mental model of what kinds of things you can navigate to, when you talk about substructure, subvalue, view, etc. and what the different behavior of setval, transform, and select would be under those scenarios.
substructure is something like srange or subset, where it's the same structure as the parent but with a subset of the values
transforms on substructures transform the parent
subvalues are the common case where you literally navigate to that value inside the structure
and views navigate you to something with no structural relation to the input
something like view
totally replaces it's input
on transform
I don't think these are hard boundaries between navigators, subselect
is like a combination of substructure and view
learning to think in terms of substructure is probably one of the major milestones in using specter to its potential
So the other topic I mentioned is something I encounter a fair amount in my programming. Let's say I want to keep a map that bins words by first letter. So I start out with {}
. Now, I add the word apple {\a #{"apple"}}
and then banana and ape {\a #{"apple" "ape"} \b #{"banana"}}
. Now I remove apple. {\a #{"ape"} \b #{"banana"}}
Now I remove ape and I want it to go to {\b #{"banana"}}
. A similar scenario is a map of numeric counters. If a counter doesn't yet exist in the map, I create it at 0 and increment it to 1 on demand. When it decrements back to 0, I'd like the counter to disappear from the map.
I've thought about that a bit, and I think ideally this kind of information would be encoded and handled within the data structure implementation
you could do it with navigators like this: (setval [(non-nil-keypath \a) (nil<->val #{}) (subset #{"apple"})] #{} data)
, but I think it would work better at the data structure impl level
so for example a map would understand what "empty" values look like, and would automatically remove them if they become empty
and it would know how to initialize a non-existent key to the empty value
then you could do with specter: (setval [(keypath \a) (subset #{"apple"})] #{} data)
and everything would work in a nice and elegant manner
and it composes nicely to arbitrarily complex data structure combinations
constructing a data structure like that would be something like (def my-map ((hash-map (hash-map (hash-set empty-nil))))
Are non-nil-keypath and nil<->val new? I don't see them in latest stable or in the github repo.
A bit of a testimonial: Prior to now, I've been aware of specter, but figured I'd reach for it only if I really needed it -- get-in and update-in are usually sufficient for me. Today, I decided to use specter for the "trivial" things I could do with get-in and update-in; once I had it at my disposal, within a few hours I started seeing several non-trivial uses for it as well which made my implementation a lot cleaner.
Was everything in specter motivated by actual use-cases? There are several things I can't figure out what the utility would be, and I'm wondering whether some of the things are there just because they were an experiment to see if something was doable, or whether each one actually solves some particular problem that comes up in practice.
@puzzler no those are hypothetical navigators... non-nil-keypath would remove the value if it becomes nil and nil<->val would navigate to val
if it's nil and transform to nil
if it's val
. They would be trivial to implement
@puzzler yes, I use most of what comes with Specter, plus I have a whole lot of private navigators for dealing with DAGs
I think the only ones I don't use are ATOM
and the zipper navigators
for which ones do you have trouble seeing the utility?
Is this how I'm supposed to require specter macros ?
(:require-macros [com.rpl.specter.macros :refer [transform]]
from clojurescript
Ok just saw the changelog, no more macros namespace
Haven't imagined a use yet for anything relating to value collection (DISPENSE, collect, collect-one, terminal, terminal-val). Also don't see the point of the "view functions", i.e., transformed and view. I'm also not sure whether STAY and STOP have any direct utility, or if they are only useful in building other navigators. If you're using all these things, then there must be a whole level to specter that I haven't grokked yet, because I couldn't extrapolate from the toy examples on the wiki page to a real-world use case.