Fork me on GitHub
#beginners
<
2019-05-29
>
noisesmith00:05:30

@lockdown- the common idiom you see for that is (->> z (fun-b) (fun-a x y))

2
noisesmith00:05:44

(it expands to what you showed, but makes the steps of processing clear)

johnj00:05:06

I see, but in general the concept is not crazy then?

noisesmith00:05:19

one exception I'd make is (println "foo" "bar" (pr-str baz-data)) - that's perfectly clear with the nested call

johnj00:05:25

crazy as in leads to sloppy/unmaintainable code

penryu00:05:47

Not crazy, but can be hard to read/maintain.

noisesmith00:05:03

it's a straightforward way to show a series of operations that use the result of the previous operation, which is legitimate, and clearly what you ware doing

noisesmith00:05:44

you can also use a let block to bind intermediate data, but I'd prefer to see -> or ->> if each step only consumes data from the immediately preceding step

penryu00:05:46

Right. To clarify, the idea of using the evaluation of one form as a parameter to another is not innately bad/crazy/sloppy. But there are a number of ways to make it more maintainable/easier to read: ->, ->>, let, et al.

noisesmith00:05:00

oh yes, agreed

johnj00:05:55

@noisesmith the let approach is what I'm doing, fun-b inside fun-a in a let but fun-b side effects, was thinking of passing a fun-b call as an arg to make fun-a pure

noisesmith00:05:57

but what you do above doesn't pass the call - it passes the result of the call (maybe this distinction doesn't matter here)

noisesmith00:05:33

but you are on the right track - deal with side effects by pulling it up the abstraction stack to callers, rather than pushing it down the stack into callees

johnj00:05:46

yeah, fun-b just returns data, making fun-a just work with values

noisesmith00:05:49

that way more of your code is pure

noisesmith00:05:41

sometimes an OO mindset (or something similar) makes people want to hide IO at the bottom of a stack of abstractions, which just multiplies your problems by increasing the amount of side-effecting functions in the codebase

johnj00:05:01

that's what I'm afraid of, but here, these are the same two functions, not more code

johnj00:05:14

when making the change

johnj00:05:28

don't really want to hide it, just group them better to have simpler/better tests

noisesmith00:05:01

right, getting the value out of IO and passing it into the pure function is the right way to do this when possible

johnj00:05:53

great, thanks

johnj00:05:12

recently saw a video where side-effects are described with maps (returned by functions) and then a "system" takes the descriptions and executes all the side-effects

johnj00:05:02

the author mentioned that is how re-frame does it, but sounded a bit overkill for small apps

noisesmith00:05:40

yeah, this is an adaptation of a lib for JS where structured data defines updates to be done in a batch because it's a bad idea to do DOM updates directly

noisesmith00:05:02

but it happens to accomplish this same thing I'm advocating - moving IO and state to the top of your stack of function calls, and pushing pure things to the bottom

johnj00:05:57

well wait, I do a need a third function, can't just put something like (->> z (fun-b) (fun-a x y)) at a top-level

noisesmith00:05:01

well, you could but it would be bad form

johnj00:05:14

yeah, gross, there should be no side-effects until -main is called

johnj00:05:05

at least in my case, I think it should be that way

noisesmith00:05:09

right - and a common gotcha for beginners is that even a (def foo ...) will do side effects at file load or even just compilation if ... has side effects

johnj00:05:59

@noisesmith by compilation you mean if someone is doing AOT?

seancorfield00:05:30

It's not always practical to hoist all of your side-effects right up to the top tho' -- much depends on how write-heavy your app is -- but separating pure and impure is a worthy goal.

seancorfield00:05:08

This is an experiment of mine in terms of trying to do that separation completely https://github.com/seancorfield/engine in a generic way. The resulting code was hard to read and certain convenient idioms were hard to express.

johnj00:05:27

true, adding a third fn for every two fns to make one pure doesn't make sense, but adding one to make 10-20 pure seems worth it

johnjelinek02:05:55

hihi all, I'm looking at generating an oauth api token for a service I want to integrate with. I'm not using ring in my application so far, but I found this library: https://github.com/weavejester/ring-oauth2 -- has anyone used this in a non-ring context?

jumar06:05:38

Based on your info you shouldn't need to generate oauth token at all

jumar06:05:42

What Sean mentioned is usually enough. Use clj-http client or equivalent to get access token based on the "authorization code" you receive when the user accepts your app as authorized on the OAuth provider's consent page.

seancorfield03:05:10

@johnjelinek Are you trying to integrate with that service as an OAuth client? Or are you hoping to be an OAuth provide so the service can call you?

seancorfield03:05:27

@johnjelinek Then you should be able to get the token from the service somehow by authenticating with it.

johnjelinek03:05:43

@seancorfield: at the moment, I want to spin up a datomic ion (HTTP Direct) to receive the auth code from the external service. Then in my repl, I can initiate the oauth2 flow and the service will turn it into a token that I can play with for the duration of my repl session

johnjelinek04:05:44

it looks like this lib is no longer maintained, but it seems to work in a non-ring context so far (for the initiation of the oauth2 flow) https://github.com/DerGuteMoritz/clj-oauth2

