Fork me on GitHub
John Bradens01:09:44

Hi I'd just like to clarify something: so with shadow-cljs, I can run "watch app" and when I make changes to cljs files, I get to see the changes in the browser as soon as I save the files. This is awesome. When I make changes in clj files, I have to recompile every time though, right? Or is there a way I can see the changes immediately when I save those files as well? Basically I just want to make sure everything is still working each time I update a clj file. Or is this basically what writing tests is for?


Are you using CIDER?


If so (I'm sure other development environments also support this), you can rerun tests on every save with cider-auto-test-mode , and you can reload your entire namespace with cider-ns-reload . For an editor-independent (and apparently better) way to achieve the latter, you can use

Drew Verlee02:09:03

When you eval expressions, that is send them to the repl, it will know. But if your running an http server, you might have to do more to make sure it reloads. See this for details


Namespace reloading is a very coarse-grained method (it tries to refresh the entire state of your running application). If you have any specific parts of your code you'd like to more easily update, maybe we could offer some better advice.


Reloading code with shadow-cljs is a very smooth process because there is usually a clean separation between state+data and actions in the cljs code. Reloading usually only changes how actions work, preserving the state in your app.

John Bradens02:09:19

Thanks for the answers. I feel like I might be too ignorant to be able to articulate my question well. Basically right now, I use lein, so I do lein run and then shadow-cljs watch app to see my app in localhost. Every time I make a change in a file, I double check in localhost to see that it's working. So when I make cljs changes, I don't have to re-run any commands. But when I make clj changes, I re-do lein run. I feel like this might not be the best way of going about things... any resources you can point me to, or general things to look into? I do have cider on my emacs and I'll look into those cider commands more. I'll also look into the ring link that's posted.


What pieces of your app are currently requiring you to restart the repl? Often you can just re-evaluate a function in the repl and it'll pick that up.

John Bradens02:09:08

I'm a total beginner so I'm basically experimenting with adding a bunch of random features and also trying to understand routing, websockets, MVC and all that. I edit all sorts of different pieces all the time since I'm trying to learn as much as I can. If I understand correctly, it sounds like I would run lein repl, then go into the namespace I'm currently editing, and then test the functions there to see that they work instead of reloading & compiling the whole app?


Testing functions directly in the editor is probably how most people do it, at least when we're just messing around

John Bradens02:09:06

My problem was that I did lein run and ran shadow-cljs, then when I was editing things in clj files, those changes weren't reflected. And then when I finally recompiled I found out I broke a bunch of stuff without realizing.

John Bradens02:09:36

Ok thanks so much for that advice. I don't think any tutorials I did have gone over testing in the editor directly. Is that what I can use the cider commands for in emacs?

John Bradens02:09:45

Thanks for all the help btw @U024X3V2YN4

John Bradens02:09:58

I'll google this stuff too and will probably find some tutorials on it. Thanks again.


You're only welcome 🙂 shout out if you have more questions


CIDER can find and run tests for you, giving you access to the test-driven style if you want. But it is definately worth trying to get good at using the repl, modyfing, re-evaluating and testing functions (even just in (comment) blocks!) as you go

❤️ 2
John Bradens02:09:47

Cool, now I know the next thing to practice! 🙂


I did not understand this statement of lazy-seq ,

clojure.core/lazy-seq accepts one or more forms that produce a sequence of nil (when the sequence is fully realized) and returns a seqable data structure that invokes the body the first time the value is needed and then caches the result.


It's not only for recursion.`lazy-seq` can be used anywhere you'd benefit from a delay of computation. A common example is to imagine you want to process a large text file line-by-line. The size of the file might be several GBs, too big to hold in memory all at once. If you look at the source code of line-seq , you can see a tactic for processing such a file: Read a line, then recurse in a lazy-seq. After processing, each line could be garbage collected, thereby freeing up space in memory.


what is the meaning of caching the result? in lazy-seq?


The computation in the body of lazy-seq is only done the first time you try to access that value


(defn positive-numbers 
	([] (positive-numbers 1))
	([n] (lazy-seq (cons n (positive-numbers (inc n))))))


in this function it calls iterativly, what do you mean by only done the first time you try to access that valu


(positive-numbers) returns a lazy sequence of numbers. If you call (take 5 ...) on it, only the first five values will be computed. If you then do (take 10 ...) on the same sequence, it will remember those first five values, but has to compute the next five. Notice that there actually are infinitely many values in the sequence, but it will only produce for you as many as you ask of it


ahh I understood it! i was just remember the definition, but now I am able t connect the dot! thanks for your write! 🙂


To demonstrate when execution happens, you could try

(def a (cons 1 (lazy-seq (cons 2 (lazy-seq (println "Done!") (list 3))))))
(nth a 0)
(nth a 1)
(nth a 2)
(nth a 2)
and see the output in the repl.


one more question!, (take 5 (positive-numbers)) positive-number will call fist and then take right.?How cllojure handles it? just resulting 5 values.


Yes, you are first producing the lazy sequnce, then taking the first five values from it. Are you asking how take works?


i got take works! but did not get how clojure handles take, because positive-number will call first right?


Yes, but remember that positive-number returns a lazy sequence. That means it doesn't necessarily contain all its values yet, just the recipe for how to compute them.


If you are trying to look at a lazy sequence in the repl, it will probably try to take as many values from it as it can. This means that lazy sequences can potentially blow up your repl.


Thanks, that helped


I understood that lazy-seq is only used for calling safe recursion, to avoid stackoverflow, is my understanding right?


Hello. Something that I need to do quite often is to transform a map with two elements, grouping by the first and wanting a sequence of the second. Example:

[{:a 1 :b 2} {:a 1 :b 3} {:a 2 :b 5}] => {1 [2 3] 2 [5]}
I usually use group-by then map on values using specter, but is there a more idiomatic way to do this, because it feels like my solution is a waste of space and computation time.


A map doesn't have a notion of positionality


There is no well-defined way to talk about a first and second element


Can you group by the key instead?


Ohg, yeah, soryr, I didnut use the correct termn.


I meant "group by a key and have the value of the other".


Today I do something like this:

(def data [{:a 1
              :b 2}
             {:a 1
              :b 3}
             {:a 2
              :b 5}])
  (->> data
       (group-by :a)
       (sp/transform [sp/MAP-VALS sp/ALL] :b))


A pure Clojure version could look something like

(defn fun [m]
  (fn [result m]
    (update result (:a m) (fnil conj []) (:b m)))
  {} m))

