This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-11-16
Channels
- # announcements (11)
- # beginners (184)
- # calva (91)
- # cider (68)
- # cljdoc (42)
- # cljs-dev (44)
- # clojure (228)
- # clojure-dev (1)
- # clojure-europe (3)
- # clojure-italy (4)
- # clojure-losangeles (6)
- # clojure-nl (9)
- # clojure-spec (73)
- # clojure-uk (19)
- # clojurescript (61)
- # core-async (6)
- # cursive (2)
- # datomic (11)
- # fulcro (28)
- # hyperfiddle (16)
- # leiningen (2)
- # luminus (3)
- # off-topic (19)
- # om-next (1)
- # re-frame (2)
- # reagent (12)
- # reitit (4)
- # ring-swagger (5)
- # shadow-cljs (14)
- # slack-help (6)
- # spacemacs (2)
- # tools-deps (40)
- # vim (15)
- # yada (4)
Anyway to have one namespace equal another? Like say I want to move all the code elsewhere, but I dont want to break existing code?
Hello, I am trying to set up Clojure in a Gradle project using the gradle-clojure plugin. But when I try to run the test, it cannot find the Java code class. (Class not found error.) Would anyone have some pointers how to set up the classpath correctly?
what class is it not finding?
Thanks for the reply. I figure it out. The class it wasn't finding was in fact a static inner class so I was in fact importing it incorrectly... :-S
coercions are a runtime thing -- you could spec those function and catch improper calls with instrumentation
@alexmiller suggests not specing to expect sets as it would break existing code
@ghadi good suggestion. we’re exactly going to with speculative: https://github.com/slipset/speculative/issues/70 one open question is if set functions are expected to receive sets or things that are coercible to sets. if we can settle that the result should always be a set, one or more args to these functions must be set, right now.
I had not seen anyone go full on for the approach of "help many Clojurians help collect data on how they use set functions in the wild" before. I suspect that collected data may be used in making a decision here (but don't know that for sure). Cool you are trying to collect such info.
check out the coal-mine step here: https://circleci.com/gh/slipset/speculative/402
Alex Miller has at least made a few statements suggesting that how these are commonly used in the wild might affect how an official spec for these functions would be written.
I’m not going to decline it but agree this could be better written from a problem point of view
@andy.fingerhut We discovered a lot of beginners use merge
as a replacement for conj
, which works in most cases, but I’m not going to spec merge
the same as conj
because of crap code from 4clojure
@borkdude Is there some part of that page that I can click on to see spec failures for calling set functions? Not sure what I am looking for there.
@andy.fingerhut we haven’t got the specs yet, it’s being worked on. but there are questions, like: what is an incorrect call. we’re able to run this on a corpus
Are you considering running the corpus on more than one proposed spec, of varying strictness on the function inputs?
i.e. don't wait for "the correct spec" to be given to you. Try experiments on 5 or 6 different reasonable-looking variants, and see which have what kinds of things that are caught.
@andy.fingerhut we usually start with a spec. we find a problem, and come to the conclusion the spec wasn’t good and change it.
I suspect that some of the specs that do have failures may be reasonable ones for the core team to consider. They might be willing to "break" some existing uses.
They have done so for existing usages of the ns macro, and helped fix them in the wild.
i.e. don't discount a spec as bad because it had some failures. Record them and summarize them, on a JIRA issue.
yeah, we may not permit the usage of union in pprint for example, because that’s an outlier
I'm not demanding that you do this, of course, and sorry if my language makes it sound like I am. These are suggestions for possible way to move forward here.
we have had a use of merge with java.util.HashMap, which works and isn’t that unreasonable
but for crazy shit that just works by luck, we may not. it’s on a case by case basis
Right, just realize that whatever you come up with as your favorite spec, the core team may want to go a different way, so having the data on the failures for other alternatives is probably useful in making that decision.
cool, and thx. You will not be getting any fame or groupies over this, by the way 🙂
OK, if you do, I want to know how you did it 🙂
I have one groupie, and she married me 🙂
I am speaking tongue in cheek when I mention "fame and groupies" and software development in the same sentence. Any specs I may use to impress her are proprietary information 🙂
Yeah, that sounds like good data to collect there, and would love to see what you find. Please let me know when you have more on that.
the problem (as I understand it) is that calls to set functions with invalid inputs do not fail or give feedback and instead often “succeed” with unexpected results
there are a set of alternative options to address that
do nothing
@alexmiller first we have to define: invalid input.
that is another aspect of the problem, yes
Hyrum's law and the desire for backwards compatibility can make the best laid plans of mice and men gang aft agley
With appropriate tribute and apologies to Robert Burns: https://en.wikipedia.org/wiki/To_a_Mouse
actionable tickets have: problem, tables of alternatives and their tradeoffs, performance information if applicable, and of (least important) patches
there’s one call in clojure.pprint that calls set/union with two key-seqs. is it an invalid call (works by luck) or valid.
Sorry for the huge in-line image there. Didn't intend for that, either.
please make a table of questionable known uses
@borkdude Do you mean the one in clojure.data/diff ?
@andy.fingerhut correct
If so, "infamous" I think overstates how well known it is, outside of a very small group of people, most of whom are in this discussion 🙂
and to reiterate, I find this to be like way way down the list of things I consider to be priorities. are people really running into this so often? my suspicion is that we have spent vastly more time talking about it than people have spent debugging it.
If there is not a term or phrase for "annoyance that something is so close to perfect, but not quite", there ought to be 🙂
something related to the princess and the pea maybe
personally I always coerce myself when calling these functions or “type check” manually
@andy.fingerhut probably in German
Ah, there is a phrase for it! There is: http://wiki.c2.com/?AnnoyanceProportionalityPrinciple
Semi-related, this joke I find quite hilarious by Emo Phillips: https://www.theguardian.com/stage/2005/sep/29/comedy.religion
I’m happy to have it closed with a decline, but then I’d suggest to decline the referenced tickets as well.
I’m happy to try to address it (if only so I can never talk about it again :), but would appreciate having everything laid out before-hand
This problem of garbage in -> garbage out is kind of everywhere in Clojure though. I feel if we need to address it, Spec is the way to go. With pre/post and assertions probably coming in second.
or plain asserts, which can be elided with an option. s/assert might also make sense
Like I can choose between a spec that is what it historically allowed you to do. And one that is what you probably think it should do.
does the clj
command-line tool include an option to just install deps for deps.edn
without doing anything?
@U053V4R5N clj -Sforce < /dev/null
@didibus if you want the spec that allows you what it historically allowed you to do, you might as well spec it with any?
Or union over vectors, sure, the name union no longer makes sense, but it does a kind of unordered merge of both vector. And if it does so consistently, then that can be allowed in the relaxed spec
@didibus what’s the point of writing specs that aligns with code that is known to work, but at the same time, code that should optimally be changed
@borkdude The point is that, I expect a lot of people try out a function in their REPL, and they are looking for a behaviour, if it works, they use it, even if there is a mismatch with the common sense you'd expect given the name of the function. But, you still want to catch passing input that would throw, or that doesn't return consistently over the entire domain of the type. Like say it works for small vectors, it can union it, but for big vectors it changes on behaviour. So that is still useful. If I'm trying to add speculative to an old big project, that would be a lot less pain. And on new projects I could move to the strict spec.
@didibus We have exactly the thing for you if you’re running into a speculative spec that doesn’t work for you:
(stest/unstrument `the-fn)
🙂yes, I updated the text. you don’t see this? I’ve had this more often that people’s Slack clients were not reflecting changes..
@didibus we also have some helper macros in speculative which allow you to turn off instrumentation in the scope of a body for one specific spec
Ya. I guess unstrument probably is good enough. Actually, that's why I feel Spec is better then assertions. Because assertions if they broke an existing use case, you can't really say, exclude this assertion.
so I've decided to give rebel-readline a try as a replacement for ultra as my main repl driver... am I right in thinking I'm going to need to spin up cider-nrepl myself from some post-launch code if I want to get the two working together?
@didibus in speculative you could do:
(speculative.test/with-unstrumentation `merge (merge #{} 1))
i don't believe rebel and cider can play nice together @jesse.wertheim
this is from trying to spin up rebel readline in emacs terminal. you can try it there though
neovim runs a great one. My general workflow with nrepl, though, has been to use my terminal emulator in "stacked pane" mode, and then have one pane in that tab for neovim, and another for the repl. Then I just toggle between them depending on whether I want to send stuff from the editor or interact directly with the repl
the one big gotcha with the nvim terminal, is that if the buffer is hidden it can quietly get killed
but there's also the instrumentation that fireplace provides, which is IIRC enhanced by using nrepl over eval
the one thing I like about a regular repl over fireplace is that with a tty repl I have an immutable log of my interactions
(both the terminal hardcopy itself and the repl history file)
the quiet implicit death of ttys inside nvim means I can't really use the feature - I guess I could see how tmux works inside it but...
Like, I'll use :%Eval
or :Eval
from nvim to load/reload stuff, and then call it from the repl to tinker
for the curious: I was able to hack it into working using clojure CLI tools with this quick and dirty setup in my $HOME/.clojure
https://gist.github.com/jaawerth/a8b2ad0e1e9de013b0110c549b1216f9
I wonder if there's a way to refer to env vars from deps.edn
... wasn't able to find anything 😉
I have wanted a requiring-resolve
in Clojure for years -- hurrah for 1.10 Beta 6! 🙂
It turned out we had a variant implementation of this in four different systems at work -- all replaced by requiring-resolve
now and everything tested on Beta 6! i.e., four variant implementations 😐
There were several impls in core already and I’m sure more in the wild...
I have an impl in almost every single project... 🙂
It's in integrant and cider. @seancorfield I'm surprised you have it in application code?
Heh, reasons.
We actually have some data-driven stuff that uses qualified keywords for a variety of lookups and transforms, and then at the end they are converted to symbols and resolved to functions to call.
I agree that in general, "valued oriented programming" is better than "place oriented programming" (overwriting memory adcdresses), and that "functional programming" is better than "imperative programming." One area I am having a lot of problems with is GUI programming. I often have one big state atom, and a particular GUI element (a textbox, a slider, a toggle) might have a "path" or something like [:superman 23 :sword :damage]. This is problematic in that if I ever insert/delete before an item, or move a hshmap around, the "path" changes. It seems that in this case, we want something similar to "place oriented" programming, where for each GUI element, I can have it point to something and say "this is the object you are going to MODIFY". Unfortunately, "MODIFY" is not pure and does not seem to go well with functional programming / purity. The best I can think of so far is somtype of datascript db that we modify in place. How do otherss solve this problem? (PS: this seems to be a brick wall I run into with any gui app of sufficient complexity)
@todo the way that reagent handles this is requiring a metadata :id so it knows how to track persistent objects in a sequence that will be modified
if there is no :id, then every redisplay is a brand new object
yes, ^ is the syntax for adding metadata
The problem I'm referring to is: we interact with some GUI element, we modify the state -- how do we update the global atom?
you are still place based because you are updating a state
you update atoms using swap!
if you treat the contents of the atom as data that you derive UI from, things get simpler
no, it's a single atom
@seancorfield seems to me from the patch that requiring-resolve
is private, so you’re getting it. But not?
Doesn't look private to me
Clojure 1.10.0-beta6
user=> (source requiring-resolve)
(defn requiring-resolve
"Resolves namespace-qualified sym per 'resolve'. If initial resolve
fails, attempts to require sym's namespace and retries."
{:added "1.10"}
[sym]
(if (qualified-symbol? sym)
(or (resolve sym)
(do (-> sym namespace symbol require)
(resolve sym)))
(throw (IllegalArgumentException. (str "Not a qualified symbol: " sym)))))
nil
You’re right. I just read the latest patch in https://dev.clojure.org/jira/browse/CLJ-2432
This means each gui element needs to somehow store "which path in the atom" it modifies.
The storing of 'which path' seems very problematic to me, and seems like it's a hack at getting 'pointers'
why would the ui element modify the global atom for itself?
it might have internal state separate from app state (data about the domain)
@todo this is why re-frame makes you use named keys for subscribing and dispatching of changes
you create an event of :button-clicked
that is then associated with a function that actually knows about the shape of the state atom
@lilactown: in re-frame, is your "full path" a single keyword or a vector of keywords tracing a "pth" from the "root of atom" to "this particular gui element" ?
likewise, it can subscribe to the :toggle
subscription, which is also associated with a function that actually knows about the shape of the state atom
the position in the page of the GUI element shouldn't have any correlation to the position in the app state of the data it touches
I mean, sometimes incidentally it might, but why tie them?
@noisesmith: suppose you have a todo list, the item's location in the vec and it's location in the div are pretty related
(reg-sub :some-query-id
(fn [state _] (:toggled? state))
;; elsewhere in your UI code
@(subscribe [:some-query-id]) ;; => false
@todo I wouldn't make it a 1:1, that seems extremely error prone
you subscribe to the named query, which is a reference to a function that creates the "materialized view" into the state
@lilactown: so the reframe model is: every button fire's off it's on 'msg' , which in this case is some keyword, ... and some other function has the job of doing "keyword + old-state -> new-state" ?
you then write code that associates handler functions to those events and subscriptions which update the state and parse the state into the form that your UI cares about
Doesn't look private to me
Clojure 1.10.0-beta6
user=> (source requiring-resolve)
(defn requiring-resolve
"Resolves namespace-qualified sym per 'resolve'. If initial resolve
fails, attempts to require sym's namespace and retries."
{:added "1.10"}
[sym]
(if (qualified-symbol? sym)
(or (resolve sym)
(do (-> sym namespace symbol require)
(resolve sym)))
(throw (IllegalArgumentException. (str "Not a qualified symbol: " sym)))))
nil
honestly the re-frame docs probably explain this better than I can: https://github.com/Day8/re-frame
@lilactown: I tried re-frame before, but preferred reagent. After all this dispatch system -- how does re-frame store data?
The problem I have is: clojure/functional programming seems to encourage deeply nested structs. For manipulating state, I think modifying a sql table is easier than modifying a deeply nested structure.
it solves the problem you're talking about - you have a single state atom (called the "db"), and you want to separate your UI from the structure of your app's db
This whole notion of keeping a pointer/ref to some part of a deeply nested structure seems very troublesome, and it seems like raw pointers or a sql table via 'id' is easier.
why would you want nested state here, I don't get that - you have a widget tree, if two widgets need to share state they can refer to a separate and flatter shared data location
why should the widget care where it is in the parent tree?
Does re-frame have cursors like reagent? That was the way to deal with nested state.
@lilactown: doe re-frame structure the "single atom db" ?
Jumping in here as a total reframe n00b, but the whole concept of subscriptions is bothering me.
it is it's primary attribute I would say. all of it's machinery is in support of your state being stored in a single global atom
how does 'cursor' deal with the problem of 'my parent just moved' or 'some sibling got inserted before me in the vec' ?
Simply because my components have gone from being pure fns to something that receives data through other ways than its arguments.
@slipset you can have as many or as little components that actually subscribe as you want
@lilactown: I googled https://github.com/reagent-project/reagent-cursor after @seancorfield mentioned cursors
I don't see how these cursors can stay valid if it's parent moves or if some sibling gets inserted before the cursor, thus changing it's 'pathn'
are you talking about UI parent/sibling or a change to the structure of your application state?
then ,suppose we insert an item into (:foo atom) before 222, ... now the 222 points at what was originaly item 223
I'm saying: when we modify the global atom, the cursors can become invalid / point at wrong objects.
@lilactown sure, but it’s more about the concept. And as such I’m not overly enthused about the whole automagically-deref atom functionality of reagent either.
so you have something like this:
{:foo [ ... {:bar [1 2 3 ... 34]} ... ] }
where :foo
is a collection that is being updated dynamicallythis seems very contrived. you would simply not create a cursor that refers to a specific element in the collection :foo
Right, cursor is assumed to be a path through nested maps (only), not vectors etc.
(and I'm pretty sure cursors got merged into reagent core itself -- not sure why they didn't remove the separate repo?)
Yeah, as a standalone lib. I'm sure it was merged in (partly because I wrote it and we had lots of discussions about whether it was a good addition to core reagent or not! 🙂 )
okay, so the reagent solution of cursors is: cursor = path, path = vec of keywords, so all ancestors = maps, no vecs allowed
so if you were to implement a todo list and have cursors, how do you get a cursor to a single todo item, or do you not ?
If you're changing an element of a map in a vector, you have to rebuild the vector anyway so you only need a cursor to the containing component.
@todo I keep thinking you're conflating layout structure and domain data structure, these should really be distinct as I understand it
(in other words, you need to change the value for the whole :todo-list
, in order to change the boolean flag inside a map in one of its elements)
@noisesmith: I don't agree with what I think you think I am thinking of. For any app, we need some central storage of state. Then, GUI elements need to end up manipulating this state. This state, in my current work, is often very compositional and thus deeply nested / hierarchical.
you could have something like this (re-frame):
(reg-sub :todos
(fn [db _]
(:todos db))
(defn todo-item [todo]
[:div (prn-str todo)]))
(defn app []
(let [todos (subscribe [:todos])]
(fn []
(for [todo @todos]
[todo-item todo]))))
And we don't care that all the cursors have been invalidated because if we have to rebuild the items, we can rebuild the cursors as well?
but if the child components did contain subscriptions as well, yes those would be re-created
I am confused, so say we are editing an item in the todo list, and that we wanted to update the global atom on every keystroke (hypothetically), how would we do this?
I think you'd want to make your global atom state a map of maps of maps. And when you need to represent order, like a list of todo items. You'd have your individual todo items be a map also, so you have a vector of maps. Now to find the element, you need to perform a quick search over it.
{:id :todo-items
:value [{:id item1
:value "Call sarah."}
{:id item2
:value "Buy milk."}]}
Another way to do it is that your items are also maps, but they have an order entry.
{:todo-items
{:item1 {:value "Call sarah."
:order 0}
{:item2 {:value "Buy milk."
:order 1}}
And now your component just needs to make sure to render each item based on their order entry.I'd probably do something like this
(reg-sub :todos
(fn [db _]
(:todos db))
(reg-event-db :update-title
(fn [db [_ todo-num new-val]
(update-in db [:todos todo-num :title] new-val])))
(defn todo-item [todo-num todo]
[:div [:div "Title: " (:title todo)]
[:input {:value (:title todo)
:on-change #(dispatch [:update-todo todo-num (.. % -target -value)])]])
(defn app []
(let [todos (subscribe [:todos])]
(fn []
(for [[todo-num todo] (map-indexed vector @todos)]
[todo-item todo-num todo]))))
It turned out we had a variant implementation of this in four different systems at work -- all replaced by requiring-resolve
now and everything tested on Beta 6! i.e., four variant implementations 😐