Fork me on GitHub
#beginners
<
2020-06-09
>
mjw00:06:59

I’m incorporating Integrant into a side project, and I’m wondering if it’s common to put all of the init-key and halt-key! multimethods in the same file, or if folks more often break up those declarations into multiple files and rely on side-effectful imports to ensure the system is composed properly?

Phil Hunt07:06:42

Hi. Has anyone got suggestions for introductory hands-on Clojurescript material. Recent enough that it's not going to collapse in a muddle of broken dependencies and with enough context that you don't have to be thoroughly conversant with the modern JS ecosystem to know what's going on?

kosengan09:06:27

Looking for good learning resources for Clojurescript+Re-frame/Reagent!

Jim Newton10:06:36

What is the correct way to write tests for function which are defined using memoize .? Before testing a function, my test case would often like to erase the cache.

mloughlin11:06:02

There's no way to clear the memoize cache, you may only define a new memoized version One way of implementing it is to define your pure un-memoized function (defn f* [] ...) and then create a memoized version (def f (memoize f*)) Yes, this will use a load of memory.

mloughlin11:06:51

It's recommended to use a different type of cache if memoize doesn't fit your performance requirements

jkrasnay11:06:17

You can just create a memoized version in a let form for the duration of your test:

(let [f (memoize f*)]
  ; test f here
  )

✔️ 4
Jim Newton13:06:52

if I memoize a function which is already memoized, does that work? when the let finishes, is the old cache restored. Sorry, I don't really know how atoms work.

Jim Newton13:06:05

It is not just a question of directly testing memoized functions. one of my low level functions is memoized, but I need to test the api functions. They effectively rely on the memoized data. I'd like to test those api functions independently of the current state of the cache.

Jim Newton13:06:01

can I do something like

(binding [f (memoize f)] ...)

Jim Newton13:06:39

so that low level calls to f not explicitly mentioned in my tests, will use the dynamically bound f

jkrasnay13:06:44

Like most things in Clojure, memoize doesn’t change the function its given, it returns a new function, so yes, the original unmemoized function continues to exist in its original state.

jkrasnay13:06:19

I believe your binding solution will work. You just have to declare f as ^:dynamic

Jim Newton13:06:04

hmm. so for memoize functions to be testable, they need to be dynamic.

mloughlin14:06:38

Since memoize makes the pure function stateful, you could pass the memoized function as a parameter. What aspect are you testing?

Jim Newton14:06:06

I explained before, but again: one of my lowest level functions (the work horse) is implemented with memoize. I want to test the API functions which don't call this low level function explicitly. But since the value is memoized, the tests only test according to the current state of VM. I'd like the test cases each to assume there's no cache. That seems to me like a much safer test.

mloughlin14:06:29

IMO only if I was testing caching performance and needed control over that, in which case maybe memoize is not the right tool to use. You're either unit testing the underlying function, or testing the stateful caching aspect. The reason I say that is because memoize expects a referentially transparent function, so I think it's fair to say there's a decent guarantee that what you get out is what you put in. I don't think there's a reason not to add ^:dynamic to the fn for tests, but I don't see a huge reason to do it either I'm working off of information in Clojure: The Essential Reference from Manning and (source memoize)

mloughlin14:06:24

It seems like a judgement call 🙈

Jim Newton15:06:04

suppose the function you are memoizing calls some other function. and in the mean time (in the live session) you edit that other function. Now when you run your tests, is the information cached from v1 or v2 of that other function? Even if it is referentially transparent, functions can be edited while you are editing. This means it is likely that you'll get different results from testing in your live session vs testing in stand-alone.

Jim Newton15:06:50

Basically memoizing is side-effectful computation, which makes it difficult to test.

mloughlin16:06:03

Fair point about redefinitions in the REPL

Ludwig10:06:52

hi everyone, is there an open source project that uses datomic in extense where I could study its code?

Alexandre Grison11:06:12

Did you take a look at this repository: https://github.com/Datomic/mbrainz-sample

👍 8
Ludwig11:06:55

