Fork me on GitHub
Alex Miller (Clojure team)00:04:47

Or you could just make the function to call configurable and not call it at all


What would be an idiomatic way to do memoization-type dynamic programming in Clojure, where it seems like mutability would help? For example the coding example on

There's a staircase with N steps, and you can climb 1 or 2 steps at a time. Given N, write a function that returns the number of unique ways you can climb the staircase. The order of the steps matters.

For example, if N is 4, then there are 5 unique ways:

1, 1, 1, 1
2, 1, 1
1, 2, 1
1, 1, 2
2, 2
What if, instead of being able to climb 1 or 2 steps at a time, you could climb any number from a set of positive integers X? For example, if X = {1, 3, 5}, you could climb 1, 3, or 5 steps at a time. Generalize your function to take in X.


The python solution is pretty crisp:

def staircase(n, X):
    cache = [0 for _ in range(n + 1)]
    cache[0] = 1
    for i in range(1, n + 1):
        cache[i] += sum(cache[i - x] for x in X if i - x >= 0)
    return cache[n]


there's clojure.core/memoize


I had a go using sequences and reduce, but not sure if it's more or less readable

(defn staircase [n segments]
  (letfn [(valid-paths [cache i]
            (->> (map - (repeat i) segments)
                 (filter #(<= 0 %))
                 (map cache)
                 (reduce +)
                 (conj cache)))]
    (last (reduce valid-paths [1] (range 1 (inc n))))))


@noisesmith Yeah I know about memoize, but you'd need to make it a function call, whereas here the technique (or at least python equiv) is just a simple mutable vector without the overhead of recursion


actually, to me that staircase function from python looks precisely like a lazy-seq


That was exactly my first thought, but then you'd get O(n) lookup performance when you needed to reference the earlier elements for building each new one


but you always read them start to end in order anyway


one moment, I'll try a lazy-seq version that uses an accumulator instead of linear walks...


Many dynamic programming techniques are effectively like memoization, but they fill in some kind of a vector, 2d array, etc. in a particular order, such that whenever you are calculating the value to fill in one entry, all subproblems have been calculated earlier, and can be retrieved from the saved vector/2d-array/etc.


In such a case, doing any kind of loop/reduce/etc. where one of the loop values (or values accumulated from one reduce step to another), is that vector/2d-array/etc.


That need not be considered mutation any more than the rebinding of a loop variable's value from one iteration to the next is (which means, not mutation at all, except perhaps in the underlying machine code implementation)


@U0CMVHBL2 Thanks, makes a lot of sense! I settled on the reduce implementation on similar thinking, though it did seem that the machinery around needing to setup a function/direct the flow of implicit returns sometimes ends up being more verbose than a side-effecting python assignment. Was just wondering if there were more elegant approaches to tackle this domain of problems


I have not looked at the particular Python solution proposed, but a single mutating assignment to a Python dict can become a single call to assoc on a Clojure map, for example


(defn steps-dynamic [n]
  (let [cache (merge (zipmap (range (inc n)) (repeat 0))
                     {0 1})
        cache' (reduce (fn [cache n']
                         (update cache n' + (apply + (vals (select-keys cache (take 2 (reverse (range n'))))))))
                       (rest (range (inc n))))]
    (get cache' n)))

(defn steps-tail-recursive [n]
  (letfn [(iter [x prev prev']
            (if (= x n)
              (+ prev prev')
              (recur (inc x) (+ prev prev') prev')))]
    (iter 0 0 1)))

(defn steps-recursive [n]
  (cond (< n 0) 0
        (zero? n) 1
        :else (+ (steps-recursive (dec n))
                 (steps-recursive (dec (dec n))))))


aren't those missing the X arg?


i got confused about that but i think X is #{1,2}


for "climb 1 or 2 steps at a time" but i'm honestly not sure what X is otherwise


> What if, instead of being able to climb 1 or 2 steps at a time, you could climb any number from a set of positive integers X? For example, if X = {1, 3, 5}, you could climb 1, 3, or 5 steps at a time. Generalize your function to take in X.


from the link

👍 4

seems like you could use for with X as an inner comprehension to get every combination of steps


My solution renamed X to segments, but yeah, I tried the for loop approach initially to mirror the python code but it was pretty unwieldy to slot into a large reduce, ended up going all sequence functions. Would be cool to see a really crisp clojure implementation that runs just as fast


@U11BV7MTK Those look nice for the #{1 2} case! Though that particular case does devolve exactly into the Fibonacci sequence


You can just use a cache the same way. That's how memorize is implemented anyways

Ben Sless09:04:04

Generally, a naive and non optimal solution would be (this is for 1,2,3 steps):

(defn ways
    (< n 0) 0
    (= n 0) 1
    :else (+ (ways (- n 1))
             (ways (- n 2))
             (ways (- n 3)))))
You can trivially cache it with memoize:
(declare ways)
(declare ways*)
(def ways (memoize ways*))
(defn ways*
    (< n 0) 0
    (= n 0) 1
    :else (+ (ways (- n 1))
             (ways (- n 2))
             (ways (- n 3)))))
Both of these are easy to generalize to any number and size of steps If you want to do it with tail recursion, you can create a sliding binding:
(defn dways
    (<= n 0) 0
    (= n 1) 1
    (= n 2) 2
    (= n 3) 4
        [a 1, b 2, c 4, i 3]
      (if (< i n)
        (recur b c (+ a b c) (inc i))
I think if you want to parametrise that over the possible steps you might need a macro


has anyone had experience with Ring to inspect an incoming request? i am able to get the body but it is in an input stream and i would like it in a map. essentially what im trying to do is intercept an incoming request to do a type conversion if an ID is present


intercept a request between what and where? if you have an input stream, you can use slurp to read the full inputstream into a string, but you’ll need to parse the string if you want a map.


so my angular app is making a POST request. when the request hits the server i want to intercept this request before it hits my controller / routes in order to do the modification


does that make sense?


or maybe this is not possible


and i have to do the modification in each route


this sounds like a job for middleware!


correct i am trying to create one but i am unsure where


at this level i get the :body as an input stream which i am unsure if this is the right place


(defn wrap-database-component [handler database]
  (fn [req]
    (handler (assoc req :database database))))

(defn wrap-uuid-type [handler]
  (fn [req]
    (handler req)))

(defn routes-handler [database]
  (-> routes/define-main-routes
      (wrap-database-component database)


seems reasonable


wrap-uuid-type is what im trying to create. i have come across (`request/body-string)` ring util but since it is just a string i dont think it makes sense to do that. would i convert the input stream here into a map and modify the type then?


does it have to be converted back to an input stream? i am unsure and maybe trial and error is needed


let me see...


typically, you just add more data to the request map to help later handlers. something like:

(defn wrap-uuid-type [handler]
  (fn [req]
    (handler (assoc req :uuid (make-uuid)))))


right i was hoping i didnt have to push the logic down more cause then i would have to copy-pasta for each route to do the modification on the :id key


shouldn’t it add the uuid to the request route for every handler/route in define-main-routes?


well i dont want to add a key called :uuid i want to convert the id key in the incoming json to type of uuid


well, there’s two options: 1. add a new key of uuid that is the conversion:

(defn wrap-uuid-type [handler]
  (fn [req]
    (handler (assoc req :uuid (extract-uuid (:body req)))))
2. replace id in the actual body
(defn wrap-uuid-type [handler]
  (fn [req]
    (handler (assoc req :body (convert-uuid-in-body (:body req)))))
I would typically recommend the first, but there’s still a lot I don’t know about the use case. is the response body json or some other format?


option 2 is what im attempt but intercepting the incoming POST request body to make the conversion seems to be way more problematic than i thought


the response body is json


do you have middle ware that parses the json?


that seems to work out of the box with ring


for incoming requests


my responses are wrapped with this middleware


(defn wrap-routes
  (-> routes
      (wrap-json-body {:keywords? true})
      (wrap-cors :access-control-allow-origin [#""]
                 :access-control-allow-methods [:get :put :post :delete])))


let me know if i am not making sense haha


you’ll probably want your conversion middleware after the wrap-json* stuff


im pretty bad at trying to take what is in my head to paper


but ^ only fires when i do a response


not on the incoming request


i think?


im pretty sure. let me triple check


my understanding is there are two middleware locations: for incoming requests and outgoing responses


im so stupid


thank you so much @smith.adriane


i was in the wrong place the WHOLE time loL!


actually i guess they are the "same"


not sure why i have two different places :thinking_face:


it’s been a while since I’ve messed with middleware, but I think you can wrap before, after or both with any middleware


interceptors are a popular alternative to middleware and I think interceptors are opinionated about before and after


the body still seems to be a stream at this point which i hope the wrapper would have converted it


:body #object[org.eclipse.jetty.server.HttpInputOverHTTP 0x1a637ea3 HttpInputOverHTTP@1a637ea3[c=0,q=0,[0]=null,s=STREAM]]


it seems to only replace the body if the content-type header matches #"^application/(.+\+)?json"


correct i just checked the docs and checked the request and the content-type is there


and the body is converted to json by the time it hits a normal handler later?


triple verifying


i’m not sure where wrap-uuid-type is specified in your middleware list, but I would try it right before wrap-json-body and if that doesn’t work, try right after


i always forget which order things need to go in 😬


i tried after the wrap-json-body and no dice let me see before


cause this is how it looks in my normal handler


:body {:id c0c03eff-9870-4c0e-83ac-506bf59d69cf, :first Yusuke, :last Urameshi, :email }


ok it works before!


im pretty lost why it does that


i thought -> threads first?


and goes in order


nope it does not LOL


man im so newb at this


it’s super counter intuitive




i wonder what is the reasoning behind that


because -> does go in order, but something about how the wrapping works makes it go in the other order


i’ve reasoned through it before, but I usually just try one and then try the other if it doesn’t work


ohhh shit


i got it now


the docs give a good visual


but there’s one extra level because you’re creating functions that return functions that call the next function


it’s the same thing with transducers


this is a mind trip


i feel my brain growing


like if you do (-> 10 (+ 1) (* 2)) you get 22


im back to being confused lol


cause this does (10 + 1)


then 11 * 2


but thats the opposite of what is happening in my thread macro


the thread macro is doing the same thing


(defn wrap-uuid-type [handler]
  (fn [req]
    (handler (assoc req :body (convert-uuid-in-body (:body req)))))
so if you’re doing (-> (wrap-json) (wrap-uuid-type))


wrap-uuid is creating a function that’s going to call wrap-json


so wrap-uuid is the “outside” wrap


and it will call wrap-json which will call the next inner layer


visually it would look like (wrap-uuid-type (wrap-json)) ?


thats the opposite of the docs it seems like no?


;; Arguably a bit cumbersome to read:
user=> (first (.split (.replace (.toUpperCase "a b c d") "A" "X") " "))

;; Perhaps easier to read:
user=> (-> "a b c d" 
           (.replace "A" "X") 
           (.split " ") 


maybe docs are wrong?


the docs are right


cause if it was (wrap-json (wrap-uuid-type)) that doesnt make sense


cause the json has to be wrapped before the wrap-uuid takes place


remember, there’s 4 functions


1. wrap-json, 2. wrap-json’s inner function. 3. wrap-uuid-type and 4. wrap-uuid-type’s inner function


so wrap-json gets called first, but wrap json’s inner function gets called last


and it’s worth noting the inner function doesn’t get called when you create the route, but the inner functions get called every time a request comes in


i’ve been told that I’m not good at explaining things so don’t feel bad if I’m not making any sense


this is wild and im still confused however i have made a note about this to do some debugging to better understand it


no you are doing an awesome job


and helped me figure out my problem 🙂

bananadance 4

thank you for taking time out of your day to rubber duck with me and share your knowledge with me

👍 4

i noticed ring has this helper method request/body-string but thats about it 😞


bubbling this to the top but big s/o to @smith.adriane in the thread above. thank you again and thank you for this community!

Phil Hunt11:04:46

Hi, I'm trying to use to work with a file. The file is definitely there because I can slurp it directly, but when I try to use (def something (io/resource "filename")) it evaluates to nil.


Did you verify the file is in your classpath? Slurp can work on any file I think

Phil Hunt11:04:14

slurp works fine, it's the io.resource bitt hat's not working


Yes, I meant slurp can possible pick the file up even when it's not in classpath.

Phil Hunt11:04:21

so I'm failing to understand something about classpaths. Common Lisp was so much less confusing 🙂


If it’s in the resources folder it should be in the classpath though

Phil Hunt11:04:31

do I need to put in my deps.edn and restart the REPL or something?

Phil Hunt11:04:09

Or maybe go read more about Java classpaths 🙂


A classpath is kind of like the Linux path. Just like an executable in PATH is magically found, Java will find and access code/resources from the classpath.


No, is part of core. It will come along with Clojure.


So, the deps.edn file will be used to construct the classpath.


All the dependencies will be in the classpath.


You can use this: as a dev dependency and print out the classpath.

Phil Hunt11:04:15

OK so my issue is there someplace


Alternatively, just build the uberjar and see what's inside.


Yeah looks like your resources directory isn’t getting added to the classpath somehow, I’d have expected that to be a default (but I’m mostly using leiningen, not really up to speed on deps stuff)

Phil Hunt11:04:27

;; debugging classpath issue with io/resource (require '[ :as cp]) (cp/classpath)


Can you share your deps.edn?

Phil Hunt11:04:26

{:paths ["src" "test" "resources"] :deps {org.clojure/clojure {:mvn/version "1.10.1"} metasoarous/oz {:mvn/version "1.6.0-alpha6"} org.clojure/data.csv {:mvn/version "1.0.0"} semantic-csv {:mvn "0.2.1-alpha1"} }


Did you start your REPL in the same directory as the deps.edn?


Because I just tried, and I get this,

(#object[.File 0x32aa7f46 "dev"]
 #object[.File 0x74fb4367 "classes"]
 #object[.File 0x5f17bffe "src"]
 #object[.File 0x704b5a50 "resources"]

Phil Hunt11:04:48

just restarting everything

Phil Hunt11:04:37

there was an issue with that semantic-csv dependency

Phil Hunt11:04:21

still fiddling, but thanks for the help so far

Phil Hunt11:04:07

Do you get a lot of people struggling with the Java underneath? I find it quite disorienting


If Clojure is their first exposure to Java, yeah, people do have struggles sometimes


Of course, they have the same struggles if Java is their first exposure to Java. It’s just more expected there. 😉

😅 4
Phil Hunt11:04:59

OK sorted thanks guys

Phil Hunt11:04:10

it was likely something to do with the bad dependency

Phil Hunt11:04:20

I guess the horrible debug experience compared to Common Lisp is the price paid for interop.

Phil Hunt11:04:42

Probably worth it if I want anyone to let me do this in prod though 🙂

Phil Hunt11:04:19

Thanks for your patience guys 🙂


It might be a problem with the relative path, is it inside a resources folder in your project root?


what relative path are you using?

Phil Hunt11:04:04

path is /resources/data/myfile


Ok, so slurp will take a path like resources/data/myfile, but io/resource just takes data/myfile (iirc)

Phil Hunt11:04:02

I'm doing (require '[ :as io]) then (io/resource "data/myvile)

Phil Hunt11:04:27

yeah, if I add the resources bit I can slurp just fine


Hmm, ok, that should work, I think, let me double check my own code

Phil Hunt11:04:43

but io/resource just gives me nil

Ory Band12:04:09

hi. is there a dedicated channel for all online meetups that might be happenning for clojure here? #remote-meetup history is empty

👍 4
Ory Band15:04:10

No. There are some for local meetups like #clojure-uk


If I wanted to build a REST API what's the go to technology? I've worked out that ring is the defacto choice for HTTP but I still need to pick things on top of it. There's a list in the wiki but most are outdated. Compojure seems relevant:


Luminus has a template that implements swagger out of the box. I’m learning that this kind of thing seems to be what Clojure’s web stacks are aimed at, and less at routine CRUD + progressively enhanced server-side rendering stuff that would probably be better off done with rails.


Thanks. Luminus looks like it has a lot of answers for most web development needs... quite a few steps above these other libraries. I might keep it simple and stick to the reitit and find a problem to solve then build on things from there

practicalli-johnny09:04:52 is a project template for building an API in Clojure


but I've also come across Pedestal


What else is relevant? How can I find relevant libraries? I've found but the categories are wide


I think if you’re familiar with compojure you’ve got a good basis for looking at the other alternatives. I’m using reitit right now, and I like it, but it hasn’t been around as long as compojure


On the plus side, it does have built-in support for swagger, which could be helpful in building a REST API.


Thanks -- is knowing or finding these just a matter of experience?


You ask, you pay attention, look at what others are using, etc?

Jose Ferreira15:04:24

I'm trying to create a datascript connection, but i always get the error no namespace d.

Jose Ferreira15:04:31

What am i doing wrong here....

Jose Ferreira15:04:35

(ns cs50-proj1.core
  (:require [ring.adapter.jetty :as webserver]
             [ring.handler.dump :refer [handle-dump]]
             [ring.util.response :refer [response]]
             [ring.middleware.reload :refer [wrap-reload]]
             [compojure.core :refer [defroutes GET]]
             [compojure.route :refer [not-found]]
             [datascript.core :as d]))

(def conn (d/create-conn))


how are you loading the namespace? have you reloaded since adding the datascript dependency?

Jose Ferreira15:04:35

i just added it as a dependency in my

Jose Ferreira15:04:51

(defproject cs50-proj1 "0.1.0-SNAPSHOT"
  :description "a book database exercise from CS50"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [ring "1.8.0"]
                 [compojure "1.6.1"]
                 [hiccup "1.0.5"]
                 [datascript "0.18.11"]]
  :repl-options {:init-ns cs50-proj1.core}
  :main cs50-proj1.core
  :profiles {:dev
             {:main cs50-proj1.core/-dev-main}})


you need to restart your entire repl if it's new in project.clj


(and reloading the namespace should hve errored)


project.clj "doesn't exist" in a repl - it's a config that decides (among other things) how a repl is started

👍 4
Jose Ferreira15:04:16

ok, thats the main keypoint...(how its started)

Jose Ferreira15:04:45

any changes i make in the


not just reload - restart


I only clarify that because there's things like (require 'some.ns :reload) and (wrap-reload) that are about seeing new changes to your own project source files, but those don't make project.clj changes visible


@jose.ferreira.ptm2 There are tools that let you load new dependencies into an already running REPL so you can avoid that restart -- but they are not exactly beginner-level tools and have several caveats.


If you use Boot, you'll find that it has built-in functionality to load new dependencies into its REPL. That functionality is also available for CLI/`deps.edn` but relies on a branch of tools.deps.alpha (this is what I use for adding new deps while I'm working). I expect there are plugins for Leiningen, or some code you find online that would expose the dependency-loading machinery, so you could also do it with lein repl but I haven't used Leiningen for years now so I'm not up to date on the latest options there.


I have recently found Pure Clojure Webdriver protocol implementation and it just blew my mind. I always hated writing(and maintaining) Phantomjs/Selenium tests because it is rather hard to write & debug them in traditional(non-lisp) languages. But having real-time access to drive such specs from IDE/REPL is so much easier. The higher the abstraction level of spec the more benefit you get from REPL driven development. Clojure definitely sparks joy


We went with clj-webdriver because that was the most mature years ago when we started writing "browser-level" tests but it's no longer maintained so I've looked at etaoin a couple of times and it does look really good.


At this point tho', our front end team maintain their own tests and everything's in JS there, and our webdriver test suite is shrinking as we convert legacy apps to JS/Clojure apps (and so the front end team takes on more of that work).


We also use HtmlUnit for one application and drive that from Clojure. I haven't spent much time with those tests but, on the surface, etaoin looks much nicer.


I hope the author of etaoin wouldn't lose the interest in maintaining it because all those "bug reports" in Github issues could be really discouraging. For some reason the author ignored donation offers


Yeah, that's always a risk with projects such as this -- and often it's not so much a loss of interest but more often employment changes and the maintainer ends up using a different tech instead.


I have given up maintaining a number of projects in the tech I used before Clojure. I maintained them for a few years after I moved on, but it gets harder and harder when you're no longer a user of your own projects.


It's how I ended up maintaining CongoMongo, clj-time, and HoneySQL -- the original maintainers moved on but I was using those projects and had a vested interest in maintaining them. Then we stopped using MongoDB at work so I stepped away from CongoMongo. Joda Time (what clj-time is based on) has been deprecated so clj-time is also deprecated (so my maintenance work there is nominal, and we're actively moving away from clj-time at work).

Jose Ferreira16:04:33

@seancorfield I'm using leiningen for now. It's good to know that there are ways to 'hot reload' deps, but for now, it's really not a problem for me. I'll keep things slow to not get to much overwhelmed...^^

Jose Ferreira16:04:30

I've noted that the community seems to be parting from Leiningen in favor of CLI

Jose Ferreira16:04:14

do you feel this is the case? should i start using CLI as well? are they exact matches, or is there an advantage to lein in some cases?


I went from lein to boot (2015) to CLI/`deps.edn` (2018) and I prefer the simplicity of the latter. lein is definitely easy but it is not simple. Given that CLI/`deps.edn` is the official offering for running Clojure programs, and is well-documented on and well-supported, I think anyone new to Clojure should learn it.


But the vast majority to books and tutorials will remain based on Leiningen for a long time since it was the first tool for running Clojure.

Jose Ferreira16:04:24

I'll take a look at CLI/deeps.edn for the next project i start

Jose Ferreira16:04:17

if its the official way, eventually everything will gravitate towards that


Could anyone help me print the error message from the try catch block on the else clause?

(defn h [] 
 (if-let [k (try (/ 1 0) (catch Exception e   nil))] 
   (+ k 1) 
currently if i try to run this bit i get Unable to resolve symbol: e in this context, which i guess is expected since the namescope would be in the try block


e is only accessable inside the catch block, maybe you want to return it, or wrap the entire body and not just the division and return e there


for example (try (+ (/ 1 0) 1) (catch Exception e e)) (where I assume in real code (/ 1 0) is something more interesting using local bindings)


or (let [k (try (/ 1 0) (catch Exception e e))] (if (number? k) (+ k 1) k)


thanks, i think iw as too fixated on using if-let


Clojure doesn't attach metadata to fn calls so that I could see the caller does it?


no, but you can look at the call stack

user=> (seq (.getStackTrace (Thread/currentThread)))
([java.lang.Thread getStackTrace "" 1606] [user$eval147 invokeStatic "NO_SOURCE_FILE" 1] [user$eval147 invoke "NO_SOURCE_FILE" 1] [clojure.lang.Compiler eval "" 7177] [clojure.lang.Compiler eval "" 7132] [clojure.core$eval invokeStatic "core.clj" 3214] [clojure.core$eval invoke "core.clj" 3210] [clojure.main$repl$read_eval_print__9086$fn__9089 invoke "main.clj" 437] [clojure.main$repl$read_eval_print__9086 invoke "main.clj" 437] [clojure.main$repl$fn__9095 invoke "main.clj" 458] [clojure.main$repl invokeStatic "main.clj" 458] [clojure.main$repl_opt invokeStatic "main.clj" 522] [clojure.main$main invokeStatic "main.clj" 667] [clojure.main$main doInvoke "main.clj" 616] [clojure.lang.RestFn invoke "" 397] [clojure.lang.AFn applyToHelper "" 152] [clojure.lang.RestFn applyTo "" 132] [clojure.lang.Var applyTo "" 705] [clojure.main main "" 40])

👍 4

I am debugging something and was hoping to use the metadata to determine the callers


Callers can attach metadata using with-meta


That's a good idea, thanks


Is the best way to run tests (using clojure.test) if you're using deps.edn?


it's one option. another option that you can consider is

👍 4

what specific object do you have in mind, when you talk about attaching metadata? a function call evaluates to some result, which may or may not be able to support metadata being attached


Yeah it needs to extend IObj


Do you think the thread will contain the callstack?


I don't understand the mechanics you are looking for either - would it be adding metadata to a function argument, to a function return value?


the thread always knows the current call stack, as I demonstrated above


you'd need to do a bit of parsing / interpreting to get the real info, but it's all there


(if the thread didn't know the call stack, your function wouldn't be able to return to its caller)


Mostly the things you can add metadata to are collections or symbols. (More precisely, any objects that implement the IObj interface, if I recall correctly, but most of those are the Clojure collections and symbols. Metadata cannot be added to much else, but that may be all you need.)


Trying to squash a reflection warning on .getTimeInMillis (from java.util.Calendar) but adding a type hint isn't allowed because obviously Can't type hint a local with a primitive initializer. Any suggestions for getting rid of the reflection?


what did you put the hint on?


the local var bound to the calendar value


You should paste that code with hint


Sure thing. thanks:


that should be fine

kcmds)user=>(#(.getTimeInMillis %) (java.util.Calendar/getInstance))
Reflection warning, NO_SOURCE_PATH:1:3 - reference to field getTimeInMillis can't be resolved.
(ins)user=> (#(.getTimeInMillis ^java.util.Calendar %) (java.util.Calendar/getInstance))


Whoops. sorry. it was messed up the in the first paste


that error should go with a primitive, I don't see how it would apply to what you are doing there


when I attempt to eval a function in my buffer it throws with that error message:


while it evals fine without the java.util.Calendar hint


are you sure the error isn't related to the Long hint?


that's the only hint I see there that clojure would derive a primitive from, and the compiler claims the error is related to a primitive


Well now that's interesting. It will compile if I remove either of those type hints. So it works fine if only the ^Long type hint is there. Same with ^Calendar. Oh well, since ^Long can be inferred I'll remove it. Strange that it wouldn't throw that exception when only the Long hint is there though, no??


it could be that it would prefer to compile a primitive long rather than a boxed Long so the inference on the method and your annotation are in conflict


Interesting! Thanks @noisesmith!!



(let [time ... extract and hint Calendar
      msec ... get millis and hint])


the hint would cause a runtime crash if the types didn't match, but no compilation error


@U011LEAEURE Thanks for the suggestion, but the issue is at eval time, not run time. The entire form fails to evaluate if both hints are present, and works if just one or the other is present.


Just so y'all don't think I'm crazy 😂 :


You dont need ^Long hint since .getTimeInMillis returns primitive long... compiler knows that because you specified ^java.util.Calendar

🙏 4
💯 4

Thanks, yeah. I've read that SO entry. The thing I couldn't figure is that it doesn't complain about ^Long by itself unless ^Calendar is also there. But @noisesmith gave a good suggestion on that front. Tks!


Feels like a "doh" moment. I should have realized this facepalm Thanks again, to both of you.


I've never had this problem... Not hinting much 😛

Kurt Sys20:04:00

I'm having a go-loop which works fine, and which has an and-condition. However, when I run from the command line clj -m hosa , the process doesn't terminate:

(defn start-capturing [message-ch]
  (let [...
    (go-loop [wait-seconds (rand-int 5)]
      (let [action (alt!
                     (timeout (* 1000 wait-seconds)) :timeout
                     message-ch ([m] m))]
        (let [...
          (if (= action :stop)
            (do (println "stop!")
                (async/close! message-ch))
            (recur (rand-int 5)))))))
  (println "loop started"))

(defn -main [& _]
  (let [message-ch (chan)
        stop-fn #(>!! message-ch :stop)]
    (start-capturing message-ch)
      (async/<! (timeout 30000))
So this will so some stuff (I removed the uninteresting parts with ... ) every max 5 seconds. After 30 seconds. It should stop, but the process doesn't terminate. What am I missing?

Kurt Sys20:04:27

oh well, just adding (System/exit 0) works fine. Not sure if that's a proper way, though.


if you use clojure's built in thread pools you should use shutdown-agents


though calling System/exit explicitly is a workaround


(nb. don't use shutdown-agents unless you are trying to make the vm exit, it will cause strange errors otherwise)

Kurt Sys20:04:12

ok, thx... What is preferred?


I prefer a call to System/exit personally.

👍 4

there are two different operations here - System/exit means you want the vm to exit, and don't care what other threads are doing right now


shutdown-agents means that the vm can exit once all non-daemon threads (including futures / go blocks etc.) exit, but nobody is forcefully shut down


if you don't use shutdown-agents, you'll still get an exit after 30 seconds, as seen above, it's just that that timeout passes before the threads are fully reclaimed so exit can happen


Yes, that's true. shutdown-agents will not exit immediatly, it will wait for all tasks already sent to the threads to complete first, while System/exit will quit immediatly


So it depends what you want

Jack Kiernan23:04:31

Hey I was curious curious if anyone could tell me the advantages to learning clojure over java? Trying to figure out what to learn. Do I need to learn Java first to learn Clojure? Does Clojure have disadvantages to Java? I know a little Python already (I know they're not related just providing context to my knowledge)


You do not need to learn Java in order to learn Clojure. It is fairly common when using Clojure for projects, that one will need to know how to read the API documentation for how to use one or more Java libraries that do useful things related to your project.


So at least having a basic working knowledge of what Java means by terms like class, interface, method, primitive types, are good things to know. You certainly don't need to know enough to design a Java class library, or develop a full Java application.


I think an advantage of Clojure over Java is that Clojure is a much simpler language than Java (while still having access to all the Java libraries) and because it has immutable data structures, there are a lot of errors you can make in Java that can't happen in Clojure (especially if you start getting into concurrent code).


A "disadvantage" of Clojure compared to Java is that it's more of a niche language so there are fewer jobs and less documentation/books/etc.

Phil Hunt10:04:29

On the other hand, there are some fantastic books and code examples for ancestors of Clojure, e.g. PAIP, SICP etc.

😲 4

then there's the fact that "simpler" doesn't mean "easier" if you already know languages descended from Algol. Clojure's ancestors are more obscure so learning Clojure is harder if you already know how to program in a mainstream language. People will argue this is a benefit :D


but it's definitely an upfront cost for people coming from mainstream languages