🙌 2

For what it's worth, it's 50% faster.


Nice. Thanks.


what is a simple way to load some user config? In elisp I can just load the file because there are no namespaces, how do I deal nicely with this in clojure?

Johan Thorén10:09:30

What's the format of the user config? I've done the following for reading a config file containing an edn map:

(defn- read-config
     (let [config (clojure.edn/read-string (slurp (config-file)))]
       (get config k))
     (catch _e nil)))
     (clojure.edn/read-string (slurp (config-file)))
     (catch _e nil))))
This returns a map containing the whole configuration, or just a specific key. Assumes that (config-file) will return a string containing the path to the file.


Any code placed in a user.clj on the classpath will be available under the user namespace. This is the quick and dirty way.

👍 2

Comming from elisp it seems to be natural to give the user the power to also redefine functions etc. There is not really a use case in my app for this atm though.


For more control, you can do like @U02DW53HCSE above, or use a library like

👍 2

Hello everybody, would you recommend some library / article about how to create a HTTP proxy in Clojure? I use Ring+jetty


I would like to explore the clojure project which using compojure mount integrant, has anybody make use of this DI in there open source project? Wanted to learn how this is written


@U01J3DB39R6 for compojure / mount, take a look at:

👀 2
🙌 2

The readme also includes a link to a sister project using Integrant and Reitit (vs Component and Compojure)


