Fork me on GitHub

to "activate" characters someone knows I have (swap! known-kanji union kanji) how can I do a conditional to de-activate a character when someone clicks again?


(swap! known-kanji disj the-char)


Are there any books or blog articles about debugging exceptions within futures, lazy evaluation, anonymous functions etc?


@caleb.macdonaldblack I can't think of anything off the top of my head -- but what's different about those contexts from regular "debugging exceptions"?


Stack traces are limited


For example here points the the first function called but my actual error is deep down the call stack (perhaps not "call stack" due to lazy eval and futures but you get the idea)


I don't get the idea based on that two line screenshot, I'm afraid, no.


Sorry I don't understand if call stack works the same way with lazy evaluation and concurrency as I'm not familiar with the internals of clojure. I'm wasn't sure if "Call Stack" was the right terminology.


But I'll try to find a comparing example


@seancorfield Looks like I might actually be having trouble with the cursive repl and not clojure itself


maybe not even cursive but just the repl


With that snippet. Executing the comment shows an error like my previous screenshot but the line number is the comment code I executed.


Nothing in the that error helps me find out that a is where my error is


In a bare clj REPL, I get this:

user=> (c nil)
Execution error (NullPointerException) at user/a (REPL:1).


And the full stack trace is available via *e showing this

user=> *e
#error {
 :cause nil
 [{:type java.lang.NullPointerException
   :message nil
   :at [clojure.lang.Numbers ops "" 1068]}]
 [[clojure.lang.Numbers ops "" 1068]
  [clojure.lang.Numbers divide "" 186]
  [clojure.lang.Numbers divide "" 3877]
  [user$a invokeStatic "NO_SOURCE_FILE" 1]
  [user$a invoke "NO_SOURCE_FILE" 1]
  [user$b invokeStatic "NO_SOURCE_FILE" 1]
  [user$b invoke "NO_SOURCE_FILE" 1]
  [user$c invokeStatic "NO_SOURCE_FILE" 1]
  [user$c invoke "NO_SOURCE_FILE" 1]
(more stuff elided)


So that shows it tried to do a divide on nil inside a called from b called from c


Yea I get the same in my repl too.


So it looks like an issue I'm having with the cursive REPL then


I'll take this over to the cursive channel


Some tools try to be "helpful" about stacktraces but they're usually not actually helpful.


Yeah true. Thanks for the help. *e is neat. I didn't know about that


With anonymous functions, you get the entire usable stacktrace but each anonymous function is compiled to a uniquely named class so that can be confusing at first. One trick there is to give your anonymous functions meaningful names, such as (fn my-func [x] ...) instead of just (fn [x] ...) and then you'll see my_func in the stacktrace. But I think it's better just to get used to reading the stacktrace as-is without doing that.


With futures, you "lose" the exceptions altogether unless you are specifically try/`catch`ing them -- but if you catch them, you'll have the full stack trace from the top of that thread. I use com.taoensso/timbre as an aid there: it has a logged-future macro that is a drop-in replacement for future but it will log any exceptions (without you needing to do anything else). Again tho', use it only if you need to -- the taoensso libraries drag in a bunch of stuff and you're really better off with plain ol' Java logging if you really want logging.


And with lazy evaluation, the "gotcha" is that the exception occurs when a collection is realized which might be some distance away from where it was created -- but given the stacktrace, you'll know what was being realized so you can usually trace back to where it was created and debug that by forcing realization of the full collection (if it isn't infinite) by adding doall just while you figure it out.


In all of these cases, familiarity and experience mean you need these debugging aids less and less.


Thanks that's very helpful. My misconception was the clojure was not able to provide those stacktraces. But now I know it can.


For some reason loading code into the cursive repl doesn't give me any information in the exception. But running that code directly in the repl does.


Thank god. This has been painful.


I would expect, even in Cursive, that you can still retrieve the last exception in full somehow...


how would you add clojure and clojurescript to a php project?


clojure as a new microservice. clojurescript wherever you use javascript (like a SPA served by your php backend)


I have no microservices yet...


its a big php monolith


but thanks a lot, I have been reading Microservices with Clojure


how would you add it to the app then? when and how to call clojure? use same DB?


yes, you could do that. if you want a new service in your php monolith, you could try creating a microservice in clojure for it instead of adding it to the monolith.


how would that look? how to add a service in clojure and make iot available to monolith in php?


an example would be new REST endpoints. the clojure microservice would only interact with the DB; the php monolith wouldn't need to be aware of it


Why do you want to add clojure to a PHP project? For a particular reason? Or just cause its cool and to try something new? If it's to try something new, maybe try it on a new project instead.


thx for the responds. the reason is that the php monolith is used in production. I want to add new features in clojure and clojurescript without touching the core scripts for now. comparable to how visa and mastercard are still running the credit card transaction trough cobol code


its just such a pain to expand the monolith


@jarvinenemil thanks I will read that book too


so you all recommend to setup a microservice infrastructure?


my clojurescript app doesn't see my min build ;(


