Fork me on GitHub
#clojure
<
2022-09-06
>
djblue04:09:36

I think I've run into an interesting edge case with range + float step + metadata:

(range 0 5 1.0) ;=> (0 1.0 2.0 3.0 4.0)
(with-meta (range 0 5 1.0) {}) ;=> (5)
I would expect with-meta to preserve the initial value of the range.

dpsutton06:09:50

weird

user=> (def x (range 0 5 1.0))
#'user/x
user=> x
(0 1.0 2.0 3.0 4.0)
user=> (with-meta x {})
(5 1.0 2.0 3.0 4.0)

dpsutton06:09:37

ah, it looks like they have the args swapped

public Obj withMeta(IPersistentMap meta){
	if(meta == _meta)
		return this;
	return new Range(meta, end, start, step, boundsCheck, _chunk, _chunkNext);
}

dpsutton06:09:46

private Range(IPersistentMap meta, Object start, Object end, Object step, BoundsCheck boundsCheck, IChunk chunk, ISeq chunkNext){

dpsutton06:09:36

it is called with meta, end, start, step, boundsCheck, _chunk, _chunkNext but the args of the constructor are meta, start, end, step, boundsCheck, _chunk, _chunkNext

Alex Miller (Clojure team)11:09:28

Thx - what version are you on?

☝️ 1
dpsutton13:09:08

1.11.1 for me.

Gerome16:09:52

Hello everyone? Does anyone know a good way to programmatically escape regex chars in a string? I would like to escape them because I am constructing the pattern at runtime from different parts. I have a starting point but it's not working because \ are special characters in Clojure strings. See thread for my code.

p-himik16:09:01

java.util.regex.Pattern/quote

Gerome16:09:32

Hmm, thanks, I'll look into it.

Gerome16:09:37

I'm not entirely sure this is exactly what I need because this returns a quoted pattern but I need something to quote the \ character in my replacement.

Gerome16:09:18

However, if I escape it with \\ it turns into a single \ which is then interpreted as an escape for the $

delaguardo16:09:59

(clojure.string/replace "." #"[.*+?^${}]" #(str "\\" %1))
third argument for replace could be a function

🎉 1
Gerome16:09:40

That did the trick! Thank you!

winsome16:09:33

fwiw, I've found https://github.com/lambdaisland/regal very helpful for composing regexes, the alternate syntax makes it almost self-documenting for future reading too.

winsome16:09:18

I had to write a parser by hand and regal made it much easier.

Gerome20:09:01

Huh, interesting.

Gerome13:09:35

@U028BUU1P3R I took a look at regal but it doesn't have non-capturing groups and it looks like I need those unless want to write my own state machine.

winsome13:09:24

It does, you just use :cat instead of :capture. If you want to check what it produces just call regal/regex and compare the generated regex with a hand-rolled version.

Gerome15:09:23

I've been using it and I don't want to go back. It’s convenient to have refer literals in a string like format but it’s super powerful to be able to compose and manipulate regexes like you manipulate arrays.

🙌 1
nonrecursive16:09:54

Hey y’all - I recall reading recently that Excel added support for Scheme and Clojure, but I’m having trouble finding an article or announcement. Does that sound familiar to anyone, or am I making that up?

nonrecursive17:09:22

Ah that must be it, thank you! For some reason I was thinking that Microsoft itself had done this

valtteri17:09:49

This is the one I saw here and cross-posted to our company slack 🙂 https://code-magazine.com/Article/2207071/The-Excellent-Schemer

nonrecursive17:09:32

cool - thanks!

madis17:09:44

Hello Masters of Clojure. I have a monorepo structure (here simplified), where

/deps.edn                      - defines :deps {is.mad/server-web3 {:local/root "./server/web3"}}
/server/web3/deps.edn          - defines :test alias with {:extra-paths ["test"]}
/server/web3/src/<more files>
/server/web3/test/<more files> - these I would like to be included in the build & run
I'm trying to run commands via clj from the top level with clj -A:shadow:test watch test-node But the test alias isn't taken into consideration. My understanding and assumptions must be wrong how this works, so maybe you could help me figure out the following - How to define aliases (that have extra-paths) in dependency (sub-project) folder deps.edn? Or is there a better way to achieve it? > I'm using CLJS, but reading tools.deps.alpha code this doesn't seem to be ClojureScript specific, so I thought to post it here. My apologies if it belongs to #clojurescript

Alex Miller (Clojure team)17:09:40

the aliases will only be used from the deps.edn in the directory you are in (dependency aliases are not used)

madis17:09:47

Thanks... that seems to leave me with 2 options for now: 1. define aliases in the top-level deps.edn for each sub-project so that each alias has extra-deps and extra-paths 2. Have each sub-project include the test folder in paths in its deps.edn Am I missing something, is there a better way?

Alex Miller (Clojure team)17:09:31

I think those are both options that would work

Alex Miller (Clojure team)17:09:12

a simpler version of 1 would define a single alias with all the nested extra-deps and extra-paths (not sure how interrelated all of those are)

madis17:09:49

Awesome. Thanks Alex! Clojure is great, the best language in ze worlld!

madis17:09:15

And the community is great too! What's not to love 😍

FlavaDave17:09:48

Hey all, I am trying to write a macro to the declares a function and have gotten something pretty simple to work. The issue is that i want to iterate through a map and apply a function to the key to generate the function name and apply a function to the value to generate the args list and part of the function body. Below is a simplified version of what I am trying to do:

(def sample-map {:foo/bar {:properties
                           {:a "a"
                            :b "b"}}
                 :biz/baz {:properties
                           {:c "c"
                            :d "d"}}})

(defn key->fn-name [k]
  (-> k name symbol))

(defn make-arg-array [obj]
  (let [properties (keys (:properties obj))]
    (->> properties
         (map csk/->kebab-case-symbol)
         vec)))

(defn create-function
  [k v]
  (let [fn-name  (key->fn-name k)
        arglist (make-arg-array v)]
    `(defn ~fn-name
       [{:keys ~arglist :as ~'resource}]
       ~'resource)))

(defmacro build-fuctions [map]
  (let [fn-list  (map (fn [[k v]] (create-function k v)) map)]
    `(do
       ~@fn-list)))
most things work with manual calls such as
(create-function :biz/baz {:properties
                           {:c "c"
                            :d "d"}})
;; => (clojure.core/defn baz [{:as resource, :keys [c d]}] resource)
and
(map (fn [[k v]]
       (create-function k v)) sample-map)
;; => ((clojure.core/defn bar [{:as resource, :keys [a b]}] resource) (clojure.core/defn baz [{:as resource, :keys [c d]}] resource))
but when i run (build-fuctions sample-map) I get
1. Unhandled java.lang.IllegalArgumentException
   Don't know how to create ISeq from: clojure.lang.Symbol

                   RT.java:  557  clojure.lang.RT/seqFrom
                   RT.java:  537  clojure.lang.RT/seq
              LazySeq.java:   60  clojure.lang.LazySeq/seq
                 Cons.java:   39  clojure.lang.Cons/next
                   RT.java:  713  clojure.lang.RT/next
             Compiler.java: 7181  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  833  java.lang.Thread/run
can anyone help me out?

hiredman17:09:43

you are shadowing map

hiredman17:09:19

once you fix that it still won't work because you are trying to use the value of evaluating a symbol in a macro which runs before evaluation happens

FlavaDave17:09:39

So does that mean I should do map outside of the macro and pass it in? It isn't quite clear to me how to address this.

hiredman17:09:37

which part?

FlavaDave18:09:59

Both parts tbh. I'm pretty new to macros. I dont really understand how I am shadowing map when I called it outside of the syntax quote.

hiredman18:09:24

the shadowing of map has nothing to do with macros and quoting

hiredman18:09:11

you have a function/macro with an an argument named map and then in the body you have (map ... map)

hiredman18:09:53

where both those map names refer to the argument named map that is passed in, and neither refers to clojure.core/map

FlavaDave18:09:57

ohhh I see. Yea I changed the arg name and still got the same error

hiredman18:09:23

well, you got the same exception, but thrown in a different place

hiredman18:09:49

and that is because you are doing something like (m x) where m is a macro, so m is getting passed the symbol x not the value the symbol x evaluates to, and you are trying to use the value x evaluates to in m

FlavaDave18:09:20

hmm so I tried moving the syntax quote up to the let. But it keeps throwing

(defmacro build-fuctions [data]
  `(let [fn-list  (map
                  (fn [[k v]]
                    (create-function k v)) data)]
     (do
        ~@fn-list)))
Unable to resolve symbol: fn-list in this context

hiredman18:09:41

yeah, you are trying to generate code at compile time that depends on information available at runtime (which is after compile time)

hiredman18:09:08

you are trying to time travel information

hiredman18:09:07

for a limited class of things, you can actually do this, because clojure's notion of compile time vs, runtime is fine grained, and the runtime information of previous code is part of the compile time information for the next code

FlavaDave18:09:41

so the data I plan to iterate through will come from http. So could I make that call inside the body of the macro to make it available at compile-time?

hiredman18:09:01

you could, but doing side effects like that in a macro is not a good idea

FlavaDave18:09:08

how else could i make it available at compile time then?

hiredman18:09:18

you can do it, it is a fun party trick, but doing http requests everytime you load your code (and what do you do if those http requests fail, how does that effect your code loading?) is going to drive you nuts

FlavaDave18:09:13

hmm well it looks like I have some thinking to do. Thank you for your help!

dpsutton19:09:33

One thought, if you absolutely must have this information in vars rather than just data at runtime, make a script that hits the endpoint and stuffs it into some edn files. Build vars from the edn map?

FlavaDave19:09:36

that makes sense! thanks

John Conti20:09:14

I have assumed something for a very long time, and now wonder when being asked if I’ve quite been making things up (I certainly could be!). My thought was given something like this:

(def foo
  [reqs]
  (let [xfm (comp set (partial map :resource-object-id) :response))]
    (apply xfm (map do-post reqs))))
that when the defn is evaluated (when the file is loaded), xfm will be identified as a constant function value and will not be recreated each time the function foo is called. To restate comp will not rerun every time foo is called. Instead an anonymous function in memory will exist as a value that xfm is set to. Does any one know enough about the compiler to confirm this or set me straight?

p-himik20:09:52

It will be evaluated each time. It's easy enough to check:

user=> (defn f [] (comp map set))
#'user/f
user=> (f)
#object[clojure.core$comp$fn__5876 0x69b2f8e5 "clojure.core$comp$fn__5876@69b2f8e5"]
user=> (f)
#object[clojure.core$comp$fn__5876 0x1a411233 "clojure.core$comp$fn__5876@1a411233"]
user=> (= (f) (f))
false

1
isak20:09:21

(defn foo []
    (let [a {:a 1}]
      a))

  (System/identityHashCode (foo))
^ an exception where it can re-use.

👀 1
🙇 1
Joshua Suskalo20:09:30

The way you could do this would be to extract your xfm to a def or a local variable, e.g.:

(let [xfm (comp set (partial map :resource-object-id) :response))]
  (defn foo [reqs]
    (apply xfm (map do-post reqs))))

🙇 1