does somebody know why Var$Unbound cannot be cast to IAtom with this setup?


because clojure require some ceremony before you can call the function provided by your namespace.


IFn require = Clojure.var("clojure.core", "require");

IFn f = Clojure.var("com.github.benjaminasdf.idlelib.core", "-initProj");
something like that


tldr; when calling that function from java - com.github.benjaminasdf.idlelib.core$_initProj`.invokeStatic(project)` you missing namespace “loading” which will initialize the var -project

👀 2

tldr2; calling clojure from java is not a smooth sailing as it might be seeing at first (


thanks a lot! This loading namespace is interesting do you know where to read on this?


the same limitation applies in clojure - even if the ns was AOT compiled you shouldn't try to use its functions without loading the namespace first (usually via require)


i found below code in one of the repo , what is the meaning of #?(:cljs and #?(:clj

#?(:cljs (:require
             [goog.string :as gstring]
  #?(:clj (:import
            [clojure.lang IDeref])))


They are called reader conditionals. You can read about them here:

🙌 2

In short they allow the same source files to be read both by Clojure and ClojureScript, but with conditional evaluation depending on which platform you're on.


They are commonly used in library code so that the same jar can be used on both platforms.


So when this namespace is called from either clj or cljs depends on that package will bw imported right?


Yes, exactly. On Clojure, it will import clojure.lang.IDeref , on ClojureScript it will import the goog stuff, which is particular to ClojureScript.

🙌 2
Franco Gasperino15:09:46

Good morning. What is the recommend route for evaluating the result of a clojure.spec.gen.alpha/check result summary set with the test framework (runners, assertions, and family) of clojure.test?

Franco Gasperino15:09:14

I'm happy to extract summary result key-val pairs from the former and apply them in equality tests in the latter. Is there a better practice?


I'm a bit lost on trying to transform a table with some local state


I have x, I need to transform it into x-processed:

