Fork me on GitHub
#beginners
<
2019-08-22
>
sova-soars-the-sora00:08:58

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?

seancorfield01:08:04

(swap! known-kanji disj the-char)

4
caleb.macdonaldblack01:08:39

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

seancorfield01:08:25

@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"?

caleb.macdonaldblack01:08:11

Stack traces are limited

caleb.macdonaldblack01:08:01

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)

seancorfield01:08:48

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

caleb.macdonaldblack01:08:13

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.

caleb.macdonaldblack01:08:24

But I'll try to find a comparing example

caleb.macdonaldblack01:08:13

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

caleb.macdonaldblack01:08:28

maybe not even cursive but just the repl

caleb.macdonaldblack01:08:09

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

caleb.macdonaldblack01:08:33

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

seancorfield01:08:09

In a bare clj REPL, I get this:

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

seancorfield01:08:44

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

user=> *e
#error {
 :cause nil
 :via
 [{:type java.lang.NullPointerException
   :message nil
   :at [clojure.lang.Numbers ops "Numbers.java" 1068]}]
 :trace
 [[clojure.lang.Numbers ops "Numbers.java" 1068]
  [clojure.lang.Numbers divide "Numbers.java" 186]
  [clojure.lang.Numbers divide "Numbers.java" 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)

seancorfield01:08:16

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

caleb.macdonaldblack01:08:30

Yea I get the same in my repl too.

caleb.macdonaldblack01:08:44

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

caleb.macdonaldblack01:08:56

I'll take this over to the cursive channel

seancorfield01:08:10

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

caleb.macdonaldblack01:08:59

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

seancorfield01:08:31

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.

seancorfield01:08:16

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.

seancorfield01:08:24

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.

seancorfield01:08:51

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

caleb.macdonaldblack01:08:02

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

caleb.macdonaldblack01:08:32

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.

caleb.macdonaldblack01:08:56

Thank god. This has been painful.

seancorfield02:08:23

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

dl10:08:33

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

uosl11:08:26

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

dl11:08:38

I have no microservices yet...

dl11:08:42

its a big php monolith

dl11:08:15

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

dl11:08:37

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

uosl11:08:21

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.

dl11:08:33

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

uosl11:08:39

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

Crispin13:08:39

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.

dl09:08:56

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

dl09:08:16

its just such a pain to expand the monolith

dl09:08:28

@jarvinenemil thanks I will read that book too

dl09:08:49

so you all recommend to setup a microservice infrastructure?

sova-soars-the-sora14:08:12

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

sova-soars-the-sora14:08:20

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

sova-soars-the-sora14:08:05

oh it's lein cljsbuild once min

sova-soars-the-sora14:08:35

@abdusalam http://www.japanesecomplete.com/compounds ^^ thanks for your help, it'll take time to get all the kanji up there xX 😄

🎉 4
salam19:08:45

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?

seancorfield16:08:59

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

Mario C.16:08:23

How would I use it?

seancorfield17:08:54

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

seancorfield17:08:37

(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

dpsutton17:08:51

(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

dpsutton17:08:24

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

seancorfield17:08:32

@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
seancorfield17:08:12

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!).

seancorfield17:08:33

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.

seancorfield17:08:11

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.

hiredman17:08:05

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

hiredman17:08:51

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

hiredman17:08:50

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

hiredman17:08:36

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

hiredman17:08:48

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)

hiredman17:08:29

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

hiredman17:08:40

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

hiredman17:08:05

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

dangercoder17:08:11

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'),
      ...Ionicons.font,
    });
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?

hiredman17:08:41

call and response I guess

hiredman17:08:50

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.

hiredman17:08:57

a clojure map is not a js object like that

dangercoder17:08:07

yeah I need to add #js ?

hiredman17:08:18

cljs keywords are not strings either

David Pham17:08:02

You could try to call the assign operator from JS?

noisesmith17:08:56

yeah Object.assign should do it

dangercoder18:08:07

for the ... part?

noisesmith18:08:41

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

noisesmith18:08:24

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

dangercoder18:08:49

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

noisesmith18:08:47

I just remembered I have lumo

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

dangercoder18:08:43

worked like a charm, thanks for the help guys 🙂