Fork me on GitHub
#beginners
<
2019-09-12
>
pferron00:09:24

Thanks 🙂 there's no conclusive argument in that discussion as to why this shouldn't work, so I'll open a JIRA ticket

seancorfield00:09:18

@pferron I cannot imagine it will be changed. You'd have to make a good case that changing the behavior would solve a real world problem that people actually need solving.

pferron00:09:47

Does this mean that warts are generally left unfixed? ->> doesn't behave according to its docstring, but of course it's possible to work around it so I don't think anyone is blocked by that

andy.fingerhut00:09:17

I don't think it would be accurate to say "warts are generally left unfixed". I think it is taken on a case by case basis. Given the desire to maintain backwards compatibility, changes with perceived low benefit are correspondingly low in priority to change.

andy.fingerhut00:09:42

In this case, do you actually have a use case where it would be important for your example expression to work?

seancorfield00:09:02

You may find that consistency between these two forms is considered more important (and perhaps the ->> docstring should just be updated to be more specific):

user=> (-> (constantly 1) ())
Syntax error (IllegalArgumentException) compiling at (REPL:1:1).
Can't call nil, form: (nil (constantly 1))
user=> (->> (constantly 1) ())
Syntax error (IllegalArgumentException) compiling at (REPL:1:1).
Can't call nil, form: (nil (constantly 1))
user=>  

pferron00:09:14

I had a ->> pipeline where at some point the value was a function. The next step in the pipeline was to call this function, and then continue the pipeline with the return value.

seancorfield00:09:03

By all means, go ahead and create an issue in JIRA -- we're just setting your expectations that changes to core are generally few and far between.

pferron00:09:36

Yeah, and I do appreciate the focus on stability and backward comp 🙂

seancorfield00:09:04

That's why I've been comfortable running prerelease builds of Clojure in production for eight years 🙂

pferron00:09:40

Actually I also wish that apply would accept taking only a function as arg so I could just write apply instead of ()

pferron00:09:05

So many core changes that won't happen 😄

seancorfield00:09:24

Why would you want to write (apply f) when you can just call (f) directly in that case? In general, you're going to have (apply f coll) and coll may be empty...

seancorfield00:09:31

(genuinely curious)

pferron01:09:03

I meant in the previous example that currently doesn't work:

(->> (constantly 1)
     ())

pferron01:09:21

My next attempt was:

(->> (constantly 1)
     apply)

pferron01:09:38

But that doesn't work either because apply wants a coll of args

pferron01:09:56

The cool thing with the second version is that it would work both with -> and ->> and is arguably more readable as well

andy.fingerhut01:09:49

Are you familiar with as-> ? It can be useful to change the position at which each expression appears in the following expression, rather consistently the first position like ->, or consistently the last like ->>

pferron02:09:00

I had forgotten about as->. It works fine for calling a function in the middle of the pipeline:

(as-> (constantly 1) x
      (x))
so it's a nice alternative even though it's not quite as concise. Thanks for bringing it up 🙂

seancorfield02:09:50

(-> (constantly 1)
    (as-> f (f)))
It's designed to be used inside -> hence the "weird" order of arguments (expression, symbol, body)

seancorfield02:09:48

I'd probably define a helper function for use in situations like this

(defn call [f] (f))

(-> (constantly 1)
    (call))

pferron03:09:44

Oh, thanks for the tip about using as-> inside the ->, the arg order definitely makes more sense this way!

alfredx06:09:14

Can do

(-> (constantly 1)
    (apply []))
as well

Michael10:09:18

hello, i have this line that gives me reflection warning on lein check: (if (empty? (.getAllFrames desktop))) i can solve this with type hinting: (.getAllFrames ^javax.swing.JDesktopPane desktop) my question is basically how to pack this properly into a macro that takes as arguments the method & object and returns the above line..i'm slightly(understatement) confused with quotes backquotes etc..

yuhan10:09:16

(set! *warn-on-reflection* true)

(defmacro string-method
  [obj method-name & args]
  `(. ~(vary-meta obj assoc :tag `String) ~method-name ~@args))