(def x
  [{:p 49 :v "red"}
   {:p 49 :v "blue"}
   {:p 1  :v "green"}
   {:p 1  :v "yellow"}]

(def x-processed
  [{:p 49 :start 0   :v   "red"}
   {:p 49 :start 49  :v  "blue"}
   {:p 1  :start 99  :v  "green"}
   {:p 1  :start 100 :v "yellow"}]


so just conj-ing. :start with the running sum of 😛

Russell Mull20:09:09

Have you looked at the reduce function?


Yes, its a bit confusing how I'd write the reduction function


so I looked at loop/recur.. but that seems like a worse way to do it


the accumulator will hold your new vector so far


Ok so that's the working new conj'd map?


from the last entry in the vector, the :start key will tell you where it left off - does that make sense?


I think so


well, reduce has to take the whole vector of maps from call to call


so you'd get the :start out of the last map in the vector, then return a new vector with a new map on the end


ok, on the reduce page I see some signatures for the reduction fn that are like

(fn [accumulator current-item]  ...)


I think where Im getting caught up is the first call


lets start with the loop/recur solution


the first call gets the initial accumulator, plus the first item of the input


(loop [running-total 0
       unprocessed   x
       processed     []]


this is roughly what you need to keep track of - your running total, the things that are left, and the things you have done


(loop [running-total 0
       unprocessed   x
       processed     []]
  (if (empty? unprocessed)


ok right


if you have nothing left to process you are done


(loop [running-total 0
       unprocessed   x
       processed     []]
  (if (empty? unprocessed)
    (recur (+ running-total (:p (first unprocessed)))


you want to set the new running total each time through


unprocessed is a collection not a single item


right loop's bindings are like let, and recur like re-maps bindings for the next pass?


which is why this is messy in loop / recur


the best solution i could think of would be to reduce over your collection to get a sequence of the running totals


maybe my approach is the inelegant part


({:p 49 ...} {:p 1 ...} ...) -> (0 49 50 ...)


and then "zip" that with the original collection


([{:p 49 ...} 0] [{:p 1 ...} 49] ....)


because at the end of the day, I want to generate a random number that produces red 49%, blue 49%, green 1% yellow 1%

Russell Mull20:09:07

> always be empowered to fall back on this if you cant find a "beautiful" solution

Russell Mull20:09:11

That is very good advice


then map over that list of tuples to make your final maps


that makes sense


(reduce (fn [acc item]
          (let [prev (peek acc)
                {:keys [start p] prev}]
            (conj acc (assoc item :start (+ p start)))))


the task itself already builds the running total


all the info you need to make the next item is contained in the prev item and next input


Yeah I noticed its similarities to a basic sum


except I want to conj in the running total instead of reducing to a single sum



(def x
  [{:p 49 :v "red"}
   {:p 49 :v "blue"}
   {:p 1  :v "green"}
   {:p 1  :v "yellow"}])

(reduce (fn [acc item]
          (let [prev (peek acc)
                {:keys [start p] :or {p 0 start 0}} prev]
            (conj acc (assoc item :start (+ p start)))))
[{:p 49, :v "red", :start 0}
 {:p 49, :v "blue", :start 49}
 {:p 1, :v "green", :start 98}
 {:p 1, :v "yellow", :start 99}]


@U020G0VEL75 also, regarding your final goal, I made a similar weighted choice script recently


it lets me rank characters for a game, then picks one based on that weighted chance


(map #(assoc %1 :start %2) x (reductions + (map :p x)))

💯 2

This is actually to generate some Savage Rifts NPCs 😄


@U024X3V2YN4 but will that start with a 0?


I think you might need (cons 0 (map ...)) there


Yes, or rather (map #(assoc %1 :start %2) x (reductions + (cons 1 (map :p x)))) . EDIT: I take this back. I was worried about off-by-one errors. You should just make sure that the random picker function has the right probability distribution.


(haha math brain, I see 1 as a placeholder and assume multiplication was specified somewhere)


Ah the way I'm calculating the results its just to sum them, generate a random int in the range and figure out where it lies, that way I didn't have to worry about summing to 100 if I combine some tables


none of this is precision, just fun and games and it seems like doing it this way touched some parts of clojure I've been blissfully ignorant of avoiding state and trying to do things functionally 🙂. Thanks for all your help, much to think about!


@U020G0VEL75 if you look at the code I linked, I basically collapse the vector of hash-maps with :p and :v entries into a hash map mapping each :v to a :p (for brevity if nothing else), and do the same sum operation you do, but I never need a running total - instead I walk the entries subtracting the p for each one, and stopping when I reach a number <= zero


a running total would let me do a binary search, but the startup time of the jvm (not to mention clojure startup time) is orders of magnitude greater than the time I'd save by not linearly searching a list that short


oh sure, my deployment target is a clojurescript webapp, so i'm almost never doing a cold startup to run again, the tool suite is running waiting for me to hit 'generate npc' again 🙂


I love cljs, but boy can debugging it be painful


would running it cmdline lend itself to that.. bubushka or whatever the tool for using clj for shell scripting is called?


babashka lets you do a subset of what jvm clojure can do, with a much shorter startup


it can be helpful to put the "interesting" part of your code in cljc files, so that you can test / repl debug with jvm clojure


both clj and cljs will use cljc if it finds it, so the setup for that isn't hard


or you can go deep into the weird world of js, and use the clojurescript tooling that integrates with that world (but I find that annoying because js moves very fast while improving slowly, and the tooling is a bunch of annoying rube goldberg monstrosities)


I mean I am using shadow-cljs 🙂


I'm also coming from doing typescript / angular dev work mostly so its a runtime Im comfortable with, tho interop is weird, very weird


right, but if debugging is still a pain there's yet more tooling you can hook up to fix that - it's just tedeous half-broken stuff in my experience


I think most of my debugging hell comes into the last mile intersection with react


but I'm going to see how much of taht I can do in cljc side because that does sound better


I've also had some luck with making cljs pages that run clojure.test tests and write output via document.write to an unstyled page


That sounds fun


it's pretty easy - use with-out-str to capture the test output, do a page injection


the hard part is keeping it simple so you can get to the actual task at hand :D


but the yak needs to get shaved eventually!

Rob Haisfield20:09:25

Hey, long time no see! How do I set up my project.clj and ns require function to use a Java library? The actual java interop of writing functions isn’t the hard part, just not seeing much on setting up a project with Leiningen for it

👀 2

in the ns form you can use import to make shorthands for classes, but that's not neccessary in the project.clj you provide coordinates for the artifact containing the java class you need


lein help sample has extensive examples in the form of an example project.clj


look for the lib you need in maven, then translate group foo, artifact bar, version 1.2 to [foo/bar "1.2"]


that particular project maintainer expects you to build and install to your local maven cache instead which is annoying


tl;dr this would be easy, except the people behind that project are being weird


maybe fork their lib and add it on jitpack?


idk thats some wierdness


to be fair its not much code - maybe just translating the http calls you need would be better?


oh yeah, when the tooling side gets weird it's often easier to just not use the lib


    Call<Ping> ping();

    Call<Map<String,Map<String, Double>>> getPrice(@Query("ids") String ids,
                                      @Query("vs_currencies") String vsCurrencies,
                                      @Query("include_market_cap") boolean includeMarketCap,
                                      @Query("include_24hr_vol") boolean include24hrVol,
                                      @Query("include_24hr_change") boolean include24hrChange,
                                      @Query("include_last_updated_at") boolean includeLastUpdatedAt);

    Call<Map<String,Map<String, Double>>> getTokenPrice(@Path("id") String id, @Query("contract_addresses") String contractAddress,
                               @Query("vs_currencies") String vsCurrencies, @Query("include_market_cap") boolean includeMarketCap,
                               @Query("include_24hr_vol") boolean include24hrVol, @Query("include_24hr_change") boolean include24hrChange,
                               @Query("include_last_updated_at") boolean includeLastUpdatedAt);

    Call<List<String>> getSupportedVsCurrencies();

    Call<List<CoinList>> getCoinList();

    Call<List<CoinMarkets>> getCoinMarkets(@Query("vs_currency") String vsCurrency, @Query("ids") String ids,
                                           @Query("order") String order, @Query("per_page") Integer perPage,
                                           @Query("page") Integer page, @Query("sparkline") boolean sparkline,
                                           @Query("price_change_percentage") String priceChangePercentage);

    Call<CoinFullData> getCoinById(@Path("id") String id, @Query("localization") boolean localization, @Query("tickers") boolean tickers,
                               @Query("market_data") boolean marketData, @Query("community_data") boolean communityData,
                               @Query("developer_data") boolean developerData, @Query("sparkline") boolean sparkline);

    Call<CoinTickerById> getCoinTickerById(@Path("id") String id, @Query("exchange_ids") String exchangeIds,
                                           @Query("page") Integer page,@Query("order") String order);

    Call<CoinHistoryById> getCoinHistoryById(@Path("id") String id, @Query("date") String date,
                                             @Query("localization") boolean localization);

    Call<MarketChart> getCoinMarketChartById(@Path("id") String id, @Query("vs_currency") String vsCurrency,
                                        @Query("days") Integer days);

    Call<MarketChart> getCoinMarketChartRangeById(@Path("id") String id, @Query("vs_currency") String vsCurrency,
                                                  @Query("from") String from, @Query("to") String to);

    Call<StatusUpdates> getCoinStatusUpdateById(@Path("id") String id, @Query("per_page") Integer perPage, @Query("page") Integer page);

    Call<CoinFullData> getCoinInfoByContractAddress(@Path("id") String id, @Path("contract_address") String contractAddress);

    Call<List<Exchanges>> getExchanges();

    Call<List<ExchangesList>> getExchangesList();

    Call<ExchangeById> getExchangesById(@Path("id") String id);

    Call<ExchangesTickersById> getExchangesTickersById(@Path("id") String id, @Query("coin_ids") String coinIds,
                                                       @Query("page") Integer page, @Query("order") String order);

    Call<StatusUpdates> getExchangesStatusUpdatesById(@Path("id") String id, @Query("per_page")Integer perPage,
                                                               @Query("page") Integer page);

    Call<List<List<String>>> getExchangesVolumeChart(@Path("id") String id,@Query("days") Integer days);

    Call<StatusUpdates> getStatusUpdates();

    Call<StatusUpdates> getStatusUpdates(@Query("category") String category, @Query("project_type") String projectType,
                                  @Query("per_page") Integer perPage, @Query("page") Integer page);

    Call<Events> getEvents();

    Call<Events> getEvents(@Query("country_code") String countryCode, @Query("type") String type,
                           @Query("page") Integer page, @Query("upcoming_events_only") boolean upcomingEventsOnly,
                           @Query("from_date") String fromDate, @Query("to_date") String toDate);

    Call<EventCountries> getEventsCountries();

    Call<EventTypes> getEventsTypes();

    Call<ExchangeRates> getExchangeRates();

    Call<Global> getGlobal();


just these mechanical calls and java type definitions

Rob Haisfield04:09:29

@U3JH98J4R so what do I do with that code? I’m very much a beginner and these “project details” of java interop are really throwing me.


yeah sorry that might not have been super helpful


clojure libraries like take in a data structure describing how to make and interpret an http request


so for example {:method :get :url ".../ping" :as :json} would describe the ping callyou want to make


so you can make a function like

(defn ping []
  {:method :get
   :url    ".../ping"
   :as     :json})


and then pass the resulting structure to something like (hc/request (ping))


the big code block above shows a bunch of java annotations that describe how to make the requests


which you could reasonably translate to one of those data structure formats


so like getCoinMarketChartRangeById


(defn coin-market-chart-range-by-id [{:keys [id vs-currency from to]}]
  {:method       :get
   :url          (str base-path "coins/" id "market_chart/range")
   :query-params {"vs_currency" vs-currency
                  "from"        from
                  "to"          to}
   :as           :json})


and then

(require '[hato.client :as hc])

(hc/request (coin-market-chart-range-by-id { ... }) 


which is roughly what I would recommend, since the java library is simple enough to recreate (you don't have to pre-validate their api responses and its a fairly short list of things to write) and they are wierdly obtuse about how they are distributing their code

Rob Haisfield05:09:25

Ah okay I understand a little better… Seems like there’s a lot of locations for me to mess stuff up there. I’m sorry, I really appreciate you writing all this out but I’m not sure how confident I feel about creating a new library from HTTP. How would I do it through the jitpack method?

Rob Haisfield05:09:35

For some reason, this isn’t working

Rob Haisfield05:09:57

  (:require [com.rpl.specter :refer :all]
            [com.github.classicrob.CoinGecko-Java :as coin]))

Execution error (FileNotFoundException) at$loading (play.clj:1).
Could not locate com/github/classicrob/CoinGecko_Java__init.class, com/github/classicrob/CoinGecko_Java.clj or com/github/classicrob/CoinGecko_Java.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.


for the jitpack method you need to mae sure to add jitpack as a repository in your project.clj


and to import a class you don't use require


you use :import


(ns your.ns
  (:require [some.dep :as d])
  (:import (com.litesoftwares.coingecko CoinGeckoApi))

Rob Haisfield06:09:02

Ohhh okay I’ll try the :import in the morning. I’m about to go to bed but I’ll let you know if it works. I’ve got jitpack in my project.clj in the way the site said

Rob Haisfield15:09:37

It worked, thank you so much! I’m going to save this whole thread for when I feel more comfortable implementing a new http based lib

Rob Haisfield20:09:38

If it’s helpful, I’m trying to use this Java library