seancorfield04:05:51

I've never used a library for dealing with OAuth tokens as a client. Just simple HTTP calls has always been enough.

Ahmed Hassan05:05:38

In which directory I should keep my my main.js JavaScript file when using Pedestal to be loaded in index.html? In the body of function named index which is served by interceptor I have put [:script {:src "resources/public/js/main/main.js"}].

Eric Scott13:05:37

Hi- A question about namespaces Suppose you have Two libraries: A-lib and B-lib, each with a module x.core. ;; In A-lib:

(ns x.core)
(defn foo [] (println "Greetings fromm A-lib"))
;; In B-lib:
(ns x.core)
(defn foo [] (println "Greetings fromm B-lib"))
Now you start an app myapp and have these dependencies:
...
                 [A-lib/x "0.1.0-SNAPSHOT"]
                 [B-lib/x "0.1.0-SNAPSHOT"]
The ambiguity seems to be resolved by arbitrarily picking one:
(ns myapp.core (:require [x.core]))
(x.core/foo)
You get:
"Greetings from A-lib"
Assuming you have no access to the source code of either A-lib or B-lib, is there a way to retroactively disambiguate our 'x' module, either in our project declaration or in the require clause? Thanks.

mfikes13:05:05

@eric.d.scott At the bottom, the source for dependencies are resolved from the classpath

mfikes13:05:33

(So it is really a question below Clojure/Script.)

mfikes13:05:00

Or, another way to put it: Clojure/Script provides no direct way to deal with this issue.

Eric Scott13:05:14

That's what I suspected. I was hoping there would be some provision for doing this. Thanks.

eskemojoe00714:05:22

Coming from the pytest python world, I'm used to having parametrize to run a test with multiple cases. What's the clojure way of doing that. Run a simple function with input/output pairs so I don't have to write so many is statements. (https://docs.pytest.org/en/latest/parametrize.html for reference)

eskemojoe00715:05:43

I guess are is pretty close to the same thing.

haus15:05:47

@UHUG06517 I’ve also seen ‘doseq’ used to achieve that. I don’t love that solution but it gets the job done

RafaMedina15:05:37

which framework recommend for a restapi?

lispyclouds15:05:17

@comparalf you could also try https://github.com/zalando-stups/swagger1st bit higher level, works directly from the API in a YAML file.

RafaMedina15:05:38

I'll take a look at both, thanks!

Mario C.15:05:09

I am trying to use the figwheel repl from nRepl. I am using this https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl as a guide but I keep getting Could not locate cljs/stacktrace__init.class or cljs/stacktrace.clj on classpath

Mario C.15:05:31

