This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-12-07
Channels
- # adventofcode (94)
- # babashka (29)
- # babashka-sci-dev (2)
- # beginners (103)
- # calva (15)
- # cider (17)
- # clj-kondo (62)
- # cljsrn (24)
- # clojars (13)
- # clojure (97)
- # clojure-belgium (3)
- # clojure-berlin (3)
- # clojure-czech (1)
- # clojure-europe (68)
- # clojure-nl (1)
- # clojure-norway (3)
- # clojure-seattle (3)
- # clojure-uk (1)
- # clojurescript (7)
- # community-development (29)
- # conjure (2)
- # cursive (14)
- # data-science (15)
- # emacs (3)
- # graphql (10)
- # gratitude (1)
- # holy-lambda (32)
- # hoplon (21)
- # hyperfiddle (2)
- # jobs (2)
- # joyride (36)
- # lsp (4)
- # meander (13)
- # off-topic (203)
- # pathom (3)
- # polylith (6)
- # re-frame (4)
- # reagent (1)
- # reitit (28)
- # releases (1)
- # shadow-cljs (16)
- # slack-help (2)
- # sql (27)
- # vim (2)
How can I expand varargs when calling macro within a macro?
(defmacro reg-parse [l bind reg statement & more-clauses]
`(if-let [~bind (re-find ~reg l)]
~statement
~(when (seq more-clauses)
(apply reg-parse l more-clauses))))
So in this case I want macro to emit an iflet for each 3 items given, calling itself recursivelyYou can’t apply reg-parse because it is a macro. Is there any reason reg-parse must be a macro?
(defmacro reg-parse [l bind reg statement & more-clauses]
`(if-let [~bind (re-find ~reg ~l)]
~statement
~(when (seq more-clauses)
(apply list `reg-parse l more-clauses))))
this works
(defmacro reg-parse [l bind reg statement & more-clauses]
`(if-let [~bind (re-find ~reg ~l)]
~statement
~(when (seq more-clauses)
`(reg-parse ~l ~@more-clauses)))
the unquote splice ~@
operator was created for this exact problem.(take 3 (iterate pop [1 2 3])))
I didn't know this is ok. I thought lazy-seq, at least some of them, works 8 or 16 in a chunk.
it's immutable data - what's the harm in popping more?
but note that you're losing all the data being popped, so usually this is probably not a very useful thing to be doing
i check source of map/filter they use chunkXXX so iterate although returning lazy-seq but doesn't use chunk
why would it matter if take was chunked?
or if iterate produced a chunked seq?
from a consumer perspective, there is no difference. something might do more work in a chunk but it's unlikely to actually matter in most cases, esp when manipulating immutable collections
user> (iterate pop [1 2 3])
([1Error printing return value (IllegalStateException) at clojure.lang.PersistentVector/pop (PersistentVector.java:470).
Can't pop empty vector
that's actually the weird thing :) most coll ops have defined behavior for empty/nil
(like butlast
)
user=> (take 10 (iterate butlast [1 2 3]))
([1 2 3] (1 2) (1) nil nil nil nil nil nil nil)
public PersistentVector pop(){
if(cnt == 0)
throw new IllegalStateException("Can't pop empty vector");
public IPersistentStack pop(){
if(end - 1 == start)
{
return PersistentVector.EMPTY;
}
return new SubVector(_meta, v, start, end - 1);
}
static public Object pop(Object x){
if(x == null)
return null;
return ((IPersistentStack) x).pop();
}
I mean, you don't need to go that deep even:
user=> (doc pop)
-------------------------
clojure.core/pop
([coll])
For a list or queue, returns a new list/queue without the first
item, for a vector, returns a new vector without the last item. If
the collection is empty, throws an exception. Note - not the same
as next/butlast.
How can I create custom reader macros? I know it is not possible by design, and I know what are the reasons for why it's not good to be able to do it, but what if I really wanted to do it?
I’ve seen it done, but I forgot how
Ah. Well. Not so hard!
it is called tagged literal https://clojure.org/reference/reader#tagged_literals and it is not hard at all
common lisp has reader macros which can let you do things like use the lisp reader to reader json, or embed almost arbitrary syntax in common lisp programs (I think, haven't used them, just seen blog posts)
clojure has tagged literals, which let you build representations for things to be read out of forms the reader already understands (it is compositional)
If you aren't satisfied by tagged literals the solution is a fork, right?
Well I know I for one was frustrated with the inability to make it so that I could have a #rope
literal that allowed evaluated symbols inside it
but that's more extending the evaluator than the reader
I'd love to hear how, my tests with it showed it as troublesome
Clojure 1.11.1
user=> (def f identity)
#'user/f
user=> (set! *data-readers* (assoc *data-readers* 'rope #'f))
{rope #'user/f}
user=> #rope foo
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: foo in this context
user=> '#rope foo
foo
user=> (defn f [x] (list 'quote x))
#'user/f
user=> #rope foo
foo
user=>
Might be worth it to get into another thread to not clutter this one. I'm not so lucky here with my usecase.
Evaluating symbols in data-readers 🧵
So like you said if you return a symbol it'll get evaluated as normal @U0NCTKEV8
However, if I return a custom data structure it can't recursively evaluate it.
And unfortunately the #=()
syntax doesn't evaluate the arguments past the function
So the usecase I have in mind https://github.com/IGJoshua/ropes
Unfortunately I can't do something like
(let [[a b c] "xyz"]
#rope [a b c])
because it'll return a rope with those symbols in it, not a rope with x y and z in it
Right, but I'd expect to be able to do this with data structures defined inside clojure core, and it's frustrating that I can't make a new data structure that's equally supported
you can make #rope return code that builds a rope when evaluted, not a rope itself, but that also has issues
that's a thought I guess
Right.
What's the issue you're referring to with generating code that makes a rope rather than returning one? Stuff with identity?
It looks like it could actually solve my problem.
right, it wouldn't be a rope
which is why I was thinking of falling back to the #=
but that doesn't evaluate the args either for sensible reasons
So yeah, that all makes sense, it's kinda one or the other if you can't extend the evaluator.
you can have a #rope-template tag that produces code that creates a rope, and have all ropes still print using the #rope tag
I'll think about it for sure.
That’s what I do in #sicmutils for complex numbers, quaternions etc https://github.com/sicmutils/sicmutils/blob/main/src/sicmutils/complex.cljc#L77
And then I print using the reader literal tag https://github.com/sicmutils/sicmutils/blob/main/src/sicmutils/complex.cljc#L130
Got a question about EDN vs Transit. I’ve got an endpoint that is really underperforming by using Transit and I’ve just found that by using EDN it is 10x faster. It is my understanding that Transit should be more performant than EDN so maybe I am doing something wrong. This is the first endpoint that we have that has biggish payloads (ca 1-2mb) so maybe that’s why we hadn’t noticed. Anyways, we are using pedestal (on the server) and reframe (in a react native environment). Any thoughts about this problem?
Makes sense, thanks @U08JKUHA9
we are using clj-ajax
on the react native app
I have two libraries, lib1
and lib2
, and an app that uses both.
lib1
contains a simple defrecord:
(ns lib1.core)
(defrecord ARecord ...)
lib2
provides some additional support for this type, by extending a third-party protocol to it:
(ns lib2.core)
(extend-protocol clojure.java.jdbc/ISQLValue
lib1.core.ARecord
(sql-value [x] ...))
This compiles fine, but in my app I find that this ISQLValue
implementation doesn't work even though I've required lib2.core
. What's strange is that if I evaluate that exact extend-protocol
in my app's REPL, it works.
What could cause this? It's maddening to track down because there are no errors, and it works from a REPL.any number of things, usually related to having multiple versions of the record type or the protocol type
also possibly the result of having an aot'ed version on disk and then creating the type again, etc
given you are using the full time name in the extend call lib1.core.ARecord
and don't require lib1.core in lib2.core it is likely the aot issue
a good rule of thumb is, if you use a type, you must require the namespace that creates that type
I am actually requiring the type in lib2
, sorry for the oversimplified code
yes yes, I know
(ns lib2.core (:require [lib1.core :as lib1]))
another rule of thumb with protocols is when extending a protocol to a type you should own either the protocol or the type
like you could be loading some later code that defines a different extension of ISQLValue to ARecord
in this case lib1
is my own library so it's not likely there are other extensions lying around
agreed that there are issues with the general approach of ISQLValue
, but we've been using it without issue on various types like clojure.lang.Keyword
for a long time
you can look in the protocol to see what types it is extended to (see the user.Foo under impls)
user=> (defprotocol X (foo [x]))
X
user=> (defrecord Foo [])
user.Foo
user=> (extend-protocol X Foo (foo [x] x))
nil
user=> X
{:on user.X, :on-interface user.X, :sigs {:foo {:tag nil, :name foo, :arglists ([x]), :doc nil}}, :var #'user/X, :method-map {:foo :foo}, :method-builders {#'user/foo #object[user$eval156$fn__157 0x49aa766b "user$eval156$fn__157@49aa766b"]}, :impls {user.Foo {:foo #object[user$eval191$fn__192 0x963176 "user$eval191$fn__192@963176"]}}}
user=>
and you can capture the class for your record and extension time, and compare it to the class for the record when the extension seems to have failed to check to see if the record is being redefined
it does list ARecord
, so I guess it must be that the record is getting redefined. What's the remedy for that? defonce
?
I mean there's only one namespace that calls defrecord
, so I'm not sure how it's possible
there's no macro trickery or anything
specifically forcing a reload of the defrecord code, but not a reload of extension code (or possible reloading both but in the wrong order)
ah, that gives me something to go on. Thanks, I'll do some more digging