This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-04
Channels
- # announcements (7)
- # babashka (32)
- # beginners (106)
- # bristol-clojurians (10)
- # cider (6)
- # clj-kondo (5)
- # cljdoc (10)
- # clojure (110)
- # clojure-australia (10)
- # clojure-dev (6)
- # clojure-europe (12)
- # clojure-nl (2)
- # clojure-norway (16)
- # clojure-spec (9)
- # clojure-uk (59)
- # clojurescript (105)
- # community-development (2)
- # conjure (46)
- # cursive (12)
- # data-science (1)
- # datalog (26)
- # datomic (37)
- # docker (4)
- # emacs (10)
- # events (1)
- # fulcro (8)
- # graalvm (2)
- # jobs (1)
- # jobs-discuss (1)
- # malli (24)
- # meander (13)
- # off-topic (52)
- # pathom (4)
- # polylith (17)
- # proletarian (4)
- # react (1)
- # rewrite-clj (4)
- # shadow-cljs (56)
- # sql (21)
- # xtdb (14)
Two questions: 1. On a let, is it better to put type hint on the let-bound name or on the bounded form?
(let [^String foo "123"])
;; or
(let [foo ^String "123"])
2. Can you get the type hint of a let binding from inside the let?In the example you provided, a type hint is not necessary because the compiler knows the literal string is a…String
@didibus > 1. On a let, is it better to put type hint on the let-bound name or on the bounded form? A more realistic example would be:
(let [^String approach-1 (thing 42)
approach-2 ^String (thing 42)]
)
For me approach-1 is better. If thing
is/becomes a macro, the metadata would be useless (good read: https://github.com/jonase/eastwood#unused-meta-on-macro)
> 2. Can you get the type hint of a let binding from inside the let? (edited)
Perhaps with your own let
impl. Sounds like a thing to be best tackled at compile-time. Or you could use reflection e.g. (class x)
in the middle of the let
if i am using immutable data, do i also need to use an atom? can i just def the value again with the latest version? (eg. after an assoc invocation)
I'm not sure where you are in your Clojure journey but that sounds like a question perhaps better suited to #beginners where folks have opted in to help folks learn Clojure in depth.
This channel tends to assume a level of experience that is likely to get you short and sometimes sharp answers that may not provide enough detail.
From your question, it sounds like you are considering using def
inside another function @UH13Y2FSA?
def
is always global so you should pretty much never use it inside a function. If you're thinking about side effecting code, you'll probably want to explain more of what you're trying to do -- a lot of beginners reach for atom
when they would use a regular (mutable) variable in another language.
yes, i'm wanting to keep a lookup dictionary, that can be added to, but i don't expect client code to hold it for me. client code should be able to resolve using the dict using functions in that same namespace, but also be able to add to it as well... multiple threaded
i'm sort of presuming atom is needed since it's multiple threads, but wasn't sure if that was overkill
https://clojure.wladyka.eu/posts/share-state/ I hope you will my article helpful. Give me some feedback if you like to :)
PS disclaimer: the article was targeted to developers who have experience with Clojure. I don’t know if it is easy to understand for developers who start Clojure journey.
having had a previous "lisp-life" in general I feel comfortable with clojure, but the things that are specific to clojure outside of the "new" data structures not so much. So yes, this article hits that gap in knowledge, thanks.
When using a full clojre(script) Stack and using datomic as a database: Is there a good reason not to use instants in some specified timezone to store and transfer time? (I’m aware that time handling is messy and my question is a little generalized. Just looking for obvious things I might be missing)
As far as I know instants are the best to use. But I don’t use datomic, so I don’t know if this can have effect on this choice.
What you mean by “instants in some specified timezone”? Instants by definition don’t have timezone information ir rather they assume UTC
I worded it clumsily. I meant saving instants and converting them to the users local timezone on the “last mile” before showing them.
How to datafy 3rd party protocols? Is there a better way than the one below?
(extend-protocol p/Datafiable
Object
(datafy [x]
(if (satisfies? CustomProtocol x)
(p/datafy (do-sth x))
x)))
From the previous example:
(require '[clojure.core.protocols :as p])
(require '[malli.core :as m])
(extend-protocol p/Datafiable
Object
(datafy [x]
(cond
(satisfies? m/Schema x)
(do
(extend-protocol p/Datafiable
(class x)
(datafy [y] (m/-form y)))
(p/datafy x))
(satisfies? m/IntoSchema x)
(do
(extend-protocol p/Datafiable
(class x)
(datafy [y] (m/-form (m/schema y))))
(p/datafy x))
:else x)))
The thing is that I'm not sure extending-protocol for object is a good idea. If there is going to be another similar piece of code using the same trick, you I might get an undeterministic behaviour (well, deterministic but depending on the order of form evaluation). Have a look at the below example:
(require '[clojure.core.protocols :as p]
'[clojure.datafy :refer [datafy]])
(defprotocol ProtA (say-a [_]))
(defprotocol ProtB (say-b [_]))
(def sample-prot-a (reify ProtA (say-a [_] "a")))
(def sample-prot-b (reify ProtB (say-b [_] "b")))
(say-a sample-prot-a) ;=> "a"
(say-b sample-prot-b) ;=> "b"
(satisfies? ProtA sample-prot-a) ;=> true
(satisfies? ProtB sample-prot-b) ;=> true
(extend-protocol p/Datafiable
Object
(datafy [x]
(if (satisfies? ProtA x)
:a-object
x)))
(datafy sample-prot-a) ;=> :a-object
(extend-protocol p/Datafiable
Object
(datafy [x]
(if (satisfies? ProtB x)
:b-object
x)))
(datafy sample-prot-b) ;=> :b-object
(datafy sample-prot-a) ;=> #object[dev$reify__57541 0x3a89db1d "dev$reify__57541@3a89db1d"]
So there are ProtA
and ProtB
- two independent protocols, potentially in two separate modules, that would like to datafy theirselfs. If I have two independent extend-protocol p/Datafiable
on Object
, one overrides another.
That is why I'm looking for another ways 🙂
I'm not very well familiar with Clojure protocols, so I might be missing something 😅Well, I think your example is better, not only because the satisfies. In your example, you extend on the specific type of malli class, so even if the someone calls again (extend-protocol p/Datafiable Object ...)
it is ok, as long as the datafy was called on every Malli type.
Some alternative could be extending each Malli type separately, like:
(let [forms [[:and any?]
[:map]
[:map-of string? string?]
[:multi {:dispatch :t}]
[:or any?]
[:orn [:a any?]]
[:sequential string?]
[:vector string?]]]
(doseq [f forms]
(extend-type (class f)
p/Datafiable
(datafy [x] (m/form x)))))
Haha, I read this book in the past. I've just grabbed it to check that page and what is there? An explanation mark to remember this trick 😄
Can someone try the following and tell me what happens? I'm getting a java null pointer exception.
(cl-format false "~&~
item=~A~@
transitions=~A~@
lhs=~A~@
rhs=~A~@"
1
2
3
4
)
it appears to be caused by the final ~@
this prints a line-feed, but the error seems to be that the underlying is trying to read arguments past 1 2 3 4.
seems to be reproducible by a much simpler call
(cl-format false "
~@")
I reported this on askclojure https://ask.clojure.org/index.php/10897/is-this-a-bug-in-cl-format
looks like in compile-directive
it's calling (Character/toUpperCase ^Character directive)
where directive is (first rest)
which is assuming that the next character (after the @
) is not nil ... right? ... that's the cause of the null pointer ... I'm pretty rusty with by cl-format, is that just a bad error message?
a trailing ~@
seems to violate the description of what it should do, right? cos the newline is missing??
yes I think you're right. it shouldn't have a null exception, but rather complain that it is expecting at least one character after the @
???
putting a newline after the @ works.
(cl-format false "~&~
item=~A~@
transitions=~A~@
lhs=~A~@
rhs=~A~@
"
1
2
3
4
)
in common lisp
(format nil "~@")
gives the following
error in FORMAT: String ended before directive was found
~@
^
[Condition of type SB-FORMAT:FORMAT-ERROR]
Restarts:
0: [RETRY] Retry SLIME REPL evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1004289BC3}>)
Backtrace:
0: (SB-FORMAT::FORMAT-ERROR-AT* "~@" 0 "String ended before directive was found" NIL)
1: (SB-FORMAT::FORMAT-ERROR-AT "~@" 0 "String ended before directive was found")
2: ((FLET SB-FORMAT::GET-CHAR :IN SB-FORMAT::PARSE-DIRECTIVE))
3: (SB-FORMAT::PARSE-DIRECTIVE "~@" 0 NIL)
4: (SB-FORMAT::%TOKENIZE-CONTROL-STRING "~@" 0 2 NIL)
5: (SB-FORMAT::TOKENIZE-CONTROL-STRING "~@")
6: (SB-FORMAT::%FORMAT #<SB-IMPL::CHARACTER-STRING-OSTREAM {10045EAB73}> "~@" NIL NIL)
7: (FORMAT NIL "~@")
8: (FORMAT NIL "~@") [more]
9: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FORMAT NIL "~@") #<NULL-LEXENV>)
10: (EVAL (FORMAT NIL "~@"))
ahhh I see what's happening. the @ is just a flag to the "~
" directive. that's clever. I never realized that before. I thought ~@
was the directive. duhhh
@jimka.issy where did you get the ~@
notation from? Checking the CLHS, it seems like you want ~%
if you want to explicitly print a newline
(use '[clojure.pprint :only (cl-format)])
(cl-format false "~&~
item=~A~%
transitions=~A~%
lhs=~A~%
rhs=~A~%"
1
2
3
4
)
"item=1\n\n transitions=2\n\n lhs=3\n\n rhs=4\n"
the ~@ prints a new line and then skips the white space in the format string. This allows the lines such as "item=", "transitions=" etc to be aligned in the source code, but print at the left-margin in the output stream.
I guess ~@ at the end of string is really dubious. cl-format should skip the white-space that follows, of which there is non in my string.
using @ rather than % in your example should result in the following:
(use '[clojure.pprint :only (cl-format)])
(cl-format false "~&~
item=~A~@
transitions=~A~@
lhs=~A~@
rhs=~A~%"
1
2
3
4
)
"item=1\ntransitions=2\nlhs=3\nrhs=4\n"
optionally with a leading \n
if necessary
I see (from a conversation with @l0st3d that it is not ~@
which produces this behavior but ~ followed by newline. this directive when given the @
flag has the special meaning of newline-skip-white-space-and-left-justify-output
However, just substituting the ~@
for ~%
in your example format string and converting to CL, it fails in SBCL (one of the best ANSI CL compliant open source implementations available) as well:
❯ sbcl
This is SBCL 2.1.7, an implementation of ANSI Common Lisp.
More information about SBCL is available at < >.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (format nil "~&~
item=~A~@
transitions=~A~@
lhs=~A~@
rhs=~A~@"
1
2
3
4
)
debugger invoked on a SB-FORMAT:FORMAT-ERROR in thread
#<THREAD "main thread" RUNNING {7001538143}>:
error in FORMAT: String ended before directive was found
~&~
item=~A~@
transitions=~A~@
lhs=~A~@
rhs=~A~@
^
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-FORMAT::FORMAT-ERROR-AT* "~&~
item=~A~@
transitions=~A~@
lhs=~A~@
rhs=~A~@" 158 "String ended before directive was found" NIL)
0]
I think this is what you’re looking for:
❯ lein repl
nREPL server started on port 62801 on host 127.0.0.1 -
REPL-y 0.4.3, nREPL 0.5.3
Clojure 1.8.0
OpenJDK 64-Bit Server VM 11.0.11+9
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (use '[clojure.pprint :only (cl-format)])
nil
user=>
user=> (cl-format false "~&~
#_=> item=~A~@
#_=> transitions=~A~@
#_=> lhs=~A~@
#_=> rhs=~A~@
#_=> "
#_=> 1
#_=> 2
#_=> 3
#_=> 4
#_=> )
"item=1\ntransitions=2\nlhs=3\nrhs=4\n"
exactly. it fails with a helpful error message.
Don’t know if you found my explorations helpful or not with respect to providing more information on the issue with cl-format regarding it yielding a informative error message rather than a NPE…
as I've said several times before. hats-off to whoever implemented cl-format
for clojure. that must have been and horrendously difficult task.
Might need to type (clojure.repl/apropos "hash")
, depends on the type of REPL, I guess
clojure always runs (apply require clojure.main/repl-requires)
which ends up referring clojure.repl/apropos to your startup ns, but some repls end up changing ns after startup
luckily that one liner isn't super hard to run in whatever ns you end up in
for reference
user=> (pprint clojure.main/repl-requires)
[[clojure.repl :refer (source apropos dir pst doc find-doc)]
[clojure.java.javadoc :refer (javadoc)]
[clojure.pprint :refer (pp pprint)]]
nil
If a function f
returns a function, and I want to call that function with an argument, the syntax is something like this ((f x y) u v)
. However, I find that syntax somewhat cryptic. am I the only one?
(let [...
new-final-transitions (for [q (xym/states-as-seq dfa)
:when (:accepting q)]
;; we designate new final states each as [:F some-exit-value]
[(:index q) :epsilon [:F ((:exit-map dfa) (:index q))]])]
In this code (:exit-map dfa)
evaluates to a function which I call with the return value of (:index q)
. I have inserted an apologetic comment on the previous line.I'm often tempted to write a function funcall
just to make this pattern explicit. not sure if that would be less confusing or more confusing.
(let [...
new-final-transitions (for [q (xym/states-as-seq dfa)
:when (:accepting q)]
;; we designate new final states each as [:F some-exit-value]
[(:index q) :epsilon [:F (funcall (:exit-map dfa) (:index q))]])]
or perhaps
(let [...
new-final-transitions (for [q (xym/states-as-seq dfa)
:when (:accepting q)]
;; we designate new final states each as [:F some-exit-value]
[(:index q) :epsilon [:F (apply (:exit-map dfa) (:index q) nil)]])]
question was do other people find this cryptic or is it a perfectly valid clojurism? BTW good to know about the .invoke
function. thanks.
Strictly speaking it is a method)
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java can be considered a public API. It even shows up here: https://clojure.github.io/clojure/javadoc/clojure/lang/IFn.html
@jimka.issy — is your background in Lisp-1 languages like Scheme / Clojure or Lisp-2 languages like CL?
its about 50/50. the propietary lisp1 that I used for many years had a funcall
function which people used for this case, but was normally redundant.
if you want a funcall
you could use (apply (:exit-map dfa) [(:index q)])
or something?? ... but I too tend to use let
to name the function
(defmacro >-
[x & forms]
(loop [x x, forms forms]
(if forms
(let [form (first forms)
threaded (if (seq? form)
(with-meta `(~x ~(first form) ~@(next form)) (meta form))
(list x form))]
(recur threaded (next forms)))
x)))
(>- f (x y) (u v))
:man-shrugging:yes if you want to do it with a macro, I guess you really need to preserve the meta data. wouldn't it be better with a normal lambda function? Something like the following untested:
(defn funcall [f &rest args]
(cond (empty? args)
(f)
(empty? (rest args))
(apply f args)
:else ;; need to assert (last args) is a seq!
(apply f (concat (butlast args) (last args)))))
Besides the very heavy performance overhead of calling last/butlast and apply/concat, the macro is repeatedly left associative, which means you can write stuff like (>- f x y z)
which will expand to (((f x) y) z)
, could be useful if you're doing stuff based on untyped LC
I'd like to try my hand at debugging an error I found in cl-format
. This means locally I need to re-define (for testing) a function which is defined in cl_format.clj
and is defined as follows:
(defn- compile-directive [s offset]
(let [[raw-params [rest offset]] (extract-params s offset)
[_ [rest offset flags]] (extract-flags rest offset)
directive (first rest)
def (get directive-table (Character/toUpperCase ^Character directive))
params (if def (map-params def (map translate-param raw-params) flags offset))]
(if (not directive)
(format-error "Format string ended in the middle of a directive" offset))
(if (not def)
(format-error (str "Directive \"" directive "\" is undefined") offset))
[(struct compiled-directive ((:generator-fn def) params offset) def params offset)
(let [remainder (subs rest 1)
offset (inc offset)
trim? (and (= \newline (:directive def))
(not (:colon params)))
trim-count (if trim? (prefix-count remainder [\space \tab]) 0)
remainder (subs remainder trim-count)
offset (+ offset trim-count)]
[remainder offset])]))
I tried the following:
(def clojure.pprint/compile-directive
(fn [s offset]
...)
but I get an error
Syntax error compiling def at (clojure-rte:localhost:64772(clj)*:1829:23).
Can't create defs outside of current ns
(require 'clojure.pprint)
(in-ns 'clojure.pprint)
(defn compile-directive ...)
(in-ns 'your.ns)
does the trick. Thanks for the suggestion.
I updated https://ask.clojure.org/index.php/10897/is-this-a-bug-in-cl-format?show=10900#c10900
Not sure why the restriction to not be able to create defs outside the current ns is a necessary restriction. 😡
I would say because it destroys locality of definition. If "any" code goes around redefining things in other namespaces then a) you can no longer rely on just looking in the namespace where its source lives and b) the semantics of the function now depend on the order that code is executed. Both of those are a Bad Thing(tm) in maintainable code.
That's an interesting idea, but it doesn't work. I can still call in-ns
in any file, and end up doing the same thing in a less declarative way. It seems to me that allowing the more declarative defn x/foo
or def x/foo
would only help to find definitions rather than obscure them. Plus, one of the features of defmethod
is that even if the defmulti foo
is defined in namespace X, I can define methods on X/foo
in many different files thus allowing applications to extend a multi-function.
Sure, but this makes you work at it and deliberately doesn't normalize it. That is usually Clojure's way.
Is clojure.core AOT compiled?
I'm trying to use approach similar to the when-class
macro and conditionally definite a function only if java.lang.ProcessHandle
is available.
I'm not sure about possible implications for AOT compiled uberjar.
Would this be a good approach?
(when-class "java.lang.ProcessHandle"
(let [all-proc-fn (requiring-resolve 'util.processes.impl/all-processes)]
(->> (all-proc-fn)
.iterator
iterator-seq
(mapv process-info))))
;;; in another ns
(ns util.processes.impl)
(defn all-processes []
(java.lang.ProcessHandle/allProcesses))
> Would this be a good approach? I think so, just make sure "AOTer" and "production" environments match. Same JDKs, identical classpaths, etc Best achieved at CI level
Yeah, it might be actually a problem - I'll need to check. We have to support Java 8 in some environments but in others (like our Docker image) we can count on JDK 11.
Might be stating the obvious but that would be a good reason to generate different AOTed artifacts on a CI matrix
@U2FRKM4TW Where can I check that core is AOT compiled? I skimmed https://clojure.org/reference/compilation but didn't find it there - only that it's directly linked
> As of Clojure 1.8, the Clojure core library itself is compiled with direct linking.
Ah right, so compiled is the same as AOT-compiled - yeah, i guess that's obvious, just being a bit confused.
btw being pedantic "compiled" can also apply to any use of clojure.core/compile
... which you can invoke at runtime, which makes it a case of non-AOT compilation
Yeah, but the net affect is the same/similar? That is a class file being written to the disk. Perhaps there's a difference depending on how/when you load the code that is being compiled - before vs after compilation?
Hmm, I was surprised that the above code works well in AOT-compiled uberjar: • when run with Java 8 it returns empty collection • when run with Java 16 it returns expected processes.
> Yeah, but the net affect is the same/similar? That is a class file being written to the disk. > Perhaps there's a difference depending on how/when you load the code that is being compiled - before vs after compilation? Subtle one, I think I'd need a refresher for accurately answering :) I think that clojure has a contractual priority when finding both a .class and .clj file (don't ask me which ;p) The classpath and order of operations might also affect
> Hmm, I was surprised that the above code works well in AOT-compiled uberjar: yeah tbh it looked like it would just work. It partly depends on the use case. If you want to rest assured that you won't find opaque issues in a future I'd go the "pedantic route" and create fine-grained artifacts as menioned