Fork me on GitHub
#beginners
<
2018-06-01
>
stardiviner01:06:16

What does the :deprecated "1.2" metadata in ring.middleware.file-info means? Means it will be remove in future? on now it is already deprecated when version is newer than 1.2? I check it out in the source code file in "ring/ring-code/1.5.1/file_info.clj". But have not found other new implemented function.

noisesmith01:06:26

sometimes a function or namespace is deprecated without having a viable replacement

seancorfield01:06:40

Deprecated just means "it's recommended you don't use this function any more" and the version indicates the release in which it was marked deprecated. Sometimes a better option is provided, sometimes you're expected to go find a different solution elsewhere.

seancorfield01:06:34

According to the documentation for ring.middleware.file-info: "Prefer the ring.middleware.content-type and ring.middleware.not-modified middleware instead."

nakiya03:06:00

I'm using compojure backend with re-frame front end. I need to listen to changes on backend. Right now I poll periodically. What's the best way to employ here? Websockets? Which libraries should I use?

val_waeselynck03:06:07

Polling is not so bad - it keeps your backend stateless.

ScArcher04:06:29

What's the best way to handle error in clojure?

ScArcher05:06:08

I'm calling a REST service and I'm trying to understand if I should check for and return an error value in the map I would normally return, or should I throw some sort of exception.

seancorfield05:06:30

@scott.archer I can tell you what we do at work: we have an exception handling middleware that catches any exceptions from our handler, logs it, possibly sends an email to engineering (for unique exceptions), and hands back a 400-series status with some neutral information about the failed request (we put a unique ID in every request that goes in the logs with the exception and is reported back to the client so we can correlate if they ever ask about a specific failure).

ScArcher05:06:00

That sounds like a good approach. I'm referring to a REST client though.

ScArcher05:06:01

I just implemented returning a map {:error "this is the error"}.

ScArcher05:06:14

It seems to be OK, I was just curious if that was normal practice or not.

ScArcher05:06:39

I googled it and found a blog where they wrote a macro and started talking monads, I'm not there yet 🙂

seancorfield05:06:46

@scott.archer Sorry, I didn't read "calling"... So, yeah, on the client side, you'll want to treat non-200 responses as errors as well as failures to get a response (server times out, returns 500 Server Error, etc).

seancorfield05:06:24

Although bear in mind REST APIs will return non-200 responses for "valid" calls that could not be satisfied: 404 usually means "the requested resource was not found" so your program might "expect" that.

seancorfield05:06:58

403 (Forbidden) might be "expected" too, possibly some others, depending on how sophisticated the API is.

ScArcher05:06:04

Right, I'll add code to handle http response codes, for now I'm just handling error returned by the REST API itself.

seancorfield05:06:26

The REST API returns errors with a 200 OK status?

ScArcher05:06:34

I just wanted to understand if it was best practice to return those errors in a map vs thrrow some kind of error.

ScArcher05:06:45

Yes, it's a poor REST API.

seancorfield05:06:58

Well, "it depends". Exceptions are the right choice for "this is unexpected" 🙂

ScArcher05:06:00

you get an XML response with an <error>This is your error</error>

seancorfield05:06:14

Oh, XML... Ugh! Sorry...

ScArcher05:06:28

Yeah I was about to ask if there was a better way to parse XML in clojure.

seancorfield05:06:46

org.clojure/data.xml?

ScArcher05:06:07

right now i'm just using clojure.xml

ScArcher05:06:09

(into (hash-map) (map (fn [x] {(:tag x) (first (:content x))}) (:content (first data))))))

ScArcher05:06:28

the structure is simple so far, so I can get away with this, but it's ugly.

seancorfield05:06:51

Yeah, there really are no shortcuts with XML -- every solution is ugly 😞

seancorfield05:06:06

Can't you request responses in a different format, like JSON?

ScArcher05:06:28

No, unfortunately, only XML. I've written this same type client in C#, Java, Ruby, just using it to learn Clojure.

ScArcher05:06:04

On a good note, if I can parse ugly XML in clojure, I should be good to go 🙂

ScArcher05:06:59

I'll try org.clojure/data.xml. it's based on stax, which is "better" than DOM.

ScArcher05:06:33

@seancorfield thanks for being so responsive and helpful on here. As someone who has wanted to learn clojure for years, this slack has really helped me stick with it. I think I've bought 6 books on clojure, but always got stuck when it came to applying the language to a real-world problem.

seancorfield05:06:54

Happy to help! When I got started (2010), there was really only the mailing list and any conferences/user groups I could attend -- I want the experience to be better for beginners now 🙂

seancorfield05:06:27

