Fork me on GitHub
#beginners
<
2020-03-24
>
hindol03:03:46

@glfinn83 If you are going with defprotocol, check out shrubbery. https://github.com/bguthrie/shrubbery

Old account12:03:58

Hello, did anyone read https://luminusweb.com/docs/guestbook documentation and book asociated with it? Is book worth buying? is it a lot different than documentation?

Bobbi Towers13:03:54

Yes it's a very good book, the most comprehensive one on Clojure web development I know of. And I'd say more generally applicable than the Luminus docs because people tend to pick and choose libraries in practice.

Aviv Kotek13:03:48

hi, I see some code in libraries with the notation of (defn foo* [x] (..)) (defn foo [x] (foo* x)) Is there any convention with * ? couldn't find anything on the clojure-style-guide

Alex Miller (Clojure team)13:03:09

nothing strong, sometimes a pattern used to separate a public fn and a helper impl fn

👍 4
Alex Miller (Clojure team)13:03:10

inside clojure core, trailing * often actually marks a special form implementation that is implemente by the compiler, but that's a special case

eval-on-point13:03:35

I think of it as similar to foo'(x) in the math tradition, but we can't use the ' character in clojure, so we use *

🙂 4
Alex Miller (Clojure team)13:03:35

well, you can use ' in clojure

Alex Miller (Clojure team)13:03:22

foo' is a perfectly good symbol name

dpsutton13:03:38

cromulent i'd say

😁 4
Alex Miller (Clojure team)13:03:05

you can't put it in the front of course :)

eval-on-point13:03:47

I revise my statement to "I'm scared to"

Alex Miller (Clojure team)13:03:45

no fear needed, it's used all over

Alex Miller (Clojure team)13:03:37

and officially in the docs as a valid constituent character https://clojure.org/reference/reader#_symbols

eval-on-point13:03:29

yes, sorry for the misinfo. Will be more careful with my words moving forward 🙂

Gulli13:03:57

