beginners 2018-05-01

@nclandreimihai has joined the channel

@acripps has joined the channel

@jeff434 has joined the channel

I’m reading the docs for Luminus, and I don’t understand how you’re supposed to keep your application manageable as it grows. It doesn’t seem like there’s a clear place for “business logic”.

@hello254 has joined the channel

@chris.klaou has joined the channel

@eugenio.dileo has joined the channel

@bolivier I have the same question, I think you’re supposed to just create a few namespaces and put some functions there. Then call those functions from the various web handlers.

You might use something Component or Integrant if you have some state (e.g. database connections/setup) — I’m not sure what Luminous uses by default.

@jeff434 has left the channel

Does anybody know what the difference between recur and trampoline is? Kinda confused right now 😞

@denisgrebennicov I actually think if you do (source trampoline) you might come away with a clear "no-mysteries" understanding of it, since it is build right on top of recur.

Alex Miller (Clojure team) 2018-05-01T14:14:41.000616Z

there’s a whole chapter with more detail in Programming Clojure if you want lots more words

My mental model is that it is kind of like a recur but that looks at the return value to see if it is a function and should continue going...

It has a good name in that it mentally reflects what it does

@mfikes thanks 😅. Still not used to look up to the source code implementation for better understanding 😄

Yeah, 🙂 in this case trampoline is nice and compact in its impl

Also, if you haven't seen it, this can aid in understanding: https://clojuredocs.org/clojure.core/trampoline

I guess, when I was reading the description I was confused, because of that mutual recursive part and other than that, it seemed like it was the same as what recur does, namely, calling the function while reusing the stack

You can also use trampoline to get around (non-mutually-recursive) cases where recur can't be used. For example, you can't recur across a try, but if you really need to, you can fashion a solution using trampoline.

@denisgrebennicov recur literally reuses the stack, trampoline extends the stack to a new function but only after returning from the previous. It's reuse vs. avoid growing too far.

recur is a goto, trampoline a new function call

The other way to look at recur vs trampoline is that chances are excellent that you mean recur. 😉

😄 😄 😄

I was just reading this blog post: https://deque.blog/2017/06/01/clojure-high-performance-fibonacci/ and they guy was comparing fibonacci implementation recur vs trampoline

Alex Miller (Clojure team) 2018-05-01T14:51:45.000653Z

almost any numeric benchmark on Clojure is dominated by how well you avoid boxing

Alex Miller (Clojure team) 2018-05-01T14:52:02.000207Z

as boxed math is ~100x slower than primitive math

what is a good practice to handle database connection functions I’m doing my execute using a global invisible connection I could obviously (defn execute [conn query params]) but I can’t see why should I do that

(def conn (nr/connect ""))

(defn execute [query params] 
  (-> (cy/tquery conn query params) walk/keywordize-keys))

what best practice in this case, should I pass the connection to be more functional and my function be more pure and functional

Alex Miller (Clojure team) 2018-05-01T14:56:06.000400Z

pass the connection

Alex Miller (Clojure team) 2018-05-01T14:57:10.000124Z

in general, I would start from: never “def” a stateful thing

💯 1

or if you really have to, use defonce? 🙂

Alex Miller (Clojure team) 2018-05-01T15:00:26.000336Z

well, that depends a bit on the semantics

Alex Miller (Clojure team) 2018-05-01T15:00:57.000674Z

defonce is a pain for interactive development so I try to minimize that too

Alex Miller (Clojure team) 2018-05-01T14:59:11.000008Z

(all rules are made to be broken eventually, but I think this is a good thing to strive for)

Alex Miller (Clojure team) 2018-05-01T15:01:03.000591Z

I’ve only ever regretted def’ing state. create it during initialization and pass it around. don’t enshrine it in a var.

when you say @alexmiller create it during initialization you meant in the begining of the process , boot process ?

for instance, in my main application and passing into my handlers ?

Alex Miller (Clojure team) 2018-05-01T15:02:46.000318Z

yeah

and creating a closure

sure, makes sense to me

Alex Miller (Clojure team) 2018-05-01T15:03:21.000268Z

in the path of starting your application, cause the state to be created, then inject, close over, pass to those who need it (which of those will depend on what you’re doing)

@alexmiller would you recommend def'ing your state initially, to play with it in the repl? And then rework your functions to be more of a pipeline as you massage your functions into a compilable program?

Alex Miller (Clojure team) 2018-05-01T15:04:34.000191Z

oh sure, you will likely need some way to def state for edevelopment

Alex Miller (Clojure team) 2018-05-01T15:04:44.000243Z

but that’s dev stuff

Alex Miller (Clojure team) 2018-05-01T15:05:12.000466Z

Clojure is most glorious when arranged as pipelines of functions handling data

Alex Miller (Clojure team) 2018-05-01T15:05:23.000360Z

not all work looks like that though :)

I find when I first approach a problem, I start with all state "exploded" into its parts, sitting in vars

but then I massage things into a pipeline once I understand the domain

@jeremiah.meert has joined the channel

I wonder how closed over state would look in case of failures - ie some database goes down momentarily, connection is closed and you need to reconnect and update all dependencies...

@whoneedszzz has joined the channel

@orestis you can always pass a map back from functions with new state, and function results

I've sometimes found it useful to have a record with a reconnect method, and a shared internal state - but these things are less elegant than I'd like. I think the right solution is to make something like a jdbc connection pool object API, and let it manage its own open connection(s) in its own isolated scope

so what you pass into the code at startup carries not just a currently working connection, but the config that knows how to reconnect, and behavior that manages that

trying to make an implicitly stateful thing act like an immutable thing can make things more complex rather than less if you do it clumsily

Right @noisesmith so instead of passing a connection around you pass a connection manager or something similar. I keep thinking about the Erlang way where you use named processes to hide the details. It’s still global state but it’s hidden away and the calling code is by even aware that there is any state, as at the call site it looks like any plain function. Of course, that’s what we are trying to avoid all along?

right, in clojure you can do that with a closure that exposes a safe api, and handles any trickiness internally

though that should be reserved for things that are intrinsically mutable / stateful