@scott.archer We first started using Clojure at work in 2011 for very small things: little command-line utilities, small cron jobs, a few small library tasks (called from our legacy code)...

ScArcher05:06:21

That's cool. I don't have any opportunity to use Clojure at work. I think it would be great if I could. I'm in management now though, so I don't do much actual development work anymore 😞

seancorfield05:06:30

...now we have almost 80k lines of Clojure at work and all our new dating sites run on top of it, as well as almost all of our backend infrastructure...

seancorfield05:06:38

Clojure build/config 47 files 2510 total loc
Clojure source 259 files 60080 total loc,
    3259 fns, 703 of which are private,
    377 vars, 41 macros, 56 atoms,
    458 specs, 19 function specs.
Clojure tests 147 files 18986 total loc,
    23 specs, 1 function specs.

ScArcher05:06:56

That's impressive!

seancorfield05:06:16

Ah, it's a hard switch from management back to (junior) coding if you dramatically change languages.

ScArcher05:06:19

I love the 56 atoms count.

ScArcher05:06:43

60k lines of code 56 atoms to manage state ha.

seancorfield05:06:44

But maybe you could find a management role at a Clojure shop that had options for being more hands on? Who knows...

seancorfield05:06:07

Yeah, we're really try to reduce the number of atoms. Mutable state is bad!

ScArcher05:06:12

I may go back into development at some point in the future. We'll see how the manager thing goes.

seancorfield05:06:37

I've done my time as a manager... I liked it in England, I don't much like it in America.

ScArcher05:06:02

I have a good team, so from that perspective it's really nice, but I still really like problem solving and writing code.

seancorfield05:06:11

My style is a bit too... brutal... for U.S. sensibilities 🙂

seancorfield05:06:40

My favorite role has always been (senior) Software Architect for about the last two decades.

seancorfield05:06:59

Big, complex puzzles, but still hands on.

ScArcher05:06:51

Yep, the fun stuff!

nakiya07:06:19

can declare be used for other namespaces? i.e. (declare other-namespace/my-fn)

nakiya07:06:10

^^ I am trying to sidestep an error: Cyclic load dependency

seancorfield07:06:16

@duminda No, if you try that, you'll find it doesn't work.

seancorfield07:06:54

You'll likely get either Can't refer to qualified var that doesn't exist or Can't create defs outside of current ns

seancorfield07:06:31

Clojure namespaces don't allow cyclic references across them so you'll need to do some refactoring to break that cycle.

👍 4
sveri07:06:16

@chester.heng I have a template that covers your usecase exactly. It also provides basic login / logout facilities. Re-frame (which sits on top of reagent) on frontend for SPA and a clojure backend. Its all in the template and should get you going. The only downside is, I was not able yet to document it: https://github.com/sveri/closp If you want to give it a shot and need help you can come to #closp or ask here. I check this slack several times a day.

troglotit09:06:37

Hey! What is the default way(not terrible performance) to concat vectors and get vector back?

leonoel09:06:11

(into v1 v2)

joelsanchez09:06:42

more than two: (reduce into [...])

val_waeselynck18:06:22

or: (into [] cat vs)

troglotit09:06:05

many thanks!

troglotit10:06:55

Is it ok to alias namespaces to existing symbol names? E.g. [clojure.string :as str]

joelsanchez10:06:27

yes, in fact it's very common to use that alias (`str`) for clojure.string

jmignault13:06:25