;; warnings
(fn [s] (. s endsWith "a"))

;; no warnings
(fn [s] (string-method s endsWith "a"))

Michael10:09:25

thanks, i hadn't seen this so question no

Michael10:09:16

thanks for the macro too, looks like what i was aiming at wasn't so simple 🙂

borkdude10:09:30

@sb Ctrl-shift close paren

👍 4
sb10:09:09

@borkdude Yes, I see. at this moment I deleted. thanks 🙂

Sreeram11:09:57

Hi, How to convert the below java code to clojure: private final static Logger LOGGER = Logger.getLogger(<class_anme>.class.getName());

borkdude14:09:20

@sreerama.naga probably something like this (Logger/getLogger (.getName TheClazz))

Sreeram14:09:18

In this TheClazz give compilation error.

borkdude15:09:43

replace TheClazz with the class which you wanted to use in your specific case

Sreeram14:09:50

thank you for your response. can we add an alias to it similar to LOGGER in the java code? if yes how can we do it.

borkdude15:09:37

@sreerama.naga yes: (ns your.namespace (:import [java.blablabla.bla Logger]))

Sreeram15:09:21

thank you for response. Here I mean like an Object in java private final static Logger LOGGER = Logger.getLogger(<class_anme>.class.getName()); here we create LOGGER reference and we use where ever we refer methods/functions in it. similarly can we have a reference created similar lines in clojure. Below solution compiles fine. but I do not have any reference to use methods of the created java object. (Logger/getLogger (.getName Class))

borkdude15:09:25

if you give me the exact java code, I can translate it to Clojure for you

JoshLemer15:09:28

I think that perhaps @sreerama.naga is simply looking for how to define a var called LOGGER with that value. In that case it would be:

