Fork me on GitHub
#beginners
<
2018-08-20
>
_rj_r_00:08:33

whats the purpose in wrapping the server and db in a handler? In the PurelyFunctional vids it has this:

(defn wrap-server [hdlr]
  (fn [req]
    (assoc-in (hdlr req) [:headers "Server"] "Listronica Server")))

(defn wrap-db [hdlr]
  (fn [req]
    (hdlr (assoc req :webdev/db db))))

(def app
  (wrap-server
   (wrap-db
    (wrap-params
     routes))))
Is wrapping the db in a handler used in other languages as well? I don't recall having to do anything like this in Node.js.... and I don't think I've ever seen the server handler like this... curious as to the purpose?

dpsutton02:08:32

https://github.com/expressjs/expressjs.com/issues/689 here's an issue about this very pattern in express

dpsutton02:08:09

but the idea is to just embed the database connection into the request so the handler doesn't need any specific way to create its own connection it just has one available to it

RJ12:08:55

So just curious... does embedding the database connection like this create any security issues?

noisesmith02:08:17

@ryan.russell011 to be pedantic, the wrap- functions are wrapping the handler and replacing it with a new one that has added context, so the db isn't wrapped in the handler, the handler is wrapped by the db to create a new handler

_rj_r_03:08:34

@noisesmith oh right... yeah.. thank you for that clarification... I had it backwards in my head there.... @dpsutton So from what I got from the github issue is that this pattern isn't necessary and could cause potential issues. I'm curious why PurelyFunctional decided to present it this way. When I get to my own project, I'll look around more for how this could be handled differently.

iyahoo05:08:28