Hey, all: I’m trying to run this example file from Getting Clojure, after having run lein new app blottsbooks: `(ns blottsbooks.core (:gen-class)) (​defn say-welcome [what] (println ​“Welcome to”​ what ​“!”​)) (​defn​ -main [] (say-welcome ​“Blotts Books”​))` and getting this error: copied from CIDER: `1. Caused by java.lang.RuntimeException Unable to resolve symbol: ​defn in this context Util.java: 221 clojure.lang.Util/runtimeException Compiler.java: 7164 clojure.lang.Compiler/resolveIn Compiler.java: 7108 clojure.lang.Compiler/resolve Compiler.java: 7069 clojure.lang.Compiler/analyzeSymbol Compiler.java: 6648 clojure.lang.Compiler/analyze Compiler.java: 6625 clojure.lang.Compiler/analyze Compiler.java: 3766 clojure.lang.Compiler$InvokeExpr/parse Compiler.java: 6870 clojure.lang.Compiler/analyzeSeq Compiler.java: 6669 clojure.lang.Compiler/analyze Compiler.java: 6625 clojure.lang.Compiler/analyze Compiler.java: 6001 clojure.lang.Compiler$BodyExpr$Parser/parse Compiler.java: 5380 clojure.lang.Compiler$FnMethod/parse Compiler.java: 3972 clojure.lang.Compiler$FnExpr/parse Compiler.java: 6866 clojure.lang.Compiler/analyzeSeq Compiler.java: 6669 clojure.lang.Compiler/analyze Compiler.java: 6924 clojure.lang.Compiler/eval Compiler.java: 6890 clojure.lang.Compiler/eval core.clj: 3105 clojure.core/eval core.clj: 3101 clojure.core/eval main.clj: 240 clojure.main/repl/read-eval-print/fn main.clj: 240 clojure.main/repl/read-eval-print main.clj: 258 clojure.main/repl/fn main.clj: 258 clojure.main/repl main.clj: 174 clojure.main/repl RestFn.java: 1523 clojure.lang.RestFn/invoke interruptible_eval.clj: 87 clojure.tools.nrepl.middleware.interruptible-eval/evaluate/fn AFn.java: 152 clojure.lang.AFn/applyToHelper AFn.java: 144 clojure.lang.AFn/applyTo core.clj: 646 clojure.core/apply core.clj: 1881 clojure.core/with-bindings* core.clj: 1881 clojure.core/with-bindings* RestFn.java: 425 clojure.lang.RestFn/invoke interruptible_eval.clj: 85 clojure.tools.nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 55 clojure.tools.nrepl.middleware.interruptible-eval/evaluate interruptible_eval.clj: 222 clojure.tools.nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn interruptible_eval.clj: 190 clojure.tools.nrepl.middleware.interruptible-eval/run-next/fn AFn.java: 22 clojure.lang.AFn/run ThreadPoolExecutor.java: 1142 java.util.concurrent.ThreadPoolExecutor/runWorker ThreadPoolExecutor.java: 617 java.util.concurrent.ThreadPoolExecutor$Worker/run Thread.java: 745 java.lang.Thread/run` I’m using Leiningen 2.8.1. I’ve seen other people have this problem lacking a ns statement at the top of the source file, but this isn’t the case here. Any ideas?

jmignault13:06:29

Sorry for the lack of formatting. I wrote the question in Emacs and pasted it in; I should have done the formatting there. Apologies.

jumar13:06:15

btw. you can edit your message and wrap relevant code with triple backticks ` at the beginning and the end of your code block.

jmignault14:06:55

Thank you! I used single backticks and then wasn’t sure why it didn’t take.

Russ Olsen13:06:55

Possibly the ` right before the ns is the problem?

jmignault13:06:57

Actually, that was my trying to format the text as code. Doesn’t appear in the actual code.

Russ Olsen13:06:37

It looks a lot like clojure itself is not properly loaded.

jmignault13:06:19

What could be causing that? This happens both in CIDER and running lein run from the command line

Russ Olsen13:06:35

So did you copy the code out of ebook? When I just did that I get all sorts of strange control characters in the source.

jmignault13:06:45

That may be it, let me try retyping it

Russ Olsen13:06:02

So for what it's worth I just copied the code out of the pdf, cleaned up all of the nasty characters and ran it with lein run w/o a problem.

jmignault13:06:32

Damn, that was it. Thanks

jmignault13:06:03

I had cleaned some stuff out after pasting, but didn’t realize there were other chars

jmignault13:06:52

Very happy with the book so far, thanks for writing it

jmignault13:06:38

No more copying and pasting for me! 🙂

Russ Olsen14:06:39

You're certainly welcome. Glad the book is working for you. The code is avail online -- The url is there in the book -- but my advice is to type the code in yourself anyway. It really helps with the learning process. And besides 40% of it is just ( and ) anyway 😉

😁 4
jmignault14:06:03

Ha. True enough. Thanks again

ScArcher16:06:51

Ha, I'm also working through Getting Clojure right now. So far so good. Thanks Russ!

Russ Olsen17:06:27

Glad to hear it! If you have questions you know where to find me.

lepistane16:06:39

Hi guys any resources (free) for making your own web app from scratch that is up to date?? I used luminus thus far and have been very pleased but i'd like to get basics and just looking at the code won't cut it i know i'd like to use mount that is only requirement ?

lepistane17:06:09

kinda expensive, should've mentioned free blogs or something

adam19:06:44

