Fork me on GitHub
#clojure
<
2017-05-07
>
nikki00:05:53

hey all so i'm using bidi with ring

nikki00:05:06

(defn handler [req]
  (let [resp ((bidi.ring/make-handler routes) req)]
    (println (:request-method req) (:uri req) "->" (:status resp))
    resp))

(defn start-server []
  (jetty/run-jetty #'handler {:port 8080
                              :host "0.0.0.0"
                              :join? false}))
is how i define start-server the server

nikki00:05:24

and routes is like

(def routes ["/" {"todos" {:get {"" list-todos}
                           :post {"" create-todo}
                           :options {"" options}
                           ["/" [#"\d+" :id]] {:get get-todo}}
                  "" {:options options}}])
where get-todo etc. are functions defnd

nikki00:05:34

if i re-`defn` one of these functions it won't use the updated version right?

nikki00:05:49

what is your fav way of making sure it keeps up with function re-`defn`s

seancorfield00:05:26

Use #' on the symbols.

seancorfield00:05:48

It introduces a layer of redirection through a Var so the redefinitions are picked up.

seancorfield00:05:09

That’s how I develop web apps and it works really well from the REPL.

nikki00:05:48

ooooooo interesting

nikki00:05:43

and vars don't need to be explicitly deref'd... i see

gordon00:05:30

According to https://clojure.org/reference/reader, keywords cannot contain . but that is allowed by the reader and used in many projects (e.g. Datomic's :db.type/keyword) - are we relying on undefined behavior there or is that possibly a documentation inconsistency?

nikki00:05:53

@gws i'm still a noob at clj(s) but my guess is that db.type/ is a namespace prefix

nikki00:05:29

within a namespace, ::x means the keyword x under that namespace, and outside that namespace, :ns/x refers to the same keyword if ns is a path to that namespace from this one

noisesmith00:05:28

@gws if I understand correctly that is a limitation on the name part, not the namespace part

noisesmith00:05:34

I could be mistaken though

noisesmith00:05:23

@nikki yeah, I thought that too, and FYI the "prefix" here is called a namespace

gordon00:05:16

:a.a is accepted by the reader as well

noisesmith00:05:11

yeah, but in context, namespaces are expected to always contain a dot, and the :: reader syntax attaches the current namespace to the keyword

noisesmith00:05:38

so I think that :a.a is accidentally allowed, and :a.a/b is intentionally allowed

noisesmith01:05:27

I could be wrong about that - but it would be weird if namespaced keywords belonging to every correctly designated namespace are in error

gordon01:05:04

I agree that namespaced keywords are very intentional and almost certainly not in error, I'm just trying to get at what that part of the doc means, since I've never known . to be disallowed in keywords (may predate my use of Clojure, though)

noisesmith01:05:57

@gws the keyword reader has always accepted invalid keywords as literals

noisesmith01:05:30

where "valid" means the spec and not the implementation clearly

noisesmith01:05:53

(not to mention the keyword function happily accepting any crap you can fit in a string...)

gordon01:05:23

OK, I think that answers my question - I started looking at this because I had a short-lived notion that I'd make oauth scopes into keywords and was surprised to see : accepted in the REPL

gordon01:05:24

yeah, that looks about like my REPL session!

Ethan Miller07:05:06

Is it possible to do the equivalent of JS spread operator in clojure for function args: e.g. fn(...args)?

j-po07:05:59

Beyond apply, I don't think so.

j-po07:05:44

Or, rather, yes! (apply fn args). Nothing more brief than that, I'm afraid.

Ethan Miller07:05:47

Hmmm. That might work.

Tim16:05:35

are there ever a time when namespaces are implicitly loaded in the repl? or are procedures only ever loaded when they are called?

Tim16:05:00

(referring to runtime in an uberjar)

noisesmith16:05:42

procedures are not loaded when called

noisesmith16:05:20

a namespace is loaded if designated as the main ns, and clojure will run its -main function if any

noisesmith16:05:07

there's also mechanisms like leiningen's :init-ns configuration, that run a repl and load a specific ns and put the user in that ns at startup

noisesmith16:05:43

otherwise clojure.main (if it is run) will put you in the user ns and you will need to use require to make other namespaces available, and in-ns to switch to another ns

michaellindon17:05:54

is a function prints something to the repl, is it possible to wrap the function call in something which suppresses those messages? or redirects them to a log file?

noisesmith17:05:34

anyway, with-out-str works in a real repl

noisesmith17:05:52

it returns a string with everything that function printed in it

michaellindon17:05:39

great! thanks 🙂

bradford18:05:17

uh, oops. sorry, wrong slack. but I'll keep it because yolo

lvh19:05:04

what’s the current state of the art in parallelized reduce? (I can write my fn such that it has an init val and produces n “todo” items); it seems like the answer is manifold streams/core.async rather than fold

noisesmith19:05:11

not core.reducers?

lvh19:05:01

maybe; they seem like mostly suffering from pre-transducer everybody-gets-a-mapcat; also I’m not super sure that what I have is actually a fold (maybe it is, but it has the weird property that processing an item can produce more items to process)

lvh19:05:04

(I’m diffing)

noisesmith19:05:53

if you use core.async, just remember that go blocks are not for blocking tasks (which includes heavy CPU usage as well as io)

elevi19:05:42

@noisesmith why IO is also not recommended ? I think that because its running on a OS thread, there is no problem when doing it, the Kernel will schedule it efficient

noisesmith19:05:44

@elevi core.async uses a small thread pool, doing IO in core.async threads starves core.async, if it is done enough then no core.async code runs

noisesmith19:05:16

if you mean non-blocking IO, non-blocking IO doesn't happen in the calling thread, so it is fine

elevi19:05:21

agree, blocking IO can cause a starvation...

elevi19:05:42

Libraries the support async IO are OK to use

noisesmith20:05:19

@elevi right, my emphasis was on blocking - in most code the only things that block are io, sometimes occasionally heavy CPU usage

noisesmith20:05:14

non-blocking io isn't something I see very often in jvm code (could be I'm looking at the wrong code?)

elevi20:05:41

I think http-kit support async IO using core.async

tbaldridge11:05:22

elevi: yes and no, http-kit supports some form of async io, but the important parts of the implementation are undocumented. For example, sending to a websocket is a blocking operation It's one of the reasons why I recommend using some other library for async http work, there's a few too many edge cases in http-kit

elevi13:05:44

@U07TDTQNL didn't know that, thanks

noisesmith20:05:03

yes, java.nio provides it, and it's wrapped by things like aleph and http-kit

noisesmith20:05:10

what I'm saying is that I don't see people using it

elevi20:05:05

Agree, I don't think anyone uses nio directly, its very low-level, but I think that all the modern clients, at least in Clojure, support some kind of async IO

noisesmith20:05:01

I'm talking about app code (and library code if it does requests on your behalf) - I'm not seeing async oriented code bases

noisesmith20:05:19

not like you'd see in js

noisesmith20:05:18

I speculate that it's because outside bottlenecks the extra complication of the async apis isn't worth it, but I'd welcome an example of doing this properly that doesn't turn out more complex than the blocking code

noisesmith20:05:27

having worked on a pretty complex app for a while, I've come to the conclusion that async is a problem - it makes code more complex, it causes failure cases that are harder to diagnose and fix; it's worth taking on if it fixes some other more important problem though

elevi20:05:53

why more complex ? because of the callbacks ?

noisesmith20:05:49

because the control flow is less clear, even with core.async

noisesmith20:05:22

because the stack traces become nearly meaningless, the immediate parent of every function is the dispatching go thread (in core.async) or some random callee (regular callbacks)

noisesmith20:05:47

because simple errors have strange and far reaching consequences that in my experience devs are bad at catching when reading code

noisesmith20:05:24

because errors get swallowed by some consumer's try/catch and you never find out they happened

noisesmith20:05:53

because data pipelines get locked up by some flaw in the middle and there's nearly no way to know why just looking at stack traces and printlns

noisesmith20:05:21

example from just the other day: a coworker had some code that was taking 20 minutes to return, it should have returned in under a minute. It turned out he was doing a blocking take inside a go block. He didn't even know the take he was doing was blocking.

noisesmith20:05:47

from just looking at the symptoms, we never would have guessed which part of the code was causing the behavior

noisesmith20:05:59

he thought the remote api call was taking too long to complete

noisesmith20:05:56

I'm not saying don't use async - I use it a lot! - I'm saying it makes code a lot trickier, and it isn't beneficial on its own - you need to have a reason to use it, some problem it actually solves

pesterhazy20:05:31

that was a great experience report, thanks @noisesmith

elevi20:05:36

Yea, but core.async make it more simpler, at least for me, I tend to work with channels instead callbacks, my code feels much more synchronous with it. About the data flow and encapsulation complexity, I think things are much easier in Golang, their tools are great, every Goroutine have its own stack and it's easy to see whats your code is doing right now. I don't think these tools can be ported to Clojure because how they implemented core.async, e.g not using some-kind of thread mechanism

noisesmith20:05:05

I think core.async is the best async option in clojure for sure - I'm talking about things that tend to go wrong with async code (in my limited expertise and experience)

noisesmith20:05:28

async as in asynchronous, as in the paradigm, to be clear

elevi20:05:28

I also think that, core.async is my goto when it comes to async. Did you tried other concurrency features like Agents ?

noisesmith20:05:13

yes, I have code that uses agents and futures and claypoole/pmap and regular pmap, in the past I've also used refs

qqq21:05:35

Is there a short hand for #?(:cljs ...) ?

mccraigmccraig22:05:48

@noisesmith @elevi i've got a load of async code, mostly with promises (manifold Deferred) and streams (manifold Streams) - i use the promise monad courtesy of cats https://github.com/funcool/cats/blob/master/src/cats/labs/manifold.clj for structuring, which leads to async code which reads very similarly to equivalent sync code

mccraigmccraig22:05:31

stack traces, or the lack thereof, are sometimes still a problem, but not terrible - similar to working with lazy seqs

qqq22:05:37

is there a builtin for string -> int which taxes a hexcode string and outputs a decimal int

mccraigmccraig22:05:55

@qqq you can certainly use java.math.BigInteger to do that - https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html#BigInteger(java.lang.String,%20int) ... calling longValue on the BigInteger will give you a primitive long &c

gfredericks23:05:31

I believe I have seen a clojure library that will give you a "private" version of a library by copying its source code and renaming the namespaces

gfredericks23:05:46

something to that effect; does anybody know what library I'm thinking of?

gfredericks23:05:04

I have spent dozens of seconds on google trying to find this, to no avail

gfredericks23:05:40

many millihours have been wasted