Fork me on GitHub
#beginners
<
2020-03-24
>
hindol.adhya03:03:46

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

glfinn8306:03:07

@hindol.adhya Awesome, thanks!

stebokas12: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?

porkostomus13: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.

aviv13: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

alexmiller13:03:09

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

alexmiller13: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

mitchell_clojure13: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 *

alexmiller13:03:35

well, you can use ' in clojure

alexmiller13:03:22

foo' is a perfectly good symbol name

dpsutton13:03:38

cromulent i'd say

alexmiller13:03:05

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

mitchell_clojure13:03:47

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

alexmiller13:03:45

no fear needed, it's used all over

alexmiller13:03:37

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

mitchell_clojure13:03:29

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

glfinn8313: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"]

hindol.adhya14: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.

hindol.adhya14:03:54

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

glfinn8314: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

glfinn8314:03:53

yup, I'm going to do that

hindol.adhya14: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.

hindol.adhya14: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.

hindol.adhya14:03:46

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

glfinn8313:03:16

Anyone have any idea how I can stop this?

mitchell_clojure13: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

glfinn8313:03:51

Im using vscode

glfinn8313:03:06

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

mitchell_clojure14: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"]]

glfinn8314: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.

glfinn8314: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

glfinn8314:03:50

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

mitchell_clojure14:03:45

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

glfinn8314:03:30

true, but I'm just not that guy 😉

mitchell_clojure14:03:37

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

anikchy.cuet9314: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.

steiner304416:03:34

How about ring?

anikchy.cuet9308:03:39

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

steiner304410: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

chase-lambert17: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

hindol.adhya17:03:53

@ This might be a good use case for trampoline.

chase-lambert17:03:40

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

d.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

chase-lambert17:03:51

I was approaching it like this:

chase-lambert17: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

chase-lambert17: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

chase-lambert17:03:01

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

d.marrs17:03:03

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

hindol.adhya17:03:23

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

d.marrs17:03:36

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

chase-lambert17: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.

d.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

hindol.adhya18:03:02

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

chase-lambert18: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)))))

chase-lambert18: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)

d.marrs18:03:20

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

noisesmith18:03:51

@ the problem is the entire body is inside the println

chase-lambert18: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

hindol.adhya18:03:06

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

noisesmith18:03:26

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

d.marrs18:03:08

@hindol.adhya thanks, that's interesting.

chase-lambert18:03:27

Ok, definitely re-fixed that println problem.

chase-lambert18: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->

chase-lambert18: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

chase-lambert18: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

chase-lambert18: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

chase-lambert18:03:04

awesome. thanks for all your help!

chase-lambert18: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)

chase-lambert18: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

chase-lambert18:03:01

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

chase-lambert18:03:52

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

hindol.adhya18: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. ")"

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)")
[email protected] ~ %

noisesmith18:03:03

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

hindol.adhya18: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
       [email protected]
       (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)

ddouglass18:03:25

something like should work:

user> (defmacro with-browser [[symbol obj] & body]
  `(let [browser# ~obj
         ~symbol browser#]
     (try
       [email protected]
       (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> 

sova18:03:58

Hello everyone. Any interesting questions?

sova18:03:58

Hello everyone. Any interesting questions?

hindol.adhya18:03:30

Well, someone's bored.

sova18: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

hindol.adhya18: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

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

hindol.adhya18: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

chaow19:03:30

thank you so much!

fabrao19:03:31

@ddouglass Thank you

neo255119: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!)

neo255120:03:31

Thanks a lot!!

travis4221: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)

noisesmith21:03:32

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

neo255121:03:11

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

travis4221:03:52

aha! I'll give this a try, thanks

travis4221: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

travis4221:03:05

Sorry man. 🙂 Thanks for the details.

brandon14923:03:35

Does anybody have experience using amazonica?

brandon14923: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

brandon14923: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)

brandon14923:03:42

It reads

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

brandon14923:03:50

I am wagering that is related to my auth config

noisesmith23:03:58

what is the full printout of *e

brandon14923: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

brandon14923:03:33

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

brandon14923: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