When calling (use 'figwheel-sidecare.repl-api)

Mario C.15:05:03

I added "src/main/cljs" to my source-paths but keep getting the same error

lilactown15:05:58

@mario.cordova.862 what version of CLJS are you using?

Mario C.15:05:00

To start the repl I use lein with-profile clj,dev repl :headless and I have added it to the source-paths for both clj, and dev profiles

lilactown15:05:35

try updating to the latest CLJS version - should be 1.10.something

Mario C.15:05:52

Let me check the dep tree

Mario C.15:05:33

Still get the issue, even after fixing the dependency issues

Mario C.15:05:49

Actually i think i know what the problem is

Mario C.15:05:29

It wasn't that I had old CLJS. It was that I didn't have CLJS listed at all in my dependencies for those profiles 😅

Mno15:05:52

:thinking_face:

drewverlee15:05:36

Finally getting around to reading straight through Joy of clojure. I found this remark very interesting > you generally shouldn’t assume seq has been called on your collection arguments, but instead call seq in the function itself and process based on its result. Using this approach fosters a more generic handling of collections, a topic that we explore in great detail in chapter 5. It seems i can't wait for chapter 5 🙂, does this advice suggest that you call seq on all collections arguments? I don't see this widely done, though maybe im not looking hard enough.

drewverlee15:05:18

I mean, i'm not sure how it helps really. dont most functions that work on collections convert them to seqs?

noisesmith16:05:01

I think most of the time when we work with things as seqs, we are using functions that already implictly call seq

noisesmith16:05:29

so this would be advice for making new collection functions that aren't built on the existing ones?

evocatus18:05:02

How can I update all dependencies in project.clj? Is it generally a good idea to update them all?

noisesmith19:05:50

at least the project owner diisapproves of that, but there's a plugin (lein-ancient) that helps find deps that have new versions and even automatically update your project file iirc

eskemojoe00719:05:05

Struggling with time stamps. I have a luminus h2 database with a timestamp field. I have a test that posts a time using java.util.Date. then I check to make sure it updated and I get a difference like:

- {:last_login #inst "2019-05-29T19:28:30.456-00:00"}
+ {:last_login #object[java.time.LocalDateTime 0x7c1b5b8f "2019-05-29T15:28:30.456"]}
Test looks like:
(let [time (java.util.Date.)]
      (is (= 1 (db/update-time!
                t-conn
                {:user_name  "sam_smith"
                 :last_login time})))
      (is (= {:user_name   "sam_smith"
              :last_login time}
             (db/get-user t-conn {:user_name "sam_smith"}))))
Any advice on working with java datetimes like this?

seancorfield19:05:39

Sounds like something in Luminus has extended clojure.java.jdbc's protocols for column reading to produce Java Time values.

seancorfield19:05:14

Which is probably a good thing -- and might encourage you to also use Java Time instead of java.util.Date?

seancorfield19:05:24

(java.time.LocalDateTime/now) is the modern equivalent to (java.util.Date.)

eskemojoe00719:05:14

Ahhh...That makes more sense to me. Their tutorial had just used java.util.Date. so I grabbed that one. Worked like a charm!

seancorfield19:05:58

Yeah, a lot of tutorials still refer to java.util.Date. because it's been around forever but since Java 8 came out that's a bit of an anachronism really... but the default behavior of clojure.java.jdbc is to traffic in the older types (`java.sql.Date` and java.sql.Timestamp as well as java.util.Date) -- because that's what the JDBC drivers all do by default.

Tangible Dream21:05:28

I’m trying to test with clojure. For some reason I am not able to include clojure.test into my code

Tangible Dream21:05:48

(ns clojure.test)
(testing "Arithmetic"
  (testing "with positive integers"
    (is (= 4 (+ 2 2)))
    (is (= 7 (+ 3 4))))
  (testing "with negative integers"
    (is (= -4 (+ -2 -2)))
    (is (= -1 (+ 3 -4)))))

Tangible Dream21:05:58

clojure blah-test.clj 
Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: testing in this context, compiling:(/Users/test/blah-test.clj:2:1)
	at clojure.lang.Compiler.analyze(Compiler.java:6792)
	at clojure.lang.Compiler.analyze(Compiler.java:6729)
	at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3813)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7005)
	at clojure.lang.Compiler.analyze(Compiler.java:6773)
	at clojure.lang.Compiler.analyze(Compiler.java:6729)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6100)
	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5460)
	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4022)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7001)
	at clojure.lang.Compiler.analyze(Compiler.java:6773)
	at clojure.lang.Compiler.eval(Compiler.java:7059)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.Compiler.loadFile(Compiler.java:7452)
	at clojure.main$load_script.invokeStatic(main.clj:278)
	at clojure.main$script_opt.invokeStatic(main.clj:338)
	at clojure.main$script_opt.invoke(main.clj:333)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: testing in this context
	at clojure.lang.Util.runtimeException(Util.java:221)
	at clojure.lang.Compiler.resolveIn(Compiler.java:7299)
	at clojure.lang.Compiler.resolve(Compiler.java:7243)
	at clojure.lang.Compiler.analyzeSymbol(Compiler.java:7204)
	at clojure.lang.Compiler.analyze(Compiler.java:6752)
	... 21 more

Tangible Dream21:05:59

same with

(require '(clojure.test))

zlrth21:05:23

which does the same thing :refer :all does. neither are recommended nowadays. to make yours work, you could make it something like:

(ns blah-test
  (:require [clojure.test :refer :all]))

Tangible Dream21:05:23

let me try the repl

zlrth21:05:13

note that (ns ...) at the top of your file should not be clojure.test (that’s the name of the testing library) but something matching your file hierarchy

hiredman21:05:31

your problem is you created a namespace named clojure.test in the repl with your first ns form, which isn't the clojure.test you want

Tangible Dream21:05:44

That worked zirth

Tangible Dream21:05:32

Thanks for the necessary step I was not adding @hiredman

hiredman21:05:39

if you restarted your repl and tried again with a different namespace name it would likely work

walterl22:05:08

Hi there! This is the first time that I've had to handle circular function calls in Clojure. In the snippet below parse-expr calls parse-oper-exprs, and parse-oper-exprs calls parse-expr to parse sub-expressions. What the is idiomatic way to avoid the "Unable to resolve symbol: parse-expr in this context" error caused by calling parse-expr on line 3 of the code below? Is there a way to lazily refer to a function that will be defined "later"?

clojure
(defn parse-oper-exprs [oper exprs]
  (->> exprs
       (map parse-expr)
       (map #(str "(" % ")"))
       (string/join (str " " oper " "))))

(defn parse-expr [oper exprs]
  (cond
    (= oper "+")
    (parse-oper-exprs "+" exprs)
    ;...
    ))

zane22:05:55

Have a look at clojure.core/declare.

💯 1
walterl22:05:06

Great! Thanks, @U050CT4HR! I'm clearly still getting to know the core language.

zane22:05:27

Everyone starts somewhere. 🙂 Keep going!

noisesmith23:05:28

other options include letfn which allows circular references, and trampoline which allows a work around for no tail call elimination, but here declare should be fine

walterl23:05:29

Wow! Thanks, @noisesmith. That brings this issue up to 3xTIL for me 😜

noisesmith23:05:18

declare is the straightforward one, and what you usually want

walterl23:05:59

Yeah, it does seem like the best fit for the situation. Regardless, I appreciate the introduction to more core functions I was unaware of 🙂