This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-01-28
Channels
- # babashka (28)
- # beginners (252)
- # bristol-clojurians (2)
- # calva (28)
- # cider (11)
- # clj-kondo (15)
- # cljs-dev (7)
- # clojure (378)
- # clojure-europe (4)
- # clojure-italy (4)
- # clojure-nl (3)
- # clojure-norway (4)
- # clojure-uk (32)
- # clojurescript (128)
- # cursive (39)
- # data-science (18)
- # docker (37)
- # figwheel-main (10)
- # fulcro (45)
- # ghostwheel (7)
- # graalvm (2)
- # hugsql (1)
- # jobs (2)
- # joker (5)
- # kaocha (5)
- # luminus (12)
- # off-topic (37)
- # onyx (4)
- # pathom (22)
- # pedestal (70)
- # re-frame (7)
- # reagent (30)
- # ring (4)
- # shadow-cljs (12)
- # spacemacs (1)
- # sql (26)
- # tools-deps (7)
- # vrac (2)
- # vscode (7)
- # xtdb (27)
I’m working with 150MB file, so I used powershell to pipe the first 10 lines to a test file, and this changed formats. I’ll report if I can explain what’s going on their. Got the wiki articles up now, thanks for your thoughtful response!
Yeah, not surprising if inserting another tool in the middle, especially one that does its own independent guessing/interpretation of input file format, and choice of encoding when writing an output file, causes different results than when the intermediate step is removed.
You can wish/hope that such intermediate tools preserve the text encoding in all cases, but I do not think that is the case, and some are even designed to help you convert between text encodings.
Yeah, I just didn’t realize powershell changed encoding while reading some lines and piping out. Hind-sight I guess I could have assumed this. Otherwise how would powershell know what a line is.
@seancorfield Could not find artifact org.ode4j:core-cpp:jar:pom:0.4.0 in central (
; sadly, this is what drove me to extension
in the first place, and so i'll let the thread drop.
Hello all, how do I interop with this?
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
});
you need to figure out what class those static methods are on first
I saw something by using reify
but no interface to implement. Is it not like anon function:
(Foo/glfwSetKeyCallback window (reify SomeClass (someMethod [window key scancode action mods] (if ... (Bar/glfwSetWIndowSHouldClose window true)))))
it's an anonymous inner class, the method sig is used to figure out what it implements
I tried
(fn [window key scancode action mods]
(when (and (= action GLFW/GLFW_RELEASE) (= key GLFW/GLFW_KEY_ESCAPE))
(.glfwSetWindowShouldClose window true)))
but no waya function won't work, you need to implement a specific class or interface
that syntax is a java 8 lambda https://www.geeksforgeeks.org/lambda-expressions-java-8/
java lambdas are a lot like clojure functions, but they were implemented after clojure, so clojure's functions are not compatible
that looks right
you need to reify that, and put the code in its method
the GLFWKeyCallbackI interface
I think it's apply https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html
but really you need to find the doc or implementation for that class (if you use an ide it will look that stuff up, and most java ecosystem assume you are using a tool that does that stuff for you since it's so tedious)
you got multiple levels of things going on here a C library wrapped by some Java interfaces/classes,
second one is a Java lambda, which is essentially a syntax sugar for an anonymous implementation of some interface
if you look at the javadoc
of glfwSetKeyCallback
, the second argument will be some interface
oh yeah I got lambda / Function conflated, and the method you want really isn't apply
yeah Function doesn't have anything to do with this, except that Function is one of many SAM interfaces
yeah - you really need to find the classes these static methods are on, and their docs
thanks
maybe, yeah
right
I think @fabrao was confirming what you said already
(GLFW/glfwSetKeyCallback window (reify GLFWKeyCallbackI
(apply [this window key scancode action mods]
(when (and (= action GLFW/GLFW_RELEASE) (= key GLFW/GLFW_KEY_ESCAPE))
(.glfwSetWindowShouldClose window true)))))
Caused by java.lang.IllegalArgumentException
Can't define method not in interfaces: apply
quick question why does the libspec in require need to be quoted
(require '[compojure.core :as c])
-> ok
(require [compojure.core :as c])
-> syntax error
@fabrao https://github.com/LWJGL/lwjgl3-generated/blob/master/glfw/src/generated/java/org/lwjgl/glfw/GLFWKeyCallbackI.java#L55 is that the method?
@jakub696 require isn't a macro, and those things don't exist until you load them, so quoting allows you to use a data structure of symbols to ask for them
hm but it's just a vector - why does it get evaled - and why is the same syntax fine when used in ns?
everything in clojure is evaluated, unless a macro or quote prevents evaluation
ns is a macro, and knows the args to require need to be quoted, so does that for you
some things evaluate to themselves - :a
, "foo"
, 1
and []
, {}
, #{}
but symbols and lists cause further simplifications - symbols are "looked up" to find a substitute value with that name, and lists execute their first item with the rest as args
right
so the result of evaluating (vector 1 2 3)
is to resolve vector, finding a function in clojure.core, and then calling it with the other args
this results in the same thing you'd get with the literal input of [1 2 3]
glad I could help
got it and since in my case the symbol compojure.core did not exist - exception was thrown
it's actually refreshing how simple and consistent this is - almost always if you see symbols you know they get looked up from innermost context outward, and if you see parens you know something is executing
if so, it will be (reify GLFWKeyCallbackI (invoke [this window key scancode action mods]....))
The double-colon syntax is used for namespace aliased keywords, so the error is telling you that there's no subs
alias in the current namespace
(keyword ":subs" tag)
constructs an "invalid" keyword, or at least one that can't be entered as a literal in the way you expect
It's really confusing, because when I pass the literal ::subs/tag1
it works.
the result of (keyword ":subs" "tag1")
is ::subs/tag1
note that the keyword
and symbol
functions don't validate their arguments, so you can make all sorts of illegal values without it throwing an error
(keyword "many words with spaces" "and (delimiters??? [}")
;; => :many words with spaces/and (delimiters??? [}
What's the idiomatic way of using constants? I have seen the use of CONST: (def ^:const url "http://..."
, as ex-java this would suffice, is there any alternatives?
Forgive me now, but... aren’t all defs pretty much constant? As another ex Java dude I haven’t worried too much 🙂
Yeah, I'd say that's a good default assumption in Clojure: all vars are immutable. I've never used ^:const
myself mainly for that reason in fact but I do seem to recall reading somewhere that there was some oddness to it too - it doesn't necessarily behave as you'd expect.
Just had a quick experiment to remind myself: it doesn't stop you redefining the var further down but any references to the var in functions do not change as a result.
So for example:
(def ^:const x 1)
(defn prn-x [] (prn x))
(def ^:const x 2)
(prn x)
=> 2
(prn-x)
=> 1
This is a good discussion on it: https://stackoverflow.com/questions/9162558/how-does-clojure-const-work
yep, that seems better, so this would be the 'right' way
I think the trick is to stop thinking in java terms of "constants" and "variables" and embrace the idea that everything is immutable. Generally in Clojure a var that represents something that is expected to change is wrapped in an atom
(or ref
potentially).
And if it's reused by many functions in the program?
I won't need any 'state' mechanism here (atom, ref, etc)
Correct. Atoms, refs etc should be considered if the value is going to change. This discussion explains them very well: https://stackoverflow.com/questions/9132346/clojure-differences-between-ref-var-agent-atom-with-examples
another thing to consider is that you can replace a def with a let, or a function arg, and that often leads to cleaner, more functional code
there's a truism in the compiler design world that every global is an invisible arg to every function in the codebase
@mattias504 replied on the thread
^:const
is a hint to allow the compiler to inline certain constant literals, namely strings and numbers
I used to use ^:const
but I stopped given the reasons above - vals are immutable, so it's implicit
question is, what's the favourite - to uppercase the def val, or to keep it lowercase? 🙂
I just don't use def at all, then you don't have to decide :)
(not really, but minimizing use is imo a good idea)
I've started to favour keeping the defs lowercase (I don't use that many @alexmiller honest!) as well, as above, it's implicit and seems more clojure-ish!
curious - beginner myself i would be inclined to do this in my code (def tax_rate 0.13)
- is that wrong?
that's fine
(and this really is the case where inlining via ^:const makes sense)
it's really stateful or side-effecting defs that are problematic
What’s keeps that def from being optimized/inlined/otherwise tricked with (without const)?
vars are stateful and require dereferencing (they can change)
One of the things I had to understand is that when playing with the repl, you can define a var, i.e., (def foo "hello")
then eval it foo
, then change it (def foo "goodbye")
, eval it and it'll print out goodbye
- from my understanding is we all talk about Clojure having immutable values, but those values can change if confined to the same thread, thus foo
can be redefined since it's in the same thread. Have I got that hopelessly wrong?
immutable values are good
vars created with def however are stateful identities that refer to an immutable value
and can be changed to refer to a different immutable value
and this is not confined to a single thread; vars are global and can be redefined from any thread (as can all stateful values, if you have a reference to them).
so, is the var acting like a pointer to an immutable value, and that var pointer can change to another immutable value?
tbh, it's one of the first questions that I'm asked when I demostrate clojure at the repl, and I haven't given a good answer yet (as I'm still very much a newbie), when I def a var, then show how I can redef the var and the person asks "I thought Clojure values were immutable, how come the var has changed?"
In Java, you can demonstrate mutability by having a reference to an object. Date d = new Date();
and you can then d.mutateTheValue(...)
and, the critical thing here is, without changing the instance of the object that d references, you have mutated the value it's referencing - the instance of the Date
was changed. That can't happen in Clojure.
the var is a box - it holds an immutable value
but you can put a different immutable value in the box
(def a 1) means "declare a, in this namespace, to refer to a var, that holds the value 1"
the key thing is that we have separated the identity (a), from the state (the var), from the value (1)
https://clojure.org/about/state is a good follow up read
Hi guys. I am trying to slurp some json data, and I get the following error: No such namespace: http:
Any idea why? Also, is there a specific way to “connect” to an API, or do you just slurp URLS and convert them from JSON -> CLJ?
@georghagen sounds like you've missed the string delimiters around your URL
thanks @alexmiller
(slurp )
Syntax error compiling at (REPL:1:1).
No such namespace: http:
should be
(slurp "")
@dharrigan here's a nice little demo to show Java-minded folks, an example showing how even aggregates/collections/"objects", in Clojure, are immutable (and how this same exercise would end up with a different result in a similar Java scenario):
user=> (def v1 {:type :famous-person :fname "Luke" :lname "Wilson"})
#'user/v1
user=> (def v2 v1)
#'user/v2
user=> v1
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> v2
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> (assoc v1 :lname "Skywalker")
{:type :famous-person, :fname "Luke", :lname "Skywalker"}
user=> v1
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> (def v1 (assoc v1 :lname "Skywalker"))
#'user/v1
user=> v1
{:type :famous-person, :fname "Luke", :lname "Skywalker"}
user=> v2
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=>
@georghagen regarding connecting to an API, I use clj-http (https://github.com/dakrone/clj-http). When Cheshire (https://github.com/dakrone/cheshire) is also on the classpath you can get it to convert JSON responses into Clojure maps, like so (clj-http.client/get "http://...." {:as :json})
(https://github.com/dakrone/clj-http#output-coercion)
Thank you so much. Now I just have to figure out what to call, and then how to work with data given to present it nicely!
clj-kondo issues me a warning about my horrible fn:
(defn- safe-read-int [^DataInputStream dis]
(try
(.readInt dis)
(catch .EOFException e nil)))
I'm calling (when-let [n (safe-read-int dis)] ... )
Can anyone help me write this in a better way?(catch
would do the same thing (_ is idiomatic for a value that's ignored, and an empty body always returns nil)
the problem about returning nil (which you mention elsewhere) would come down to the consumer of this code needing a nil check now - you could take an extra "default" arg and return that on eof (a summing function could pass 0, a product function 1, etc.)
or maybe an EOF is a bigger problem, and that NPE is helpful :D
in this case I'm expecting an EOFException eventually (this might be incorrect usage of DataInputStream, I'm not a Java person). I'm essentially replacing readLine
in the clojure.core line-seq
source with (.readInt dis)
in that case another option is (try [(.readInt dis)] (catch EOFException _))
and then concat at the top level - the nil safely disappears
(due to coercion by concat of nil to empty)
this demonstrates the principle
user=> (mapcat #(try [(Long/parseLong %)] (catch Exception _)) ["1" "2" "3" "elephant" "4"])
(1 2 3 4)
it might be nice to wrap the idiom in a macro - perhaps
(cmd)user=> (defmacro successes [body coll] `(mapcat (fn [x#] (try [(~@body x#)] (catch Exception _#))) ~coll))
#'user/successes
(ins)user=> (successes (Long/parseLong) ["a" "1" "2" "b" "3"])
(1 2 3)
@michael.e.loughlin what warning do you get?
but it drew my attention to the function, and it made me wonder if returning nil from a catch is Not Good
@michael.e.loughlin as always, "it depends"
I won't dwell on it then! Thanks @delaguardo and @borkdude
Why does (set! **print-length** 10)
work, but this raises:
(def ^:dynamic d)
(set! d 1)
;; Can't change/establish root binding of: d with set
I am trying to extract the value associated with a keyword in a file of .clj, but it only returns to me the key, not the value.
(def return-station-id #(get-in % [:station] :station_id))
(map return-station-id si)
Example of data in the .clj: {:stations [{:station_id "1755", :name "Aker Brygge",
@georghagen Your data structure has :stations
(plural) but your code has :station
singular. So it doesn't find a match and returns the "not found" value -- :station_id
-- the third argument to get-in
thanks. Now I just get: clojure-noob.core> (map return-station-id si) (nil nil nil nil nil nil nil nil nil nil nil nil)
No, (map :station_id (:stations si))
If si
is {:stations [{:station_id "1755", ...} {...} {...}]}
(:stations si)
would be the vector of station information and you map
over that to extract the :station_id
user=> (def si {:stations [{:station_id "1755", :name "Aker Brygge", :other "data"} {:station_id "1234", :name "Station 2"}]})
#'user/si
user=> (map :station_id (:stations si))
("1755" "1234")
user=>
So it depends what exactly is in your si
If you're getting an empty list, si
is not the data you showed but something else.
{:stations [{:station_id “1755”, :name “Aker Brygge”… } {station_id “1023 :name “Tollerud…}]
If si
is that data structure then (:stations si)
is the vector, and (map :station_id (:stations si))
will give you ("1755" "1023" ...)
Ok, so when I define the contents of the file to a value sykkelinfo in the repl, and then call (map :station_id (:stations sykkelinfo))
, it works. But it does not work on the file. But when I (slurp si)
it gives the contents of the file. Strange.
nah that is just the slurp command, when I open the file in emacs its formatted correctly as a map with a key which has a vector of maps as a value
If you just slurp
the file, you get a string, not a data structure. You need to call clojure.edn/read-string
on that to produce a data structure. Sorry, I assumed you were already doing that.
add [clojure.edn :as edn]
to your code's :require
and then you can use (edn/read-string (slurp the-file))
to get a data structure back
(you should always require namespaces before use, even if they happen to be auto-loaded)
(def si-data-structure(edn/read-string (slurp si)))
then (map :station_id (:stations si-data-structure))
And it worked! Thank you so much.
I think you want (map :station_id (:stations si))
?
hey, how would you recommend limiting api calls? the limit is 5 per minute/500 day, but not sure how to do it? write the result of every call to a db with timestamp and check when the last call was?
At work, we use Redis to store state per session to make sure a quota is not exceeded. Redis makes a lot more sense than a DB I think.
Redis docs actually have an example of writing a rate limiter, based on IP address I think.
But also watch https://www.youtube.com/watch?v=m64SWl9bfvk as a caution about rate limiting.
hey guys,
how I can do this
(defn -main [& _]
(loop [line (read-line)]
(when-not (nil? (parser line))
....
(recur (read-line)))))
to read a line and in every interation add a line to a vector?
(defn -main [& _] (loop [line (read-line) p (conj [] line)] (when-not (nil? (parser line)) (println (conj p line)) (recur (read-line) (conj p line)))))
@d.ian.b (loop [lines []] (when-not .... ... (recur (conj lines (read-line))))
didn't understood
I want to send to bash send-> 1 return-> [1] send-> 2 return-> [1 2]
like this
this loop adds an element to lines
each time it recurs
it gets each new line via read-line
(ins)user=> (defn -main [] (loop [lines []] (println 'lines lines) (print "input> ") (flush) (let [l (read-line)] (if (seq l) (recur (conj lines l)) lines))))
#'user/-main
(cmd)user=> (-main)
lines []
(ins)input> a
lines [a]
(ins)input> b
lines [a b]
(ins)input> c
lines [a b c]
(ins)input>
["a" "b" "c"]
*replaced example so it has a prompton the last line it's hard to see, but I just hit return without typing, so it returned the vector of lines
the problem is, I have a reduce function and every input on bash I want to reduce a state again
and send to terminal after each interation
OK - at each step of that loop you have the full vector of inputs so far, so youu can reduce over it or whatever
and it currently prints them all
I made-it here
(defn -main []
(loop [m {::st {}
::txs []}]
(let [l (read-line)]
(when (string? l)
(let [result (m->output m)]
result
(recur (transact m (parser l))))))))
but somehow it prints the last state on the app
not the current
that line result
is a no op, it literally does nothing at all
what do you expect that line to do?
m->output is the function that prints
I want to print the current state off the app
OK, what I'm saying is you have (let [v (f)] v y)
- the part where v is before y does nothing
it's the same as (let [v (f)] y)
which is the same as (do (f) y)
since v is never used
and when has an implicit do, so you can replaace that when block with:
(when (string? l)
(m->output m)
(recur (transact m (parser l))))
it's printing the last m, not after the recur
and I strongly suspect you actually want
(m->output m)
(when (string? l)
(recur (transact m (parser l))))
right
I'm showing you how to simplify the code so the error is easier to see
on the first iter, its printing {}
but understood
there's no need to have read-line twice, unless there's a special case where the first line is used even if it's not parsed
also, consider that when-not makes the whole block return nil instead of a vector as soon as a line isn't accepted
(this might be OK, maybe the vector is only used inside the loop)
also, idiomatically (when-not (nil? ...) ...)
is just (when ... ...)
- nil is falsey
(unless parser returning false is still acceptable, but I'd still avoid double negation (when (some? ...) ...)
)
(and it's a homonym for winsome, which I really don't think is well enough appreciated)
I would have suggested when-some but the origiinal did not bind the return value of the parser
didn't understood
I made-it here
(defn -main []
(loop [m {::st {}
::txs []}]
(let [l (read-line)]
(when (string? l)
(let [result (m->output m)]
result
(recur (transact m (parser l))))))))