I am not getting when to quote and when not to quote lists and vectors. (into () '(1 2 3)) works, (into () (1 2 3)) throws an error. Same for require; sometimes I see it like (require '[ and sometimes (require [ - totally confused.

sundarj19:06:59

the syntax for a function/macro application and the syntax for a literal list are the same, but by default it treats (x y z) as a function/macro. thus if you want a literal list you have to quote it (quoting a collection also quotes everything inside the collection, recursively)

sundarj19:06:14

require is a function, and a function's arguments are evaluated before the function is called. the default evaluation strategy for a symbol like clojure.core is to look for a Java class. that's not what we want with require, we want to pass a literal symbol - thus the quote

sundarj19:06:20

[clojure.core] is a vector containing a symbol, that when evaluated will look for a Java class called clojure.core. (quote [clojure.core]) is a vector containing a literal (quoted) symbol clojure.core, that will be evaluated as itself

sundarj19:06:43

=> clojure.core

       java.lang.ClassNotFoundException: clojure.core
clojure.lang.Compiler$CompilerException: java.lang.ClassNotFoundException: clojure.core, compiling:(null:64:1)
=> (quote clojure.core)
clojure.core
=> (type (quote clojure.core))
clojure.lang.Symbol

sundarj20:06:34

=> [(inc 1)]
[2]
=> (quote [(inc 1)])
[(inc 1)]
=> (type (first [(inc 1)]))
java.lang.Long
=> (type (first (quote [(inc 1)])))
clojure.lang.PersistentList

adam20:06:52

@sundarj thanks a lot for the clarifications.

sundarj20:06:36

i hope that makes sense. basically quoting is a way to leave the code as unevaluated data, which is handy when you want to avoid evaluation

sundarj20:06:52

such as in the case of require

adam20:06:37

If I understand correctly, whenever require is called with a vector, the vector must be quoted all the time. Is that right?

sundarj20:06:36

it's really the symbols inside the vector that have to be quoted, but quoting the vector does that

sundarj20:06:14

=> (require ['clojure.string :as 'string])
nil
=> (string/upper-case "hello")
"HELLO"

sundarj20:06:28

'[a b c] is equivalent to ['a 'b 'c]

sundarj20:06:55

normally you want the default evaluation for symbols (`require` is a symbol that evaluates to the function you want), but in the case of require, you want literal symbols

sundarj20:06:59

so you quote them

adam20:06:46

Got it, thanks

sundarj20:06:20

here's a case where you wouldn't want to quote them:

=> (let [nmsp 'clojure.set, alias 'set] (require [nmsp :as alias]))
nil
=> (set/difference #{1 2 3} #{3})
#{1 2}

pez05:06:24

But there is something special with the ns form, right?

sundarj06:06:10

right. ns is a macro, and macros do not evaluate their arguments, so you don't need to quote anything

❤️ 4
ScArcher19:06:55

I think lists have to have the ' so it knows it isn't a function.

oVerde19:06:38

Many times I see myself doing much data transformations like this (into () (into #{} (remove s/blank? f))) just after doing a (map #(get % 0) (map #(s/split % #“([\/]*[\/].*)“) l))

oVerde19:06:51

Is it a bad practice or is it common?

oVerde19:06:02

The split gives me something like ([“string”] [“string”] [“string”]) then I get 0 to just stay (“string” “string” “string”)

adam19:06:18

@scott.archer I see, what about require '[, it's a bracket

sundarj20:06:30

@scott.archer see my replies to @somedude314's previous message

adam20:06:41

Thanks, I will check it out.

dpsutton19:06:13

You could mapcat them. When you go into a set and then back into a list are you just trying to get distinct values?

oVerde20:06:29

Yes @dpsutton when I got to set and back I’m removing duplicates.

dpsutton20:06:15

There's a function called distinct you could use then.

sundarj20:06:28

could also use the transducer version

dpsutton20:06:00

(->> f (remove s/blank?) distinct)

sundarj20:06:21

=> (into [] (distinct) [1 1 2 3 2 3 3])
[1 2 3]

😀 8
oVerde20:06:46

oh! awesome!

Chester Heng20:06:35

Hi all, anyone know which folders in window store all downloaded clojure packages?

Alex Miller (Clojure team)20:06:08

usually, anything that uses Maven stores them in the .m2/repository directory under your home directory

adam22:06:20

What is the difference between and @? The article here https://8thlight.com/blog/colin-jones/2012/05/22/quoting-without-confusion.html says "The splicing unquote is similar to unquote, except that it allows multiple forms to be inserted in the place of a single unquote-splicing form" but I still don't understand what the author means.

sundarj22:06:46

@somedude314

=> `(str ~[1 2 3])
(clojure.core/str [1 2 3])
=> `(str ~@[1 2 3])
(clojure.core/str 1 2 3)

sundarj22:06:59

also:

=> `(str ~nil)
(clojure.core/str nil)
=> `(str ~@nil)
(clojure.core/str)

adam22:06:08

So it simply expands the list as arguments?

sundarj22:06:11

~x evaluates x and inserts it as-is ~@x evaluates x and splices the elements it contains into the enclosing form

adam22:06:33

Gotcha, thanks.

8