Fork me on GitHub
#beginners
<
2020-01-08
>
seancorfield00:01:52

Yeah, I think so. It's very useful for writing your own exception pretty printer 🙂

okwori05:01:19

The direction keys(2,4,6,8) within the Numerical Keypad on Ubuntu(Cursive) doesn't display history of commands on the repl. Same keys works on the terminal(Ubuntu), and also on Windows(Cursive). Has anyone noticed similar something similar maybe?

andy.fingerhut05:01:10

There is a #cursive channel you may wish to try asking in.

okwori06:01:36

True. Higher probability of someone encountering same here also...

grierson09:01:47

(->> coll (filter even?) count) or (->> coll (map #(if (even? %) 1 0) (reduce +))

grierson09:01:16

Which method is better? I prefer the count as it's more concise but I've seen a few code examples using the reduce + method? Is it more efficient?

jaihindhreddy10:01:44

Efficiency is probably about the same for both. Both create one lazy seq each. A more efficient alternative could be (*x*/count (*filter* *even?*) coll) using the xforms library. This uses the transducer returning arity of filter. This particular example is not all that elaborate and I don't think will result in a huge win though. (Benchmark comparison in thread)

jaihindhreddy10:01:23

user=> (require '[net.cgrand.xforms :as x]
                '[criterium.core :as crit])
nil
user=> (crit/quick-bench (count (filter even? (range 100))))
Evaluation count : 239082 in 6 samples of 39847 calls.
nil
             Execution time mean : 2.643472 µs
    Execution time std-deviation : 81.013434 ns
   Execution time lower quantile : 2.559159 µs ( 2.5%)
   Execution time upper quantile : 2.760176 µs (97.5%)
                   Overhead used : 9.031450 ns

Found 1 outliers in 6 samples (16.6667 %)
	low-severe	 1 (16.6667 %)
 Variance from outliers : 13.8889 % Variance is moderately inflated by outliers
user=> (crit/quick-bench (x/count (filter even?) (range 100)))
Evaluation count : 1124898 in 6 samples of 187483 calls.
nil
             Execution time mean : 541.007719 ns
    Execution time std-deviation : 38.221458 ns
   Execution time lower quantile : 514.929972 ns ( 2.5%)
   Execution time upper quantile : 598.902050 ns (97.5%)
                   Overhead used : 9.031450 ns

mloughlin11:01:52

when dealing with a PersistentMap in an atom , is it preferable to pass the atom to my function and deref at the point of use, or deref and pass the value into the function?

alexmiller13:01:03

I find it be a good policy to minimize the number of functions that touch stateful things, so generally I think you should prefer the latter

💯 1
mloughlin11:01:57

or not possible to generalise?

littleli11:01:04

on single use, it does not matter. On multiple use in the function, me personally I would deref once in let

Ramon Rios17:01:21

(update ctx :body v)
is this update wrong?

Ramon Rios17:01:37

ctx is a map

noisesmith17:01:31

is v a function that takes the current body?

noisesmith17:01:36

or is it the new value?

Ramon Rios17:01:40

I 'm trying to update :body for the value of v, wich is a map. but :body is always getting nil

Ramon Rios17:01:48

v is the new value

noisesmith17:01:03

"update" is for a function, taking the old value and returning a new one

noisesmith17:01:07

you want assoc

Ramon Rios18:01:29

Am i able to have 2 assocs, one after other?

noisesmith18:01:56

yes, but also you can just put multiple keys in one assoc

noisesmith18:01:15

user=> (assoc {:a 0} :b 1 :c 2)
{:a 0, :b 1, :c 2}

noisesmith18:01:31

important to note: assoc doesn't modify its argument, it returns a new map

Ramon Rios18:01:13

Ok, i'm couting with it

Ramon Rios18:01:48

I already have immutabily on my mind

noisesmith17:01:49

@ramon.rios the reason you get that result:

user=> ({:a 0 :b 1} :c)
nil
a hash-map is a function on its own keys, returning nil for not found

Ramon Rios17:01:38

Thank you friend

Ramon Rios17:01:50

assoc resolve it

Raymond Korir18:01:07

Is there clojure library for Twilio SMS API that actually works?

noisesmith18:01:14

it's a REST api, I'd just use clj-http

SoV418:01:46

Is there a way to alter session duration with ring? I'd like sessions to be basically perpetual but i don't know if there's a built-in timeout someplace. i also want to figure out how to update my static pages without restarting my server, but they are rendered from a vector

noisesmith18:01:38

@sova it's documented here https://github.com/ring-clojure/ring/wiki/Sessions

(def app
  (wrap-session handler {:cookie-attrs {:max-age 3600}}))

noisesmith18:01:56

so you can add :cookie-attrs as an extra arg to wrap-session

SoV418:01:14

okay let me see thank you

SoV418:01:42

` (defn -main [ & args] (server/run-server (wrap-cors (wrap-defaults all-routes site-defaults)) {:port 8117}) (println "server on @ localhost:8117"))`

noisesmith18:01:20

wrap-defaults is a boilerplate that includes wrap-session, once you customize one of the wrappers you probably want to stop using wrap-defaults

SoV418:01:56

Thanks for your help, the middleware is still my achilles heel.

noisesmith18:01:22

so you'd usually have something like (def site-handlers (-> all-routes (wrap-defaults site-defaults) (wrap-foo) (wrap-session {:cookie-attrs ...}))

noisesmith18:01:53

where each middleware returns a new handler to replace the one made by the step before it

SoV418:01:57

i notice a (wrap-foo) in there...

SoV418:01:14

Nice. my kingdom for a flowchart

noisesmith18:01:25

right, you'd do that for each handler that wrap-defaults has - since you want to replace the configs for one of the wrap-defaults middlewares

SoV418:01:11

Okay, so everything wrap-defaults does has to be covered by another one that passes a finished map along ?

noisesmith18:01:14

wrap-defaults is nice when you don't need to change any of the defaults, but luckily all it is is a bunch of stacked middleware, easy to replace

noisesmith18:01:40

that chain isn't passing a map, each wrapper returns a replacement for your handler, and you use the last one

SoV418:01:53

Oh. Neat. That makes more [functional] sense

SoV418:01:06

purely additive

noisesmith18:01:51

this is a prototypical midddleware:

(defn wrap-foo [old-handler] (fn [request] (f (old-handler (g request))))

noisesmith18:01:09

where f modifies the result of old-handler, and g modifies the input of old-handler

noisesmith18:01:27

of course most middleware don't do both - most only modify the result

noisesmith18:01:01

so by calling wrap-foo you are basically doing aop, and making a new function that does what the old handler does, and also calls f and g

SoV418:01:44

neat. almost graspable, still a little mind-bending

SoV418:01:27

i'm trying to think of a realworld analogy

SoV418:01:45

it's a car wash that can optionally modify the car before it goes in

noisesmith18:01:23

in aop g is called a "pre-hook" and f is called a "post-hook" iirc

dpsutton18:01:47

what is aop?

noisesmith18:01:10

it's the fancy name for what ring middlewares do (arguably) https://en.wikipedia.org/wiki/Aspect-oriented_programming

dpsutton18:01:20

new term to me. thanks

🎵 1
NoahTheDuke18:01:34

quick question regarding namespaces: i have a test namespace called game.core-test, which I'm using at the moment to hold all of my test helper functions and set up/tear down logic that's used in the rest of my test suite. but seeing as I have a game.core namespace already, this feels messy. Is there a common idiom/namespace for the test utility files?

noisesmith18:01:03

I mention AOP because it's a pretty well documented programming pattern / paradigm, and it does what we do in middleware

noisesmith18:01:18

so it can help in understanding the pattern (hopefully)

NoahTheDuke18:01:08

I checked the style guide, but they didn't have much for tests or test namespaces

seancorfield18:01:49

I would add test/game/fixtures.clj and add all the helpers etc in there...

1
👍 1
NoahTheDuke18:01:07

and then import that into each of my test files?

noisesmith18:01:11

idiomatically game.core-test should be under test/ rather than src/ and should have unit tests for game.core

seancorfield18:01:34

(i.e., game.fixtures namespace, yes, require into test namespaces as needed)

👍 1
NoahTheDuke18:01:44

it is, i have the separate test/ folder which otherwise matches the folder structure of my src/ folder

SoV418:01:56

I just want one massive handler object with flags for everything ... maybe i am too lazy even for clojure xD

noisesmith18:01:11

@sova that's closer to what interceptors in pedestal do - the advantage are they are data which is queryable / updatable etc., the disadvantage is they require an execution model on top of that data, where ring middleware are just functions that return functions

noisesmith18:01:51

nothing stops you from pulling in pedestal's interceptor lib and turning each middleware into an interceptor etc. - but that might also be a waste of your time :D

didibus18:01:36

> Yada’s model is to provide a complete and correct HTTP response to a request. Ring and Pedestal models piece together partially correct responses based on small transformations in middleware or interceptors. From: https://purelyfunctional.tv/mini-guide/clojure-web-servers/

didibus18:01:56

@sova > It achieves this by providing you with a highly-configurable handler that is general enough to use in the vast majority of cases. Rather than coding the handler, the developer only has to configure one. This declarative approach provides greater scope for accuracy, consistency and re-use. Definitly sounds like exactly what you asked for :p

SoV418:01:59

Rock solid!

SoV418:01:56

i just threw wrap-session in there for now... but i will consider yada in my next project which will be fairly soon

SoV418:01:59

🙂 thx

Pavel Klavík18:01:14

Hi, is there some Clojurescript library to parse css strings in hiccups? I need to transform them into maps for Reagent.

SoV418:01:57

hmmm neat question

Pavel Klavík18:01:05

I was using str/split for : and ;, but it fails on things like

background: url("data:image/jpeg;base64,.....");

solf19:01:55

I have made a simple defn-spy macro, that wraps a function and saves it's arguments and return value in an atom every time the function is called.

(defmacro defn-spy [& args]
  (let [f (fn [name params body]
            `(defn ~name ~params
               (let [result# (do [email protected])]
                 (swap! spy-atom update (symbol (quote ~name))
                        (fn [calls#]
                          (conj calls# [~params result#])))
                 result#)))]
    (if (string? (second args))
      (let [[name _doc params & body] args]
        (f name params body))
      (let [[name params & body] args]
        (f name params body)))))
Before the function f in the let was the whole macro, but as I had to take into account functions with a docstring I had to write an if to handle those two cases (with and without docstring). Any idea how to write it better?

ghadi19:01:24

without speaking to the macro itself, this idea can be accomplished dynamically without a macro by using alter-var-root

solf19:01:16

Oh right. I've seen alter-var-root used before but I've never used it myself

ghadi19:01:16

kind of like clojure.spec.test's instrument

Pavel Klavík19:01:52

I did a quick hacky solution to solve the problem I found, if someone else is intersted:

(defn append-char
  "Appends the given character to the last string in tokens."
  [tokens ch]
  (update tokens (dec (count tokens)) str ch))

(defn parse-style
  "Parses a style value from a string."
  [style]
  (->> style (reduce (fn [[tokens inside-string] char]
                       (let [str-char (str char)]
                         (cond (= str-char "\"") [(append-char tokens str-char) (not inside-string)]
                               (and (#{":" ";"} str-char) (not inside-string)) [(conj tokens "") inside-string]
                               :else [(append-char tokens str-char) inside-string]))) [[""] nil])
       first butlast (map str/trim) (partition 2 2)
       (map (fn [[k v]]
              [(keyword k) v]))
       (into {})))

clj 1
David Pham21:01:28

In ClojureScript, are their performance benefits to use amap/areduce vs map/reduce?

SoV421:01:42

map is lazy , amap forces a full array to be assigned right then... if you can ensure the array never has to grow maybe amap is smart(er) marginally, but if you are unsure of the number of elements map is just as good or better ... someone with experience correct me if i'm wrong

Akshay C. Gollapalli21:01:16

When you eval a string from read-string, are all the symbols qualified vs the symbols required and referred in that namespace, or is it only against the symbols in clojure.core?

hiredman21:01:40

eval is just like you typed it in to a repl

hiredman21:01:50

however people often don't understand the different parts of runtime vs. compile time state that effect how symbols are resolved

hiredman21:01:26

specifically, when you define a namespace in a file, it is the current namespace (`*ns*`) only while it is being loaded, after which, presumably when you are actually running your code, it is not the current namespace, so things that use the current namespace for symbol resolution (like the compiler which is what eval calls) don't know anything about aliases or imports or whatever in your namespace because at runtime the current namespace is not your namespace

hiredman21:01:08

this can be confusing because the repl preserves the current namespace between calls to eval

Akshay C. Gollapalli21:01:40

So if I have a function in namespace a that calls eval on a list, then when the function that is actually called, the compiler might not be using the namespace the function is in?

hiredman21:01:55

almost certainly will not be

Akshay C. Gollapalli21:01:12

In other words, despite the function being in namespace a, the compiler doesn't use namespace a for evaluation.

hiredman21:01:23

at runtime the most common current namespace is clojure.core, just because that is the default value of *ns*

hiredman21:01:39

the function is not "in" that namespace

Akshay C. Gollapalli21:01:40

Hmm, so do I need to specify the namespace as part of the function before eval?

hiredman21:01:14

the only thing in namespaces are names

hiredman21:01:47

the function is a value

hiredman21:01:56

the namespace has some name defined as that value

hiredman21:01:10

some other namespace could have another or the same name defined as that same value

Akshay C. Gollapalli21:01:51

I should have joined this slack a couple years ago. All of that is new to me

hiredman21:01:26

you can bind *ns* to a particular ns if you like before calling eval

hiredman21:01:56

I've even seen code that would start a namespace off with (def this-ns **ns**)

hiredman21:01:25

that way you can capture the load/compile time value of *ns* as a runtime value that won't change

andy.fingerhut22:01:38

Most people avoid using eval and so a lot of these issues don't arise for them.

1
Akshay C. Gollapalli22:01:58

Thanks @hiredman , that got me past a hurdle.