(def LOGGER (Logger/getLogger (.getName TheClazz))

lread20:09:07

or maybe @sreerama.naga is coming form Java and is trying to translate his knowledge to Clojure code? If this is the case, have a peek at https://github.com/clojure/tools.logging

Sreeram04:09:59

@UEH6VEQQJ thank you. I was looking this way.. But however I realized it wont be sufficient for me to proceed further.

Sreeram04:09:48

Sorry if my post is not clear to you. @UE21H2HHD Infact currently we use tools.logging, but the concern for me here is, it chooses either slf4j or log4j or util logging based on the logging available for it at runtime. As our large project the underlying runtime may vary due to different 3rd party libraries we use. 1) So here we want to restrict it to use only java util logging always. 2) Plus we also want to enable package level logging at runtime. So to achive above 2 points, I was trying to do like the way we do in Java as below. Class A{ //In Java the below like creates logger for this class and hence we can the logger at runtime and can change the log levels. java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(A.class.getName()); //where ever I want to log I use http://LOGGER.info("Failed to trigger notification"); //info logging LOGGER.fine("Failed to trigger notification"); //fine logging } any thoughts and solution to achieve this beahaviour in clojure?

lread12:09:18

I don’t know if I am helping here but: I think clojure.tools.logging allows you to force which logging implementation is chosen? Also by default, clojure.tools.logging uses the namespace as the logger.

Sreeram13:09:22

tools.logging chooses the logging whether slf4j, log4j or util logging based on the runtime which was available in the path. I am not aware how we can force it to use only util logging.. any suggestion or guidance to overcome the above said 2 points will greatly help me.

Sreeram13:09:11

In our case we have slf4j, log4j and other logging as it comes from other different libraries which we can not delete or avoid.

lread14:09:30

Are you using logback to unify the various logging technolgies in use?

lread14:09:48

Some people also find unilog helpful https://github.com/pyr/unilog

Sreeram14:09:13

It is a large project and mix of all the loggins but at the end all of them will write to the util logging way.. So for clojure it is not happening to consider only util logging in this case.

lread14:09:46

ah ok, so for legacy reasons you can’t introduce tools like logback which help to amalgamate various logging tech?

Sreeram14:09:36

yah.. we can not introduce new logging now as every other logging was already tried and we encounter different issues, as we use 3rd party components and which depend on different log dependencies. Importantly the package level logging is most required feature we should always keep as it is. So keeping all these in the back of mind we want to go/force util logging which is more effective and proven for our case.

Sreeram15:09:24

Thank you for the link. let me try..

Sreeram17:09:19

@Iread this does not work as it is. I am using tools.logging-0.2.6.jar, and some of the packages as shown in the above link does not exists as it is in 0.2.6 version. But I tried different options it doesn't work. May be I am missing something.

lread17:09:24

oh my goodness tools.logging-0.2.6 was released in feb 2013. Are you unable to upgrade any libs?

Sreeram17:09:09

At this moment I do no think so. I need to make this work with this as it is until I have very strong reason to change to new library..

lread17:09:46

I expect you noticed I linked to the stackoverflow question rather than the answer? https://stackoverflow.com/a/16153713

Sreeram17:09:48

any thoughts to make this to work with current libraries ?

lread17:09:37

I’m sure you have good reasons to not upgrade libraries but I am sure it gives you many headaches?

Sreeram18:09:52

@Iread, it is like, make it work as it is instead of inviting new issues by upgrading based on the current criticality.

lread21:09:06

I’m sorry, I can’t help you here. It does not mean that you cannot do what you want to do, it just means I’m not the guy to help you figure out how to work with old libraries. I sincerely wish you the best of luck.

Sreeram14:09:02

@Iread solution in above url works perfectly. 👍 thank you all for your help.

lread15:09:21

That’s great @sreerama.naga, thanks for letting me know of your success!

tjb16:09:51

another small victory: i was able to separate everything out in my little project. prior i had everying in core.clj to POC things and was able to get everything running with proper namespaces. i have been using metabase as a guide on project structure so if anyone has an additional examples i would really appreciate it! curious what is the “correct” directory structure for clojure projects. coming from a spring background things have sub directions like controllers, domain, service etc

flefik18:09:11

If I understand it correctly you’re generally discouraged to organise by type and instead organise them by domain. So instead of: x.controllers.login and x.service.login, you would have x.login.controller and x.login.service.

Steve Hobbs16:09:59

Hi all, as a beginner to Clojure I would be interested to see if there’s a more idiomatic way to write this code. It basically takes a Json Web Token and converts the claims within to a Clojure map. It uses the Auth0 java-jwt library to parse the token. .getClaims returns a Map<String, Claim>. to-map attends to take this Java map and return a Clojure map, converting the k to a keyword and retrieving the value of the claim. map-claim attempts to retrieve the value of the claim. asString is called, which returns nil if the claim value is not a string. This naive implementation then tries to call asInt in that case. Would love to see how this could be improved. I’m sure into could be used but I can’t quite get the syntax.

schmee16:09:57

looks good to me! :thumbsup: into doesn’t really help here unfortunately since you want to convert stuff before creating the map anyway (otherwise you could do (into {} your-java-map). to-map as you have written in will return a list of maps, with one token per map. if you intended to get a single map with all the tokens, then

(into {} (for [[k v] hash] [(keyword k) (map-claim v)]))
will get you there!

schmee16:09:19

map-claim can be written more concisely like this:

(defn- map-claim [claim]
  (or (.asString claim) (.asInt claim)))

Steve Hobbs17:09:25

Thanks @schmee ! Just the kind of feedback I was looking for 🙂

Chase19:09:00

Could someone be kind enough to help me eliminate the loop/recur in this function? I've been trying unsuccessfully for awhile and expect it's probably simpler than I'm making it.

(defn author-by-name [name authors]                                          
  "given a string and set of author maps returns map if author is present"          
  (loop [name name                                                           
         authors authors]                                                    
    (if (= name (:name (first authors)))                                     
      (first authors)                                                        
      (recur name (rest authors)))))

Chase19:09:36

I feel like I've grown paranoid of using loop/recur after some advice I've gotten here saying you can almost always use some built in core sequence functions instead.

Chase19:09:24

I've also read the same in some Clojure books (I think maybe Programming Clojure) but I'll be honest I find loop/recur much easier. But I want simple instead right?!

Chase19:09:42

So the name parameter is a string of an author and authors is a set with different author maps that have a :name "name" key value pair inside. I need to match the input name with the value "name" and then return the whole author map.

Chase19:09:10

and in general, what would you put in the doc string to explain that expected input and output?

Chase20:09:03

Plus is it a "code smell" to be using first and rest when looping or iterating through a set? It feels a little wrong because they are unordered for some reason.

andy.fingerhut20:09:27

Doesn't that code have an infinite loop if there are no matching entries for the name?

andy.fingerhut20:09:39

As far as a non-loop way, filter could be used to return a sequence of only those elements of authors that return true for a function you pass to filter. That could return a sequence of all matching elements of authors.

andy.fingerhut20:09:31

filter is lazy, so if you want to guarantee only one return value, call first on the result. The filter function will stop doing work as soon as the first match is found, if any.

andy.fingerhut20:09:23

e.g. (filter #(= name (:name %)) authors)

andy.fingerhut20:09:56

Or if you prefer a slightly longer form:

andy.fingerhut20:09:12

(filter (fn [author] (= name (:name author))) authors)

Chase20:09:46

Awesome! Thank you! Yeah I've definitely been trying with filter but just wasn't quite getting the full author map out of the set.

Chase20:09:01

You know it's funny the exercise gave a hint of "remember first" and that's what got me on that weird loop/recur with calling first on a set and such. But your sequence output is why!

Chase20:09:34

And not realizing the infinite loop was embarrassing but I just today started playing around with a new editer/repl combo and was thinking I just didn't know where to see my nil output. Of course it was user (me!) error as it was spinning it's wheels in the background in an infinite loop

andy.fingerhut20:09:44

filter always returns full 'elements' out of its input sequence, so I don't see how you could try to use it and not get full author maps out.

andy.fingerhut20:09:00

keep is different than filter in this regard.

andy.fingerhut20:09:29

I guess I should clarify: If you get anything out of filter, then it should always be full elements from the input sequence. It is quite easy to call filter with a slightly wrong predicate function and get the empty sequence back.

Chase20:09:21

ahhh, this is all good stuff. The real answer is I just suck at this still (but love it!) and just flounder away at the repl hoping to stumble onto the right solution. I think at one point I had done `(map :name authors) so had a sequence of all the name strings and was comparing but then was like, but wait, how do I get the map now? So yeah, I suck. hahaa

andy.fingerhut20:09:07

No worries. A new style of programming can take a lot of getting used to, and practice.

Chase20:09:28

Definitely, just programming in general. I technically know more clojure and can do far more with it than any other programming languages.

andy.fingerhut20:09:46

You now have that bit of experience that should remind you in a future similar problem: "If I want the original full elements of some collection as part of my output, map won't do it, because it calculates new replacement things and returns those"

Chase20:09:56

very true. I've learned so much more in the last few weeks than I have in the last year I think just flitting around. I spent a lot of time trying beginner tutorials in various languages and projects before finally settling down and consistently trying to solve problems every day. I've always been drawn to Clojure despite my struggles so decided to just stick with it until it clicks.

JoshLemer20:09:56

What is the generally idiomatic way in clojure to achieve what people use so-called “algebraic data types” for in static-functional langs like Scala/Rust/Haskell, where you want to model data as being one of multiple cases? For example like (pseudocode):

enum Auth {
  UsernamePassword(username: String, password: String)
  Token(token: String)
}
Would you typically use a map with a :tag keyword like
{:tag ::username-password ::username "bob" ::password "pw" }
{:tag ::token ::token "123" }
Or just never mind with the tag at all, and just pass in the fields?

seancorfield20:09:00

If the set of fields is non-overlapping, I probably wouldn't bother with a tag and would just use a plain hash map.

👍 4
seancorfield20:09:45

(if-let [token (::token auth)]
  (,,, deal with token-based auth ,,,)
  (,,, deal with credentials-based auth ,,,))