"what how do i min what's that????"


oh it's lein cljsbuild once min


@abdusalam ^^ thanks for your help, it'll take time to get all the kanji up there xX 😄

🎉 4

looks very nice!

😉 4
Mario C.15:08:37

I am using checkouts in a lein project. Is there a way I can "reload" a dependency without having to restart my REPL?


@mario.cordova.862 require with :reload or :reload-all?

Mario C.16:08:23

How would I use it?


(require '[my.namespace :as alias] :reload) which will force my/namespace.clj to be reloaded (and recompiled)


(require '[my.namespace :as alias] :reload-all) will reload all the namespaces that my.namespace depends on as well, recursively.

👍 4
Mario C.17:08:00

@seancorfield That worked! Thanks! I wonder why this is not in any guide/docs?

Mario C.17:08:14

They all say to restart the repl


(doc require) has the info. I wish i would have used doc, apropos, dir, find-doc as a beginner more. There's a wealth of information in these. For instance the docstring of require has >>> Flags A flag is a keyword. Recognized flags: :reload, :reload-all, :verbose :reload forces loading of all the identified libs even if they are already loaded :reload-all implies :reload and also forces loading of all libs that the identified libs directly or indirectly load via require or use :verbose triggers printing information about each load, alias, and refer


and interestingly, the docstring of require has a version that's kinda shunned these days (require '(clojure zip [set :as s]))


@mario.cordova.862 Well... there are some caveats about reloading code like that (multimethods, for example) as well as old definitions staying around (e.g., if you rename a function, both versions stay loaded). I think a lot of the "restart the REPL" and the push for tools.namespace for "refreshing" projects dates back to a time before people had really figured out a good, healthy REPL-driven development workflow.

👍 4

Stu Halloway has a couple of great talks on that and Eric Normand has an entire online course about RDD (which I highly recommend to everyone -- it is totally worth the price!).


I tend to start a REPL and leave it running for days on end, just eval'ing every code change I make often before even saving the file, since I have a hot key for "evaluate top-level form"... make a small change in a function, ctl-, B, move on to the next change without even saving the change. The key is to evaluate every small change as you make it, always. And to not be afraid to have (comment ,,,) forms with scratch code in them that you can eval into the REPL to verify your code changes as you go along.


I (almost) never type into the REPL -- I eval code from files (either src files, test files, or from a (comment ,,,) form inside one of those for scratch code).

David Pham17:08:48

Not specific to Clojure or Clojurecript, but what is the common way of designing API with Clojure(Script) for communication over the wire? As I am a beginner I am strongly biased towards RPC type of design because it seems to me I can just call function in my backend and the backend returns me the appropriate answer.


I find it is a lot about just adapting functions to whatever medium the api is exposed over


and it depends a lot if you are all clojure or have a mix of clients


the one place "just do rpc" can have issues is your api needs to push events to clients

David Pham17:08:43

I really need to find a book in CLJS teaching all this haha


with clojure it is super easy to end up rolling a custom rpc format, but then, how do you document it, how do you explain how it works to clients, etc


I wrestled with this a bit on a chat server for work, my first pass of the api was sort of messaging passing, not really rpc over websockets, because internally the chat server was mostly message passing, the format of the messages defined in json-schema (generated from clojure specs)


but the json-schema described the data of the messages very well, but didn't do a good job of capturing the interactions and back and forth of the api


it also didn't do a good job distinguishing between async events and the rpc like part of the api, so I threw it out and we ended up going with graphql over websockets


the graphql schema does a good job of describing rpc like operations and async operations and the data formats


Hi, I have a question regarding js/cljs-interop. How can I translate the ... js-operator to clojurescript? js below

await Font.loadAsync({
      Roboto: require('native-base/Fonts/Roboto.ttf'),
      Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
cljs - missing the ionicons.font:
(defonce font-roboto (js/require "native-base/Fonts/Roboto.ttf"))
(defonce font-roboto-medium (js/require "native-base/Fonts/Roboto_medium.ttf"))

(font/loadAsync {:Roboto font-roboto
                   :Roboto_medium font-roboto-medium}))

David Pham17:08:16

What do you mean RPC like operation?


call and response I guess


looks sort of like a function call

David Pham17:08:23

Thanks! The web world is messed up compared to the world of data science.


a clojure map is not a js object like that


yeah I need to add #js ?


cljs keywords are not strings either

David Pham17:08:02

You could try to call the assign operator from JS?


yeah Object.assign should do it


for the ... part?


{foo: bar ...baz} becomes Object.assign({foo: bar}, baz)


in cljs that should be (js/Object.assign #js{:foo bar}, baz) right?


not sure but i'll know in a second.. the repl is 🔥


I just remembered I have lumo

(cmd)cljs.user=> (def baz #js{:bar 1})
(cmd)cljs.user=> (def foo #js{:baz 1})
(ins)cljs.user=> (js/Object.assign foo baz)
#js {:baz 1, :bar 1}


worked like a charm, thanks for the help guys 🙂