Anyone here use com.novemberain/langohr for RabbitMQ? After adding it to my project I get these annoying messages (it can't find SL4J conf in classpath):

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See  for further details.

jsyrjala13:03:10

That is using http://slf4j.org logging facade and it is complaining about missing logging implementation. To fix this you’ll need to include depency that includes the dependency

jsyrjala13:03:27

logback is one such implementation [ch.qos.logback/logback-classic "1.2.3"]

hindol14:03:38

The linked page has instructions,

If you are responsible for packaging an application and do not care about logging, then placing slf4j-nop.jar on the class path of your application will get rid of this warning message.

hindol14:03:54

But if you care about logs at all, better configure the logger properly.

Gulli14:03:28

I actually was oblivious to what SLF4J was, I thought this was a logging library, as this is an abstraction layer I'm more comfortable with this

Gulli14:03:53

yup, I'm going to do that

hindol14:03:04

The purpose of SLF4J is to give you the power to choose the right implementation for your app yet capture the logs from the library you pulled.

hindol14:03:05

Without an implementation in path, it cannot log anything. But for any app in production, you will want to at least capture WARN and ERROR from every library you pull.

hindol14:03:46

So, I don't really recommend slf4j-nop.jar.

Gulli13:03:16

Anyone have any idea how I can stop this?

eval-on-point13:03:52

Do you know if it is just a Cider issue or not? I've had the same error and just added the slf4j library to my lein config

Gulli13:03:51

Im using vscode

Gulli13:03:06

wondering, what dependencies do you have in your project? There might be a conflict here maybe

eval-on-point14:03:29

I actually got the error while playing around with clj-kondo yesterday, but I think that there is maybe something up with my personal config, which relies on

:dependencies [[alembic "0.3.2"]
               [com.gfredericks/dot-slash-2 "0.1.5"]]
:plugins [[lein-ancient "0.6.15"]
          [lein-ubersource "0.1.1"]
          [com.billpiel/sayid "0.0.18"]]

Gulli14:03:44

hmmm, nothing like mine

mavbozo14:03:24

@mitchell_clojure @glfinn83 althought it is annoying, it's the default message. many libraries uses SLF4J abstraction to log messages. It's up to the users of those particular libraries to choose one of many SLF4J logger implementation. It's a great feature because most of the time, we already have a logger implementation in our running app and we don't have to worry about conflict with another libraries that brings another logger implementation.

partywombat 4
Gulli14:03:50

I'm using Timbre, which is not going to help me here. I'm also to stubborn to add a dependency just for this lol

jsyrjala14:03:24

I’d guess that there are slf4j -> timbre converters

Gulli14:03:50

Yet another dependency I don't want though. Going to see if I can find something instead of Langohr

eval-on-point14:03:45

I don't think you need to be using it, you can just ignore the message, right?

Gulli14:03:30

true, but I'm just not that guy 😉

eval-on-point14:03:37

lol, well I think there are ways to suppress the warning with your user profile

Anik Chowhdury14:03:19

how to download username, password and anti forgery token based website using clojure? i have used clj-http. Can anyone show me a way to do this? i am kind of noob in this.

Steiner16:03:34

How about ring?

Anik Chowhdury08:03:39

@U010FU3Q9N0 don't know how to do that. Can you please elaborate or enlighten me about this?

Steiner10:03:48

I can just tell you that I am just a noob in web development,and I found ring-clojure is a simple,good framework.Yo You can search it on github

👍 4
Chase17:03:38

So I am prompting a user (at the command-line) to input a number. If a number is not input I just want to "loop" back around and prompt again until a number is entered. Is this what I would use try/catch for? I basically read in the input as a string using (read-line) right. Then do something like (try (Long/parseLong n) (catch Exception e)... or what? and then how do I circle back to the prompt?

noisesmith17:03:44

read-line and the prompt share the same input, it's automatic

noisesmith17:03:06

if you don't readline in another thread, it won't interfer

hindol17:03:53

@U9J50BY4C This might be a good use case for trampoline.

Chase17:03:40

Hmmm, ok. I'm confused on the syntax and all that involved too though.

marrs17:03:43

I doubt the jvm will run out of stack frames before the user runs out of patience 😛

noisesmith17:03:47

I don't see how trampoline would help

Chase17:03:51

I was approaching it like this:

Chase17:03:58

(defn get-num []
  (println "Number: "
    (let [num (read-line)]
      (try 
        (Long/parseLong num)
        (catch Exception e)))))

hiredman17:03:26

Trampoline is a solution to a problem several steps ahead

Chase17:03:28

But I don't actually know how to do this. I'm reading clojuredocs but could use a more extensive resource on try/catch if you folks have it.

hiredman17:03:00

E.g. you can't recur from the catch

Chase17:03:01

Yeah, once the user inputs a valid number the program runs some calculations and that's that. Nothing crazy going on here.

marrs17:03:03

You don't need to use try/catch. A simple predicate would do

hindol17:03:23

@U051SS2EU To repeatedly execute read till a number is found. EDIT: But recur can do that as well.

marrs17:03:36

(if-not (is-number? x) (get-num))

Chase17:03:19

Yup. Ok. Seems obvious now that I think of it. Lol. But I would still like to learn more about exceptions and error handling all that jazz if anyone has Clojure resources on that.

marrs17:03:35

I'm not experienced enough with Clojure to say much about exceptions, but I would search for articles on idiomatic error handling. My guess is that their primary purpose is for integration with Java/JS code and not to be used more generally

hindol18:03:02

@UVDD67FFX In the JVM world, exceptions are the canonical way to handle exceptional cases. try/catch is not unidiomatic.

Chase18:03:02

so now when I use this:

(defn get-num []
  (println "Number: "
    (let [num (read-line)]
      (if (number? (Long/parseLong num))
        (valid-cc? num)
        (get-num)))))

Chase18:03:44

it doesn't print out "Number: " first. It just sits there waiting for the input with no prompt. and then afterwards does "Number: nil" after my other calculations do there thing.

noisesmith18:03:18

you might need to add a call to (flush)

marrs18:03:20

@hindol.adhya In Java world, definitely. You wouldn't typically use exceptions in Scala

noisesmith18:03:51

@U9J50BY4C the problem is the entire body is inside the println

Chase18:03:57

ok. Also if it isn't a number it gives me the NumberFormatException and terminates instead of calling the function again

noisesmith18:03:59

you need to take the let out of the println

hindol18:03:06

@UVDD67FFX In Clojure though, it is not frowned upon or anything.

noisesmith18:03:26

@U9J50BY4C right, that's where you do the try/catch and return nil

marrs18:03:08

@hindol.adhya thanks, that's interesting.

Chase18:03:27

Ok, definitely re-fixed that println problem.

Chase18:03:49

So then I can't just use the predicate? Which takes me back to the syntax of using the try/catch.

noisesmith18:03:05

like this

(ins)user=> (try (Long/parseLong nil) (catch Exception _))
nil
(ins)user=> (try (Long/parseLong "1") (catch Exception _))
1

noisesmith18:03:20

wrap the smallest possible block in the try

noisesmith18:03:58

then have a conditional on whether the return was nil

noisesmith18:03:07

or use some->

Chase18:03:18

hmmm. Ok. Seems to be getting a bit difficult to just see if the input is a number or not and loop back around.

noisesmith18:03:33

one moment I'll have an example

Chase18:03:35

Maybe I'm not even understanding what exactly try/catch even does or means

noisesmith18:03:54

(cmd)user=> (load-file "/tmp/get-num.clj")
#'user/get-num
(cmd)user=> (get-num)
(cmd)Number: alsdjf
(ins)Number: ajdsfk
(ins)Number: lllll
(ins)Number: 3
3

noisesmith18:03:09

(defn get-num []
  (print "Number: ")
  (flush)
  (let [input (read-line)
        num (try 
             (Long/parseLong input)
             (catch Exception _))]
  (if num
    num
    (recur))))

noisesmith18:03:43

that last line should probably be (or num (recur)) but that expands to the same thing anyway

Chase18:03:12

Ahhh! Very informative. So you are saying the try will return nil if it is not a number which evaluates as false in the if statement?

noisesmith18:03:49

try returns the body of the catch if it is invoked, here the catch implicitly returns nil as there's no body

noisesmith18:03:03

you could make it explicit by adding false or nil to the catch

noisesmith18:03:23

and yes, clojure treats nil as false in conditionals

Chase18:03:04

awesome. thanks for all your help!

Chase18:03:17

What exactly does flush do here?

noisesmith18:03:36

it means you can get input on the same line as the prompt

noisesmith18:03:48

it flushes partial output (newline automatically gets flushed)

Chase18:03:12

perfect. That fits the problem description exactly as well. Thank you!

noisesmith18:03:16

here's an alternate version (does the same thing, may or may not be clearer)

(defn get-num []
  (print "Number: ")
  (flush)
  (or (try (Long/parseLong (read-line))
           (catch Exception _
             false))
      (recur)))

noisesmith18:03:59

also, I've seen (defn safe-long [s] (try (Long/parseLong s) (catch Exception _))) in multiple code bases, sometimes I write it, sometimes someone else does

Chase18:03:01

I prefer the first one but both really clear things up for me

Chase18:03:52

Awesome. Maybe I should start my own little util file and keep such things in it.

hindol18:03:15

It is also possible to use clojure.edn/read-string and check if the resulting thing is a number?, int? or integer? depending on what you want. That avoids the try/catch as well.

noisesmith18:03:59

but it will still throw on eg. ")"

hindol18:03:25

Good point.

noisesmith18:03:45

I don't like using read-string for user input

lojure 1.10.1
(cmd)user=> (read-string ")")
Execution error at user/eval1 (REPL:1).
Unmatched delimiter: )
(ins)user=> (read-string "#=(java.lang.System/exit 1)")
justin.smith@C02ZW014LVDN ~ %

noisesmith18:03:03

that is, #= is read-eval, and it does things you probably don't want

hindol18:03:50

I thought edn/read-string did not have the unsafe bits. I may be wrong.

noisesmith18:03:03

oh, I missed that you specified clojure.edn

fabrao18:03:55

hello all. I´m writing a macro like this

(defmacro with-browser
  [browser & body]
  `(let [browser# ~browser]
     (try
       ~@body
       (finally
         (quit browser#)))))

fabrao18:03:52

how do I change this macro to use like this (with-browser [driver (create-driver)]) ?

fabrao18:03:42

I just can user it like (with-browser driver)

Darin Douglass18:03:25

something like should work:

user> (defmacro with-browser [[symbol obj] & body]
  `(let [browser# ~obj
         ~symbol browser#]
     (try
       ~@body
       (finally
         (quit browser#)))))
also macroexpand is your friend here:
user> (macroexpand '(with-browser [browser (create-driver)]
                      (println browser)))
(let*
 [browser__5732__auto__
  (create-driver)
  browser
  browser__5732__auto__]
 (try (println browser) (finally (user/quit browser__5732__auto__))))
user> 

sova-soars-the-sora18:03:58

Hello everyone. Any interesting questions?

😀 4
hindol18:03:30

Well, someone's bored.

sova-soars-the-sora18:03:00

It's an open invitation to ask a question if you have one =)

chaow18:03:37

if you got a bit of time on your hand, i am wondering how to get sums of repeating keywords in a nested map. For instance if i have a map: {:a 1 :c { :a 2}} how do write a function (f map :a) -> 3

chaow18:03:37

imagine the nested map has multiple levels and i am only interested in summing the values of a certain keyword that might be repeating at different nested level of the map, could also be in a vector

chaow18:03:55

i tried postwalk but it doesnt seem to be fit for the task

hindol18:03:28

I take back my statement. Typed without thinking.

noisesmith18:03:51

user=> (->> (tree-seq coll? seq {:a 1 :c { :a 2}}) (filter map-entry?) (filter (comp #{:a} key)) (map val) (apply +))
3

🎉 4
noisesmith18:03:22

I think this one is better, does the same thing

user=> (->> (tree-seq coll? seq {:a 1 :c { :a 2}}) (filter map?) (keep :a) (apply +))
3

hindol18:03:54

Thanks, I was actually thinking about tree-seq. Forgot the name.

chaow18:03:24

wow thanks, that works great! my postwalk involved atoms to track a counter but now i see that theres a much simpler way

noisesmith18:03:25

tree-seq is great for when I want to process my tree as if it were a graph :D

noisesmith18:03:21

that is, if only data types / keys are significant, and not the relative layer in the nest

💯 4
chaow19:03:30

thank you so much!

sova-soars-the-sora18:03:00

It's an open invitation to ask a question if you have one =)

fabrao19:03:31

@ddouglass Thank you

👍 4
David Pham19:03:20

In Clojure.spec, does gen/fmap and gen/let achieve the same functionality? What would be the difference?

Lennart Buit19:03:09

I am not completely sure how it applies, but in general in functional programming, fmap indicates an operation that applies a pure function to a value in a context. So fmap allows you to apply a function that evaluates a value coming from a generator to create a new value. From what I can see, gen/let allows you to evaluate a value coming from a generator to create a new generator.

Lennart Buit19:03:47

In other languages, this would make let strictly more expressive.

Lennart Buit19:03:10

But this is my ‘general’ functional programming experience speaking

Lennart Buit19:03:55

Take a look at gen/bind, to which gen/let expands

Lennart Buit19:03:41

(Hope this helps!)

David Pham20:03:31

Thanks a lot!!

Travis Smith21:03:49

Hi! I've got one for you: I have a map with keys/vals like this: {id int title string done bool template string} I have a string for a given template (ex. "Bob", and a list that contains strings (ex. (list "Thing1" "Thing2" "Thing3"). I want to autoincrement the id and add entries to the map iteratively until the list of strings runs out, such that I have something that looks like this: { {:id 1 :title "Thing1" :done false :template "Bob"} {:id 2 :title "Thing2" :done false :template "Bob"} {:id 3 :title "Thing3" :done false :template "Bob"} } I've been having a heck of a time figuring out this seemingly simple task in Clojure. Please help!

noisesmith21:03:42

(map-indexed (fn [i title] {:id i :title title ...}) things)

👍 4
noisesmith21:03:32

where ... is your boilerplate keys and vals of course

David Pham21:03:11

Or (map #(assoc {...} :id %1 :title %2) (range) things)

Travis Smith21:03:52

aha! I'll give this a try, thanks

Travis Smith21:03:39

I need it spelled out, if you don't mind. What is ... referring to? The existing map name? Or is {...} some symbol I'm not familiar with yet?

noisesmith21:03:39

... is the keys and vals that don't change

noisesmith21:03:14

(map-indexed (fn [i title] {:id i :title title :done false :template "BoB"})
             things)

noisesmith21:03:44

it was just a shorthand so I wouldn't have to do as much typing

Travis Smith21:03:05

Sorry man. 🙂 Thanks for the details.

Brandon Olivier23:03:35

Does anybody have experience using amazonica?

Brandon Olivier23:03:27

At this point I can't even load it into the repl because somehow the amazonica.core ns isn't available

noisesmith23:03:28

you can just directly ask questions, but yes

Brandon Olivier23:03:11

Whenever I switch to a ns that requires amazonica.aws.s3 I cannot eval the ns definition because core isn't available

noisesmith23:03:24

what happens if you try (require 'amazonica.core :reload) (fixed typo)

noisesmith23:03:42

that should show you whatever error is preventing it from being available

noisesmith23:03:53

(or fix the issue, slim chance)

Brandon Olivier23:03:42

It reads

Syntax error (NoClassDefFoundError) compiling def at (core.clj:221:1).
Could not initialize class com.amazonaws.ClientConfiguration

Brandon Olivier23:03:50

I am wagering that is related to my auth config

noisesmith23:03:58

what is the full printout of *e

Brandon Olivier23:03:19

oh wow, I didn't know about that

noisesmith23:03:22

it seems strange that it would initialize auth while loading an ns

Brandon Olivier23:03:33

#error {
 :cause "Could not initialize class com.amazonaws.ClientConfiguration"

Brandon Olivier23:03:41

I guess it's a weird library

noisesmith23:03:03

@brandon149 most people just skim it as if it was spam, but the startup message of most repls do mention *e

noisesmith23:03:29

@brandon149 I really did mean the full content, not just the message

noisesmith23:03:40

(you can put it as an attachment so it's less spammy)

noisesmith23:03:01

or a version that doesn't play nicely

noisesmith23:03:44

maybe check lein deps :tree for jackson verison conflicts, jackson very frequently makes things break