This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-10-04
Channels
- # aleph (23)
- # announcements (1)
- # babashka (21)
- # beginners (70)
- # biff (3)
- # cider (8)
- # clj-kondo (45)
- # clj-yaml (9)
- # clojure (69)
- # clojure-europe (82)
- # clojure-nl (1)
- # clojure-norway (2)
- # clojurescript (34)
- # conjure (19)
- # core-typed (6)
- # cursive (2)
- # events (5)
- # fulcro (55)
- # honeysql (1)
- # integrant (18)
- # jobs (1)
- # lsp (124)
- # malli (10)
- # meander (1)
- # off-topic (26)
- # polylith (8)
- # reagent (7)
- # releases (1)
- # remote-jobs (1)
- # sci (2)
- # shadow-cljs (19)
- # squint (5)
- # vim (17)
- # xtdb (31)
Meta-data about the return-type of a function doesn't seem to be applied to objects that capture the result of this function. Is this expected?
(def ^Int x 1)
(meta #'x) ; Contains :tag Int in meta-data
(defn my-int ^Int [] 1)
(def y (my-int))
(meta #'y) ; Doesn't contain :tag Int in meta-data
This is just to illustrate the problem. In reality I'm trying to add a ^js typehint to objects that capture the result of a function, but it doesn't seem to work...
So this is an Cljs project, don't know if that makes any difference.This is to be expected. The type hint metadata is meant as a hint from the programmer to the compiler, not the other way around. Often times the compiler can still infer types, but it will not expose this information as metadata (to my knowledge).
For example, in this snippet, the compiler will know that f
is a java.io.File
, and can generate a call to .exists
without reflection:
(let [f (io/file "/")]
(.exists f))
But this will evaluate to nil
:
(let [f (io/file "/")]
(meta f)) ;; => nil
Hmmm, I see.
I'm not sure if your example proves the point as this was always gonna return nil
according to https://stackoverflow.com/questions/30082585/type-hint-stored-as-metadata-in-clojure. But I think you're right none-the-less as I can't find the type-hint in the meta-data when I do extract it properly.
It's not clear to me what this means for my situation though. I'm adding ^js typehints to my code to avoid problems with advanced-compilation with shadow-cljs as explained in https://shadow-cljs.github.io/docs/UsersGuide.html#infer-externs. Would adding the ^js typehint for the return value of the function be enough to avoid this issue. I guess it's save to assume it works if there are no more warnings in the compiler output...?
Maybe I should have posted this in the shadow-cljs channel, but I got confused with meta-data in general because of this issue.
> I guess it's save to assume it works if there are no more warnings in the compiler output...? I'd say so, too. In general, you should use type hints sparingly, and only need them when working with JavaScript code directly. If you do a lot of interop, consider writing a few "wrapper" functions that interop with JavaScript and use type hints. Functions calling these and therefore staying "in ClojureScript land" do not need typehints.
I have a question about laziness and for comprehensions. For a small example say I have something like:
(first
(for [i (range 1000)
:when (even? i)]
i))
where I basically want the first match on a predicate. I know this particular example is silly and you could use some
or other better ways to do things like this, but say you wanted a for
comprehension for other reasons so you have to do it this way. The way I see it, this is inefficient because the for
comprehension will go through the entire range and then you just take the first. But with chunking, where lazy seqs realize the first 32 elements (if I'm describing that correctly) does that mean this will stop and return the first
element after only 32 calculations?The reason I was using for
is because there were some other intermediate steps and such so the for
makes it more clear what is going on than when I was trying to get it all done with some
.
also you could use :while
instead of :when
. That should perform better as well.
for
is also lazy
if you care about the extent of lazy evaluation/chunking, you should not use lazy evaluation - use loop
or reduce
/`reduced` etc
I was discussing it with someone else though and they were thinking that maybe that for
would stop evaluating after the first 32 calculations and just take first
from that initial chunk.
it would because it's lazy, but the exact details depend on what sequences are being used at the bottom and which sequence operations are being used under/over the for
(also note that long range
s are highly optimized and relatively unlike other sequences internally)
in 1.12 ranges will actually create 1 giant "chunk" of all values (but it doesn't actually compute them until needed). but again, if you care, don't use sequences
Good stuff, thanks for the fast replies! I sort of get the part about the "if you care, don't use sequences" but what trips me up is maybe doing it this way makes the code much clearer so if you aren't going to pay a "laziness price" because it will short circuit then it would be cool to be able to do so. But it is hard for me to wrap my head around when exactly you are paying that price, which is probably the lesson to be learned
Lessons like this make me read the docstrings much more than the code. I do that way more often now. Quite enlightening!
When in doubt, transducers? 😉
In javascript, I would use map
to create a new array “all at the same time”.
Where as, if I wanted to loop sequentially (for async ops) I would use for of
In Clojure, does map work the same way? Would it present the entire list of new values in one go?
My goal is to figure out the best way to:
1. Read a csv
2. create a new domain based on the csv row
3. Use the new domain as part of a HTTP request
4. return the data for domain 1
5. repeat operation for next item.
I am currently stuck on step 3. I know how to send a HTTP request but im not sure how to do it for an entire list of data inside a csv.
Here is my code so far:
;;domain - root url for api domain
(def create-api-domain (fn [domain-name]
(str domain (first domain-name) path)))
(with-open [file (io/reader "src/frosty/domains.csv")]
(doall
(map create-api-domain (csv/read-csv file))))
And you want this to happen asynchronously?
so each request fires asynchronously?
Can you show us how you're fetching a single request?
In my JS app (which I am recreating) each answer comes back first and is saved in a file. Then the next one fires
That is synchronous
Ahh sorry yes I meant sync
Ok, that's not hard to do here...all you have to do is write a function that accepts a domain, fires the request and returns the data from that call
and you map over the whole thing
I see, so would that function replace my current create-api-domain
and I just do the whole thing in that function?
Like this:
(map (comp fetch create-api-domain) (csv/read-csv file))
where fetch
is simply a function you write to fetch the data from the url returned by create-api-domain
Can you show me how you are fetching a single request?
Sure, ill post it here when i get back to my comp
One thing to consider is that map is a lazy operation, so you must get sure that you realize the result list, or better use doseq if you don't need the results.
I have a variable k, , (def k [[:a :b] {:address "England" :city "London"} [:a :b] {:address "NY" :city "abcd"} [:a :b] {:address "Canada" :city "Toranto"} ])
when I run (println (apply array-map k)) result is {[:a :b] {:address Canada, :city Toranto}}
how to get the resullt something like
{[:a :b] [{:address "England" :city "London"} [:a :b] {:address "NY" :city "abcd"} [:a :b] {:address "Canada" :city "Toranto"}]}
Bit hard to read your formatting. But you want a map that has the key [:a :b]
three times, each with a different value?
maps can only hold a single value to a distinct key. it if the map m
was as you wanted here, what would (get m [:a :b])
return?
since there are three distinct values you want associated with that. How would you say which of the three you wanted
all those threee values will be in the vector {[:a :b] [{:address "England" :city "London"} [:a :b] {:address "NY" :city "abcd"} [:a :b] {:address "Canada" :city "Toranto"}]}
so you want a map that has [:a :b]
as a key once and its value is a vector of those three maps [{:address "England" …} {:address "NY" …} {:address "Canada" …}]
(group-by first (partition 2 k))
should do itKind of. Your grouped values will still contain the key
(let [k [[:a :b] {:address "England" :city "London"}
[:a :b] {:address "NY" :city "abcd"}
[:a :b] {:address "Canada" :city "Toranto"}]]
(reduce (fn [m [k v]] (update m k (fnil conj []) v))
{}
(partition 2 k)))
{[:a :b] [{:address "England", :city "London"}
{:address "NY", :city "abcd"}
{:address "Canada", :city "Toranto"}]}
if you want to use group-by, but don't want certain things on the values-seq, you can use (update-vals)
after. (-> (update-vals (partial map second)))
in the case of craftybones example.
@U0LAJQLQ1, I have a question here , why do we need partial here? this question will help me to understand more about partial here
@U01J3DB39R6 partial
creates a partial function where some of the arguments are supplied initially and the remaining arguments are supplied at call time. For instance
(def add-two (partial + 2))
(add-two 3) ;; 5
update-vals
accepts a function that takes an old value for a given key and returns the updated value for that. This is done for every key in the map. For instance
(def nums {:a 1 :b 2})
(update-vals nums inc) ;; {:a 2 :b 3}
our group-by
function returneda map whose values were partitioned lists as follows:
{[:a :b] ([[:a :b] {:address "NY"}] [[:a :b] {:address "Canada"}])}
We want to take the map above and turn it into
{[:a :b] ({:address "NY"} {:address "Canada"})}
Honestly, I recommend you try each of these functions in isolation and understand them thoroughly before applying them on your example
@U8VE0UBBR Thanks for the detailed reply, my question was why (partial map....)
Is there any case where a list could contain the function defined here as foo
, but then comparing to the symbol foo
inside the function would fail to filter it out?
(Maybe is there a way for the function to refer to itself that does not rely on a var?)
(defn foo [m]
(let [c (filter (fn [x] (not= foo x)) m)]
c))
(foo [foo :bar])
the symbols used there are identifiers in code that the compiler uses when compiling, but don't exist at runtime
Cool. So what is the "depends" part?
I tried calling (my-ns/foo [my-ns/foo :bar])
in another namespace, and that worked fine, and even after indirection of a new name (def fee my-ns/foo) (fee [fee :bar])
. So I think I am okay?
Just somehow feels like it might be brittle, and I don't know enough about internals to know if that fear is real.
function identity is by reference, and functions are closures, so it is possible to construct multiple instances of "the same function" (for some value of sameness), and they won't compare as equal
Ok. You mean something like this, right?
(defn make-foo [] (fn foo []))
(= (make-foo) (make-foo)) ;; false
That is not an issue here. Only created once. a var is also a mutable reference, and you have two references to it (the two foos, one as the function to call and one as part of the argument), so technically that is a race condition, the value of the var could change between when each reference gets it
I don't intend to mutate it. So expect I'm probably fine. But I would much rather a bulletproof alternative.
It depends on what you are doing, if you look at something like add-watch, you see it makes you supply a key that becomes the id of your function
I have a recursive process that repeatedly steps through a list of computations, this function being one of them. That list is defined by the user, with this function being probably included. Whenever this function is called it needs to start a new loop, but with itself not in the list. I don't need to be able to change the function, I just want to be sure that when it tries to removes itself, it actually does. In the happy path, user just makes a list, which includes this fn, and starts the loop. Maybe I am just overthinking this?
Perhaps. Also, consider remove
(defn foo [m] (remove #{foo} m))
is all you needI know I’m late to the party, but I’d like to add something to @U0NCTKEV8’s comment that a var is a mutable reference. Yes, they’re mutable. That said, it’s a serious code smell if you’re mutating vars. They’re typically created with the expectation of staying unchanged. It’s useful to change them for testing, instrumentation, etc, but it’s typically not a good idea in standard development.