This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-10-29
Channels
- # 100-days-of-code (2)
- # bangalore-clj (1)
- # beginners (141)
- # cider (33)
- # cljs-dev (13)
- # cljsjs (7)
- # cljsrn (1)
- # clojure (88)
- # clojure-conj (3)
- # clojure-dev (24)
- # clojure-italy (11)
- # clojure-nl (4)
- # clojure-russia (1)
- # clojure-sanfrancisco (1)
- # clojure-spec (4)
- # clojure-uk (53)
- # clojurescript (65)
- # core-logic (2)
- # cursive (28)
- # datomic (33)
- # duct (2)
- # emacs (3)
- # figwheel-main (9)
- # fulcro (44)
- # hoplon (6)
- # leiningen (144)
- # mount (1)
- # nrepl (21)
- # off-topic (102)
- # onyx (2)
- # other-languages (5)
- # pathom (6)
- # planck (3)
- # portkey (1)
- # re-frame (7)
- # reagent (5)
- # reitit (17)
- # shadow-cljs (24)
- # spacemacs (16)
- # tools-deps (64)
- # uncomplicate (2)
- # vim (22)
Less code focused question: So I’m working through brave and true but I’m currently stuck on Exercise #7.2 requesting I make an infix macro that obeys the order of operations (infix ‘(1 + 3 * 4 - 5))
. On one hand I get that I need to recursively pair off the priority calculations into lists of 3 then nest them so they properly evaluate to 8. On the other hand I’m not entirely sure how to implement it. Do I struggle with it until I come up with a solution no matter how long it takes or do I find an existing solution and make sure I understand how to solve it? I’m interested in what’s the recommended path for learning in situations like this.
that depends on you. i usually try to solve it myself but once it gets frustrating i just find a solution and dissect it until i understand it
Another intermediate possibility: after thinking about it for a while and not yet coming up with a solution, you can ask for a hint that hopefully doesn't give away the whole answer, which of course might vary in quality depending upon who answers.
Pointless question: Is there anything like as->
but which functions either like ->
or ->>
when the binding for a form isn't specified?
@mfiano Not quite sure what you're asking there... as->
without a binding is pretty much ->
by definition isn't it? What am I missing?
With the following, form2
doesn't specify the binding. I would like something that either behaves like thread-first or thread-last in this case. It doesn't matter which, as long as it is defined behavior.
(as-> foo m
(form1 x m y)
(form2 x y))
The answer seems to be the swiss-arrows
library. As per the documentation:
If no <>
position marker is found in a form within the Diamond Wand -<>
, the default positioning behavior follows that of the ->
macro. Likewise, if no position is specified in a form within the Diamond Spear -<>>
, the default is has the positioning semantics of ->>
That sounds unnecessarily magic and a bad idea.
For a start, as->
is really only meant to be used inside a ->
pipeline.
So you'd have
(-> foo
(as-> m
(form1 x m y))
(form2 x y))
And what if I have a ->>
pipeline with 1 or more forms requiring threading through the first argument?
`(-> foo
Start with ->
and then you can thread into ->>
@polkafreaku It does indeed error for me. What version are you using?
(* [])
=> (cast Number [])
Ah, cljs
It probably doesn't do the cast
See what (source *)
shows you.
(assuming lumo
works like a Clojure REPL, which I don't know)
cljs.user=> (source *)
Evaluating (source *)
lumo.repl.source_STAR_.call(null,new cljs.core.Symbol(null,"*","*",(345799209),null))
(defn ^number *
"Returns the product of nums. (*) returns 1."
([] 1)
([x] x)
([x y] (cljs.core/* x y))
([x y & more] (reduce * (cljs.core/* x y) more)))
nil
I don't think cljs supports cast
-- because of JS.
Not a bug, just a quirk of the platform.
But you are likely to get answers from people with much more intimate knowledge of ClojureScript on the #clojurescript channel, FYI. (No disrespect to Sean)
when it gets down into the nitty gritty details of error cases like this, I am pretty sure there are lots of tiny differences.
It's also an underlying principle of Clojure (and ClojureScript) that, in general, bad inputs are not checked for -- for performance reasons. This is sometimes called "Garbage In, Garbage Out" 🙂
I'm kinda surprised the Clojure version of *
actually casts the argument... The cljs source is what I actually expected the clj version to be
(yup, I claim no expertise with cljs -- and I'm assuming it doesn't have cast
hence the difference here)
Probably was not considered much of a performance issue for Clojure/Java to do the cast for the 1-arg case, given how unusual the 1-arg case occurs in most code.
But yeah, I was also a little surprised to see the cast there in Clojure/Java.
Am I right that cljs doesn't support cast
BTW?
(I've almost never seen it used in Clojure so I was a bit surprised it was even a thing there!)
Th "garbage in, garbage out" philosophy often leads to "accidental errors" as Eric Normand puts it, but it will be much more tolerable once core functions are fully spec
'd from what I gathered.
And people have done their own personally spec'd versions for core functions to implement the arg checking they prefer, just nothing official or widely used that I know about there.
Yes, I actually stumbled upon https://github.com/slipset/speculative today.
I've released a library that doesn't use spec, but function preconditions, to throw exceptions if you call a clojure.set function with non-set args (or whatever type is appropriate for the function), which is significantly faster than spec instrument, as the README shows measurements for. Either people don't know about it, or know about it but don't care to turn on the extra checking, or they are doing it themselves if they want it without the lib: https://github.com/jafingerhut/funjible
I played with spec quite a bit when I was learning Clojure, but I haven't made use of it yet for a serious project. I'm actually writing my second serious project right now, and the API is very unstable still. It's turning out to be quite non-trivial, so hopefully it won't be too difficult to spec once I flesh things out completely.
I don't use Slack much, but is it intended that I'm able to edit this channel's topic when I click on it?
Yeah, because it's designed for teams -- expected to cooperate -- not for giant communities like this 🙂
Ha fair enough.
As an Admin, I sure wish there were more permission controls on many aspects of Slack but, hey... we get what we pay for 🙂
There's always other services like Matrix these days, but migration is the issue of course.
There's #community-development for those discussions 🙂
(and, yes, there are lots of other Clojure communities online, but they're all pretty small by comparison)
is it a requirement to use clojure.zip? otherwise I highly recommend Specter for navigating recursive data structures
No, not requirements. I just want to understand where can I use zippers, what is the limits. I check Specter now, thanks!
this example might be helpful! https://github.com/nathanmarz/specter/wiki/Using-Specter-Recursively#a-basic-example
hello
Hello people, I have a question on best practices + pure functions..
Imagine I have a big function f
.
Now I start to refactor and separate it in a bunch of smaller functions. Let's say now I have 5 smaller functions.
f
calls f1
, that calls f2
, and so on... Imagine f4
needs to use data (variable) from f
function.
Whats is the best practice to f4
use the data? Do I need to pass the data from f
to f1
to f2
to f3
to f4
?
Or there any other solution to this? I'm trying to make all my function pure...
@leandrotk100 Is there any part of the big function f
that is impure?
I would probably rewrite f
to use a thread macro to call and redirect the imputs from all de other fn
Doesn’t seem like it’s a question of the purity of function, but more like how should you structure your code. Break out the individual pieces you want to test, then compose them to make the bigger f. Using comp or threading like orbaruk suggests is probably the best way to go.
@christian.gonzalez There are any benefits on using comp
instead of threading
and vice-versa?
I find threading to be cleaner and easier to read @leandrotk100. I’d say it’s a more data oriented approach whereas comp is a more functional approach.
Also with comp you end up needing to use partial quite a bit
@leandrotk100 it's also worth knowing that ->
and friends are macros and thus operate at read time rather than eval time.
when you say "needs to use data (variable) from f
function" does that mean there's a part of the original f
not covered by any of the numbered ones?
maybe a gist would clear up some ambiguities and allow more help? seems pretty vague at this time
yeah it sounds like the first function has some value needed in the fourth and not any intermediate ones. This definitely happens from time to time in composed functions or transducer pipelines and you pretty much have to either pass it on through each or break down the immaculate pipeline and partially apply whatever it is to f4
. there are worse things in the world than not having a pretty (-> data f1 f2 f3 f4)
😉
One thing I do sometimes is to package the function args up into a map and just thread the map through.
(defn f1 [{:keys [everybody-needs-me f1-needs-me] :as opts}]
;; do something with those args
(f2 (assoc opts :f2-needs-me :some-val)))
(defn f2 [{:keys [everybody-needs-me f2-needs-me] :as opts}]
;; do something with those args
(f3 (dissoc opts :f1-needs-me :f2-needs-me)))
(defn f3 [{:keys [everybody-needs-me] :as opts}]
;; etc
(f4 opts))
(defn f4 [{:keys [everybody-needs-me f4-needs-me] :as opts}]
;; etc
)
(f1 {:everybody-needs-me 1
:f1-needs-me 2
:f3-needs-me 3
:f4-needs-me 4})
eh, those functions need to be defined in the reverse order, but you get the idea.
something like https://gist.github.com/hiredman/71b71e4e07e7666fe1b9def9a476c765 (minus all the mapcat stuff) might be useful for refactoring that kind of thing, you would say what data each function depends on and let something else determine what order they need to be called in
Question about cacheing: Is there anyway to cache a function that receives a map and have it ignore a certain key in it?
you could compose select-keys
with a cached function
user=> (def frob (memoize (fn [x] (println x) (vals x))))
#'user/frob
user=> (def f (comp frob #(select-keys % [:a :b :c])))
#'user/f
user=> (f {:a 0 :b 1 :c 3 :uuid (java.util.UUID/randomUUID)})
{:a 0, :b 1, :c 3}
(0 1 3)
user=> (f {:a 0 :b 1 :c 3 :uuid (java.util.UUID/randomUUID)})
(0 1 3)
user=>
if the cached function doesn't look at a key, just remove it before the function call
I have this function:
(defn- parse-page-segments
[file]
(let [segment-count (types/read :uint file)
segment-len (->> (repeatedly #(types/read :uint file))
(take segment-count))]
(into {} (map-indexed vector segment-len))))
types/read
is reading a byte from a file stream each time it is called. Now everything works if I leave off the into
there, but with it it causes the pipeline to be executed multiple times if segment-count
is > 1. I'm completely baffled as to why this only occurs by converting the sequence of vectors into a map.@noisesmith Thank you!!
glad I could help
@mfiano which part of this do you call "the pipeline"
Thread last form
so the number of times types/read is called is some multiple of segment-count?
the big difference is that into as used there makes the call eager instead of lazy
if you wrap the map-indexed
call in doall
, does this make it behave like the into version did?
I get garbage data due to too many byte reads when i use into like above, but without it works as expected
what about with doall?
I can try that in a few. On mobile right now
@noisesmith Hmm what if the function does need to look at the key?
but how would you correctly memoize it?
you could decompose the logic inside the function, and memoize the part that doesn't use that key, but there's no way to memoize something as if it didn't look at a key, and also use the key...
@noisesmith Yes. Using doall
in place of into
causes the same thing with garbage data.
so the real problem is that the number provided to take is wrong I bet
That does not seem to be the case. Without into
I get the same values as my existing parser in Common Lisp.
but all doall and into are doing differently is changing when a value is consumed
perhaps there's a race condition?
This is not threaded, and both doall and into have the problem.
what they have in common is making a value non lazy
which means changing the timing of realizing results
there's literally nothing else doall does - that's its only job
you're certain there's no thread context switching in eg. the underlying IO?
I'm not certain. I'm just using
on a path string.
unless there's another very weird thing going on that you haven't described yet, the only reason that doall
would change the correctness of a result is a hidden thread synchronization issue
Let me try to produce an isolated test
the common thing is someone opening a input source in some lexical context, and getting a closed input error, that goes away when using doall
so you could have the reverse - doall reading "extra" results, and in the other case the source is closed getting the results expected
Could the take
call be doing 1 extra evaluation of its input than the minimum required? That occurs with some sequence functions in some situations, I believe. Laziness and side effects, where you want to guarantee absolutely minimum evaluation, are something I wouldn't recommend, because of these behaviors.
this could be a case for using eduction with the take
transducer
agreed that laziness and IO are an iffy combo
Personally, I would reach for loop
with reading/writing side effects inside of it, if you want to control how many of them occur, and when.
IIRC, eduction was designed for cases where you want nice functional things like take
, map
, filter
, in a side-effecting context
(but yes, loop would work here)
correcting myself above: eduction is meant for contexts where transducers aren't currently available, which can include IO, but also includes composing with various other functions that don't integrate with transducers
Well I surely cannot reproduce it in an isolated test:
(defn test1
[path]
(let [stream ( path)
bytes (->> (repeatedly #(.read stream))
(take 10))]
(map-indexed vector bytes)))
(defn test2
[path]
(let [stream ( path)
bytes (->> (repeatedly #(.read stream))
(take 10))]
(into [] (map-indexed vector bytes))))
;;;
user> (test1 "/tmp/test.bin")
;; => ([0 79] [1 103] [2 103] [3 83] [4 0] [5 2] [6 0] [7 0] [8 0] [9 0])
user> (test2 "/tmp/test.bin")
;; => [[0 79] [1 103] [2 103] [3 83] [4 0] [5 2] [6 0] [7 0] [8 0] [9 0]]
You can choose to fight the battles you want -- in Clojure, I personally would avoid the laziness+side effects debug battle.
Mixing lazy seqs with io is often problematic, I wouldn't be surprised if the doc string for repeatedly doesn't specify the function should be side effect free
also, following repeatedly by take is silly because repeatedly takes an explicit "limit" arg
repeatedly
is intended for side-effecting functions per the doc string "Takes a function of no args, presumably with side effects, ..."
it explicitly mentions "presumably with side effects" though
Jinx 🙂
@mfiano and I had a brief DM about this earlier and my feeling is this is due to something higher up his call chain -- not in parse-page-segments
. Which is why the isolated test works (and matches what I'd been trying to test in the REPL).
it could be in the ByteBuffer usage, since that involves mutations that wouldn't be thread safe - but I
only mention that because it's the other thing I saw mentioned about this code
it could easily be anything else
Why I say what I said above -- there have been N tickets filed against Clojure/Java on "function X evaluates lazy sequences once/twice/M times more than the minimum it could", where N is somewhere in the range 10 to 15, I think (over the last 10 years or so). Many have led to changes in Clojure, likely some have not and the behavior remains.
There is no solid foundation other than examining each case one by one to base "this code will not evaluate any more than the minimum necessary of a lazy sequence"
Stated another way: "If you are a masochist, I think you will enjoy mixing laziness and side effects in Clojure."
The problem was an off by one error in my byte reading that caused the side-effecting lazy sequence to read bad data and thus error from my assertion. I do agree that side-effecting lazy sequences should generally be avoided, but I think it is okay in this case since the sequence is only lazy internal to a function where it's immediately realized and thus predictable. Thanks for all the help with this. It was a red herring. Problem solved.
When I'm using the httpkit client to make a get in a list of addresses using for in a list, after making a try+ , catch
It's giving me back the requests that I make not the response
It's because for lazyness?
@d.ian.b Perhaps show us some code?
(defn list-user-channels [service-sid user-id]
(vec (let [request (:channels
(j/read-json
(:body (when not-empty
(try+ @(c/get (format "" service-sid user-id)
{:basic-auth [account-sid api-auth]
:throw-entire-message? true})
(catch [:status 401] {:keys [request-time headers response]}
(log/fatal request-time headers response))
(catch [:status 200] {:keys [request-time headers response]}
(log/info request-time headers response)))))))] (for [channel request] (:channel_sid channel)))))
after I get the vec I'll interate onto these values to make a request in another api to each value
(defn list-user-channels [service-sid user-id]
(vec (let [request (:channels
(j/read-json
(:body (when not-empty
(try+ @(c/get (format "" service-sid user-id)
{:basic-auth [account-sid api-auth]
:throw-entire-message? true})
(catch [:status 401] {:keys [request-time headers response]}
(log/fatal request-time headers response))
(catch [:status 200] {:keys [request-time headers response]}
(log/info request-time headers response)))))))] (for [channel request] (:channel_sid channel)))))
after I get the vec I'll interate onto these values to make a request in another api to each value