Fork me on GitHub
#beginners
<
2020-08-01
>
cychoi03:08:38

Hey, everyone! I’m new here, to Clojure, and to functional programming in general. I’m trying to wrap my head around how I should approach control flow in my Ring app in my request handlers and middleware as I’m fetching things from the database. Any tip for how I can improve these examples?

cychoi03:08:36

I feel like I’m possibly using these control flow macros in a way that makes the intent unclear or hard to follow

seancorfield04:08:59

You can definitely simplify wrap-auth-session and avoid that nesting:

(if-let [user (some-> (get-in request [:cookies "sid" :value])
                      (get-active-session)
                      :user_id 
                      (get-user-by-id))]
  (handler (assoc request :user user))
  (handler request))

seancorfield04:08:41

You can remove the (do ..) wrapper in the first function -- you only have one expression in there so it's unnecessary to wrap in do. I don't think you can do much other simplification -- parameter access / validation in Ring handlers is always a bit verbose.

seancorfield04:08:59

Not sure what you mean by "control flow macros" tho'...?

cychoi04:08:14

Thanks for the tips! I think some-> is perfect for what I was trying to achieve there. By control flow macros, I only meant things like do , if-let, if, and so on. Wasn’t sure what else to call them

seancorfield04:08:28

They're special forms, technically, but we tend to just refer to them directly by their names. See https://clojure.org/reference/special_forms When you say "macro" it has a very particular meaning in Clojure.

seancorfield04:08:17

(and, yes, it's a little blurred that a couple of the special forms are actually implemented as macros that expand to lower-level implementations: let in terms of let*, fn in terms of fn*)

ruyvalle03:08:44

Hello! Is there a way to add dependencies to deps.edn, get them from their repos and require them in a running clj repl?

alexmiller03:08:11

There’s an experimental branch with support for this but it’s not part of clj yet

ruyvalle03:08:35

also I am wondering if core.match supports matching nested lists. for instance given ('a ('b 1 2 3)) as input I would like to bind the list (1 2 3) to a pattern variable.

ruyvalle03:08:29

I got it 🙂

ruyvalle04:08:07

actually no I don’t

seancorfield04:08:18

Are you looking for something like this:

user=> (match [['a ['b 1 2 3]]]
[[_ [_ & r]]] r)
[1 2 3]
user=>

seancorfield04:08:59

Or if you really want sequential matching

user=> (let [x '(a (b 1 2 3))]
         (match [x]
           [([_ ([_ & r] :seq)] :seq)] r))
(1 2 3)
user=>

seancorfield04:08:02

(I'm a bit puzzled by your ('a ('b 1 2 3)) example since that is not what you might think:

user=> ('a ('b 1 2 3))
Execution error (ArityException) at user/eval1600 (REPL:1).
Wrong number of args (3) passed to: clojure.lang.Symbol
user=>

seancorfield04:08:21

^ @ is that helpful

ruyvalle05:08:58

yes! thank you! I ended up doing what your showed in your second example

ruyvalle05:08:39

and yes I should have written '(a (b 1 2 3))

p.kushwaha9705:08:56

(moved my question to a more appropriate channel)

p.kushwaha9706:08:11

I want to pass an expression or a name which binds to that expression to my macro. So I'm calling the macro like (m fn-name) and (m (fn [] ...)) instead of (m 'fn-name) . First, is this the right way to do it or should I always quote symbols when passing them to macros? Second, if it is the right way, then I can't figure out how to make the symbol expand in the original context (the code calling the macro) instead of macro definition context. When I try to macroexpand, it gives an error while trying to resolve the symbol in the wrong context, instead of expanding it.

noisesmith17:08:46

macros receive lists and symbols, unevaluated

noisesmith17:08:58

whatever the macro returns will be evaluated afterward

noisesmith17:08:11

lists will become calls, symbols will be looked up - after the macro returns them

noisesmith17:08:57

the only job of a macro is to take lists, symbols, etc, return a list (or rarely, just a symbol or whatever), so that the compiler can do the rest

noisesmith17:08:30

'fn-name shows up to a macro as (quote fn-name) because the args are not evaluated before the macro sees them

noisesmith17:08:38

the args to the macro don't expand in the context of the macro definition, they always expand in the context of the macro call

noisesmith17:08:09

but, the macro can't see the run time value of a symbol - it can return the symbol, and the compiler turns that into code that looks up the value at run time

noisesmith17:08:15

this isn't a good macro, but it establishes a point:

(ins)user=> (defmacro caller-x [] 'x)
#'user/caller-x
(ins)user=> (let [x 0] (caller-x))
0

noisesmith17:08:33

by returning the symbol x, it causes the compiler to emit code that looks up x

michael81917:08:57

Hello, is there any book or other reading material on removing state? I seem to be addicted to state like it's crack. I have written and entire app in clojure that ties 6 different apis together, but I have state all over the place. I can't seem to wrap my head around getting rid of it.

smith.adriane18:08:06

@ , on that topic, i would recommend https://youtu.be/ROor6_NGIWU

smith.adriane18:08:32

the gist is to replace state with flow between simple machines. the talk had a lot to say about how this looks in the “large” (between multiple programs and servers), but also in the “small” (within programs)

michael81918:08:01

I think I watched that before but I will watch it again.

smith.adriane18:08:38

https://www.infoq.com/presentations/clojure-core-async/ is similar, but i usually don’t recommend it since clojure.core.async is usually overkill for most projects

smith.adriane18:08:04

but the principles are still useful even if you don’t use the library

michael81918:08:22

Here is a simple toy I built that I ended up using as the basis of my app so you can see what I mean about addicted to state. https://github.com/michaelwhitford/clojure-swapi

g3o20:08:40

Hi guys, Can someone please explain what is the difference between these two snippets?

g3o20:08:00

(map vec ’[(:a 1) (:b 2)])

g3o20:08:07

(map vec [(:a 1) (:b 2)])

g3o20:08:57

if I remove the quote the second line returns emtpy lists

andy.fingerhut20:08:57

The first quotes the vector expression, which prevents evaluating any of its contents

g3o20:08:09

oh I see

g3o20:08:29

I thought quote is used only with forms

noisesmith20:08:58

a vector is a form - everything the clojure repl can consume as a standalone value is a form

andy.fingerhut20:08:35

The second will evaluate the last expression

g3o20:08:46

super, that makes sense

andy.fingerhut20:08:50

Evaluating a vector causes each element to be evaluated

g3o20:08:58

thank you very much Andy

andy.fingerhut20:08:09

Quote can be used to prevent evaluation of any expression. It is not very useful to do so for expressions that evaluate to their own values like numbers and keywords, but it is not illegal