Thanks, I will take a look at it

amirali12:06:50

Hi guys.I want to iterate over a nested vector and delete some elements (for example deletes "5" whenever finds it) and I want to use reduce / transducer instead of nested loop (unlike imperative systems). I do understand that the answer isnt nested loop recur. so i went for reduce/transdures but my code fails each time. Can u give me an example of doing so?

pilku13:06:47

@U014CTZJK2S (clojure.walk/postwalk #(if-not (and (coll? %) (seq %)) (* 10 %) %) [[1 10] [2 10]]) This is using Clojure.walk and not reduce, also can you post here what you are trying so that we can check if we can fix it.

MorongÖa13:06:07

Trying to make start a repl this error. Run a project

MorongÖa13:06:25

Could not locate cognitect/rebl__init.class

MorongÖa13:06:49

Caused by: .FileNotFoundException: Could not locate cognitect/rebl__init.class, cognitect/rebl.clj or cognitect/rebl.cljc on classpath.

MorongÖa13:06:03

What am I missing?

Alex Miller (Clojure team)13:06:16

not sure what you're doing

Alex Miller (Clojure team)13:06:26

what do you mean by "make rebl"

MorongÖa13:06:09

Trying to make start a repl this error. Run a project

Alex Miller (Clojure team)13:06:04

the error is saying that you're trying to run rebl, but rebl is not on the classpath. but I'm not sure what you're doing so I don't know how to help

MorongÖa13:06:47

How do I add rebl to my class path than?

MorongÖa13:06:22

Ahh, I see what the issue is. Thank you

Maik Wild13:06:01

Hi, I have been working with clojure fixtures. I am running a test and take a screenshot at the end, everything working fine. My question is, is there a way to get the name of the function in the fixture? I want to use it as the file name of the screenshot. Code:

(defn fixture-selenium
  [f]
  (try
    ;; setup here
    (f)
  (finally
    (println f) ;; Prints cider.nrepl.middleware.test$test_vars$fn__38755$fn__38760
    (save-screenshot)))

Maik Wild13:06:02

I tried (meta f), but meta info is nil

Romit Gandhi14:06:12

Currently whenever I add new query in queries.sql , I have to close and reopen REPL everytime to test it. I have also tried to reload in the REPL using (use '[demo.db.core :as db] :reload-all) but it didn't work. Currently I'm using Calva plugin in VS Code. So is there any way to test it without closing and reopening REPL everytime ?

bartuka14:06:20

Are you using hugsql to read the queries.sql file?

bartuka14:06:08

The only thing I do is to reload the namespace where hugsql/def-db-fns importing the file is used.

Romit Gandhi14:06:32

I tried to reload the db.core namespace but it didn't reflect the new changes.

bartuka15:06:15

can you share more code of your db.core namespace?

Romit Gandhi15:06:18

I followed the same question of stack overflow and used use as well as require but nothing worked, even I tried to reload using Calva plugin shortcut but nothing worked.

Romit Gandhi15:06:51

(conman/bind-connection *db* "sql/queries.sql") , using this way I'm getting queries.sql file.

Romit Gandhi15:06:00

This is the file, it is default code of luminus framework.

dharrigan15:06:04

I seem to recall a trick, discussed a while ago that involved some sort of compliation of the code during startup/repl? I can't recall. @alexmiller I think you mentioned it?

dharrigan15:06:55

thank you dan 🙂

readyready1572816:06:13

I'm still trying to figure out the purpose of dynamic variables when let exists

readyready1572816:06:49

What is the use case here

Alex Miller (Clojure team)16:06:07

dynamic vars let you pass context down the call stack but not as parameters

readyready1572816:06:22

Oh so it's just dynamic scope then?

Alex Miller (Clojure team)16:06:40

generally, you should avoid dynamic variables imo - usually it's better to make that stuff explicit in the call

readyready1572816:06:37

That's an ... interesting feature

Jim Newton16:06:14

dynamic variables are very powerful, but with power comes responsibility.

vlad_poh17:06:37

what is the right away to share global variables in clojure. e.g i have 3 different namespaces with ring routes and I want to pass db information

vlad_poh17:06:01

Another namespace with those variables ?

hiredman17:06:09

Don't make globals, pass arguments

✔️ 3
hiredman17:06:09

A common way to pass extra stuff to ring handlers is middleware that assocs that stuff into the request map

☝️ 3
vlad_poh18:06:24

so i'm using the environ library to hold db information

vlad_poh18:06:42

but not sure how to pass it to each route without calling it each time

phronmophobic18:06:10

without calling what each time?

vlad_poh18:06:47

yep see Yogthos example here https://yogthos.net/posts/2015-10-01-Compojure-API.html he repeats (:db config) in each route

phronmophobic18:06:35

it looks like the responses are written in-line in this example. for that particular use case, repeating (:db config) seems reasonable. if your views are defined elsewhere, then you can have middleware provide the db for each route

👍 4
Ludwig20:06:02

you could create a dynamic var and bind it in a ring middleware when a request arrives

👍 3
dharrigan18:06:52

I pass "configuration" as a map (usually I leave it the in last position, if a reasonable n-arity is required)

dharrigan18:06:29

I also use juxt clip to manage some state, like a shared database connection, or a redis connection. That state is held in the map and passed around.

bartuka18:06:41

you guys tried aero ? I just noticed that I am not using leiningen profiles to decide which aero #profile to choose. I have (read-config "config.edn" (System/getenv "PROFILE")) ... someone knows if you can use the profiles passed as lein with-profile PROFILE and catch that value to be used in aero?

dev-hartmann22:06:51

I think the with profile will either be passed as an argument to the application on startup

dev-hartmann22:06:45

like one of the jvm switches -Dprofile test

dev-hartmann22:06:16

this might be of help

dev-hartmann22:06:41

Great, so why is this more useful? The big benefit I've discovered is the .lein-env file in the root of my project. This file holds a map of environment values that can be useful during development. There are 2 ways to create this file.

The RIGHT way as set out by the creator of the project, and,
The WRONG way as in how I use it
environ also has a plugin available lein-environ which sucks profile specific settings from ~/.lein/profiles.clj and/or a project specific profiles.clj (which should not be checked into source) and creates the .lein-env file when Leiningen does its thing (effectively making .lein-env a transient file). A .lein-env file looks a little something like this.

dev-hartmann22:06:06

excerpt from the above article

dharrigan18:06:49

Yes, I use aero extensively

dharrigan18:06:58

It's part of my juxt clip setup 🙂

dharrigan18:06:12

But I don't use lein, I'm a deps guy 🙂

bartuka18:06:51

ouch =/ this app is a bit old now, I was happy with leiningen and decided to leave this way rsrs

dharrigan18:06:39

Perhaps if you ask in #juxt channel, you may get an answer?

bartuka18:06:48

Good idea, thanks

jpmicena22:06:17

Hi all! I am trying to understand how to use the component library from Stuart Sierra, but I am struggling to understand the record part. In the example, he creates a record using (defrecord Database), and then a constructor using map->Database. My doubt is: What exactly this map-> is doing? Is it mutating the Database record? How it was created? Thank you in advance!

seancorfield22:06:11

@joao.micena When you use (defrecord Foo), Clojure automatically generates two constructor functions for you: map->Foo that accepts a regular hash map and returns a Foo record populated from the hash map and ->Foo that accepts positional parameters matching the fields you declared in defrecord (and returns a populated record).

👍 4
seancorfield22:06:32

Records -- like other Clojure data structures -- are immutable.

seancorfield22:06:06

user=> (defrecord Foo [a b])
user.Foo
user=> (->Foo 1 "Two")
#user.Foo{:a 1, :b "Two"}
user=> (map->Foo {:a "One" :b 2})
#user.Foo{:a "One", :b 2}
user=> 

jpmicena23:06:23

Hmmm I see, this helps a lot, thank you! By the way, I am also using your usermanager-example repo and it has been really helpful!

8