I have looked for a function to remove duplicates from a seq with a test which define an equality. That’s like the remove-duplicates with :test on Common Lisp. However, I couldn’t find it in clojure core functions, so I had accordingly written the function like this (https://gist.github.com/iyahoo/164406dbb730f935b8677cf3420dfe2f). But I would like to know why the function is not implemented (or I missed it), and a way to write in better approach (e.g. how I can construct it on a combination of core functions). Does anyone know this?

iyahoo07:08:18

@U3L6TFEJF Thank you response. I found it but I think it cannot receive an equality function (it only check =). I would like to remove duplicates, by an equality function as belows:

(remove-duplicate-with-test [1 2 3 4 3 2 1] =)
;=> [1 2 3 4]

(remove-duplicate-with-test [{:foo 1 :bar 2} {:foo 1 :bar 3} {:foo 2 :bar 1} {:foo 2 :bar 4}]
                            #(= (:foo %1) (:foo %2)))
;=> [{:foo 1, :bar 2} {:foo 2, :bar 1}]
In the first case, it is same behavior as distinct, but the second case only check :foo’s values regardless of :bar’s values for to decide the equality. Can the distinct deal with that case? If so, I’m sorry and I will read the page once.

schmee07:08:18

ahh, sorry, I did not read the question carefully enough! there is no such function in core, but it has been implemented a number of times, here is one example: https://github.com/weavejester/medley/blob/master/src/medley/core.cljc#L177-L202

iyahoo07:08:30

Thank you very much. The library is so helpful for me.

schmee07:08:53

glad to help 🙂

quadron10:08:44

the last expression returns 2 whereas I expect 1! is this a bug or am I missing something?

noisesmith17:08:09

I am not a manifold expert but my first hunch is that map isn't fully lazy - eg. if it has a special case for empty input it needs to consume at least one item (common problem with otherwise lazy code)

quadron21:08:29

does the return value surprise you?

noisesmith21:08:19

it doesn't surprise me, because it's common for a nominally lazy process to steal a single value

noisesmith21:08:34

but I'm not 100% sure that's what happened, I just think that's likely

noisesmith21:08:43

you'd need to consume from z to really know

noisesmith21:08:19

if consuming y gives 2, then consuming z gives "1", that's what happened

quadron22:08:26

consuming z gives "1"

quadron22:08:12

how do you think of the relationship between y and z?

quadron22:08:27

what's the concept?

noisesmith22:08:47

y is a mutable source of data, z consumes from it, so reading y depends on the state ofz

noisesmith22:08:35

in reliable async code, I wouldn't expect to see anything sharing an input with z (that is, I wouldn't expect anything else to read from y), unless you are OK with z not seeing those items, and also OK with z stealing items the other consumers of y would otherwise see

✔️ 4
quadron10:08:20

this is from the manifold library (aleph)

felipebarros13:08:00

Following https://clojure.org/guides/learn/functions#_test_your_knowledge , the tests fail with AssertionError Assert failed: (= "Hello, World!" (greeting)) user/eval6094 (NO_SOURCE_FILE:1). What am I doing wrong?

felipebarros13:08:39

I have defined the function:

(defn greeting
 ([] (greeting "World"))
 ([x] (println "Hello," (str x "!")))
 ([x y] (println (str x ", " y "!"))))

felipebarros13:08:08

And (greeting) returns "Hello, World!" as expected.

felipebarros13:08:38

So far I interpret the error message to represent the lack of a file where the function is defined, but so far in the tutorial it doesn't assume more than a REPL environment, which is what I'm using.

mg13:08:04

It's not returning anything. println returns nil and prints as a side effect

4
felipebarros13:08:12

Hmm, makes sense Michael. The tutorial hasn't mentioned returning values yet though, which is why it got me confused.

mg13:08:16

Also there's an extra comma in there

mg13:08:08

You're adding a comma and an exclamation mark in the one-arity, which calls the two arity and it adds a comma and exclamation mark as well

mg13:08:18

Are you working in a repl?

PB13:08:22

@anantpaatra println returns nill

felipebarros13:08:25

The two arity is independent in this case, no?

felipebarros13:08:36

Yes, I'm working in a repl.

manutter5113:08:45

@anantpaatra The problem is you have written a function that prints the result as a side-effect. That’s different from returning the result. Solution: just get rid of the println

felipebarros13:08:11

Yes, that is the solution @manutter51

joelsanchez13:08:25

apart from removing the println, multiple-arity is not very readable some times

felipebarros13:08:46

It was just a bit confusing, where can I contribute to this tutorial?

felipebarros13:08:09

Thanks everyone

manutter5113:08:03

There’s a guide to how to contribute to clojure https://clojure.org/community/contributing

manutter5113:08:31

IIRC that includes changes to the docs

felipebarros13:08:14

@joelsanchez I guess the tutorial is just trying to make a point by using multiple arity in this case, but that looks nice.

felipebarros14:08:43

@manutter51 it's a bit more involved than I anticipated but I guess I would have to fill that someday anyway.

sundarj14:08:19

@anantpaatra @manutter51 actually, small changes to the docs only require a GitHub issue/PR here: https://github.com/clojure/clojure-site

👍 4
manutter5114:08:49

Ah, cool, didn’t know that.

Appo718:08:15

I have a performance question. I've got a map with about 150k entries. Each entry is a key with a string vector as a value (each vector is about ~5-10 strings long) I want to group the map by the length of the string vectors, but (group-by #(count (second %)) mymap)) seems to take forever.

hiredman18:08:47

are you sure they are vectors and not lazy-seqs and that doing the count isn't doing the work of forcing all the lazy seqs?

Appo718:08:57

oh yep it's a lazy sequence. How do I avoid this problem?

hiredman18:08:53

it isn't really something to avoid, you can either do the work up front, or delay it until you do the group-by, but either way it will get done

_rj_r_18:08:58

question to group: What is your preferred way/library to interact with a Postgres database? (and why?)

Chase19:08:07

so i've been able to wrap my head around basic recursion thanks to you folks. now i'm trying to figure out how recur and reduce fit in this picture. I understand now how this example works:

(defn sum
  ([vals]
     (sum vals 0))
  ([vals accumulating-total]
     (if (empty? vals)
       accumulating-total
       (sum (rest vals) (+ (first vals) accumulating-total)))))
And now, I can just sub out that last sum for recur and it works just the same:
(defn sum
  ([vals]
     (sum vals 0))
  ([vals accumulating-total]
     (if (empty? vals)
       accumulating-total
       (recur (rest vals) (+ (first vals) accumulating-total)))))
But I read the doc on recur and I don't really understand what it is saying. I'm having difficulty formulating my question, but what is recur actually doing and why is it used for performance reasons as this tutorial is telling me?

Chase19:08:29

and then what does reduce add to this equation? I'm thinking these are just various approaches to recursion right but not sure when you would use each.

hiredman19:08:48

the processing described by that sum function has a shape that is shared by lots of other processes, that shape is called a fold, and folds are tree shaped processes (a list being a degenerate form of a tree). reduce is a kind of fold

hiredman19:08:58

as for recur, that is kind of a whole other thing, but calling it a performance optimization is kind of weird

Chase19:08:05

is that shape/tree meaning where you are going to take the first item in a collection and do something, and then call the function with the rest of the collection and keep doing it until the collection is empy.

Chase19:08:28

the tutorial (brave book) says to generally use recur for performance because clojure doesn't provide tail call optimization

hiredman19:08:24

it isn't really a performance thing, it is a correctness thing. without recur your sum will blow the stack on a large enough input

hiredman19:08:05

I dunno if you saw the sicp's discussion of recursive vs. iterative processes, but recur makes a recursive process iterative

hiredman19:08:12

in a language with tco, your sum's recursive call would be made iterative by tco

Chase19:08:04

i saved the link but i haven't watched them yet. So are you saying recur is just a basic tool we would use for simple recursion functions that let us reason about it in a more iterative manner? i'm not too familiar with iterative loops either unfortunately.

Chase19:08:24

not sure if you remember but you basically gave me a step by step breakdown of simple recursion. with recur what would i do differently when writing out the steps. is it the same as if the function itself was being used in it's place?

Appo719:08:37

@hiredman fixed my problem now, was unrelated to the laziness. The function to generate the string vectors was the bottleneck

hiredman19:08:56

a recursive process builds a tree, it may actually build a tree datastructure and return it, but it also builds a tree of runtime state. at the very least a sort of linear list of stack frames to be popped as the process completes

hiredman19:08:40

so it accumulates runtime state, that runtime state has to be stored somewhere (on the jvm it is stored in a callstack)

hiredman19:08:55

an iterative process does the same thing over and over again without building up some intermediate state (like a loop that overwrites the same set of variables)

hiredman19:08:42

when you have a recursive process that is in the shape of a list (not a general tree, but there are tricks for that too) it is kind of trivial to convert it in to an iterative process, and some languages offer the ability to do that automatically as tco

hiredman19:08:36

because of technically limitations of the jvm, and wanting to interop with the jvm well, clojure can't do tco in the general case

hiredman19:08:30

recur is like a manual tco annotation, with the extra benefit that the compiler will complain if recur is used in a situation where it cannot create an iterative process. in languages that do automatic tco you can sometimes mean to write an iterative process and accidentally write a recursive one

Chase19:08:22

good stuff!

Chase19:08:53

i don't quite get all of it but it's interesting to learn some of the fundamental things going on in the background

hiredman19:08:23

http://www.sicpdistilled.com/section/1.2.1/ has example substitution model executions of a recursive fib function and an iterative fib function

Chase19:08:44

i'll read through it. and i'm already starting to understand more of what you wrote above as i read over it a couple times. i think this "reward of deep thought" that i get when I really try and grasp this stuff is what draws me to programming so much. it's cool stuff. it's also why when i feel i'm too tired to progress i just allow myself to walk away for a bit cuz it can't be forced with tired, surface thinking.

athomasoriginal22:08:07

Question about specs. Lets say I have a function like this:

(defn insert! 
   [{:keys [db id]}]
   ;; implementation here]
If I were to spec this, I would need to do something like this:
(s/fdef insert! 
  :args (s/cat :args (s/keys :req-un [::my-ns/db ::my-ns/id])))
However, what if I want the names in the argument declerations to be different from the specs? For example, I want to keep the argument declarations the same as defined in insert! but I want maybe the spec names are different
(s/fdef insert! 
  :args (s/cat :args (s/keys :req-un [::my-ns/global-db ::my-ns/global-id])))
Notice how db is now global-db vs. db. Would this be possible to do this inline the :req-un? or would I have to write a spec like (s/def ::db ::my-ns/db) to get this to work?

gaverhae22:08:47

I'm not sure how this could be done but I do want to point out that this is working against the underlying philosophy of spec. There's a strong emphasis in the spec "system of thinking" on having the same namespaced keyword for the same conceptual unit of information all throughout the program.

athomasoriginal22:08:31

Indeed. I agree with this tenant. I suppose an issue with this is if a code base defined the specs, but poor names/conventions were used. Instead of changing things everywhere, I was hoping to change things in place 😞 Obviously, this is not a problem with spec.

gaverhae22:08:37

On the bright side, if you're using literal namespaced keywords throughout, the find-and-replace operation should be fairly easy to do?