Fork me on GitHub
#beginners
<
2022-11-07
>
James Pratt11:11:11

If I want to create a Java date object from an integer to convert between UNIX time and a proper date then I would have thought that I would do that with: (.setTime (java.util.Date.) 0) for example. But this returns nil. How do I capture the value of the date object after the method call?? Is there a better way to convert between a time stamp and a Unix time and back again? Actually what I am trying to do is to iterate through calendar days of the year and I figure the easiest way of doing this would be to add 24 hours of seconds to a Java date object each iteration.

delaguardo11:11:44

(doto (java.util.Date.)
  (.setTime 0))
.setTime is mutable operation that indeed returns nil. So you should use doto to chain mutable operations for some object

delaguardo11:11:41

also better way is to use newest java.time api. There is Instant/ofEpochMilli to create Temporal from integer https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html#ofEpochMilli-long-

1
delaguardo11:11:13

pre-requisitions though is to use at least java 8

James Pratt12:11:23

thanks @U04V4KLKC! hadn't come across doto before!

stantheman14:11:06

Don't know your use case exactly, but would the clojure.instant lib help? https://clojuredocs.org/clojure.instant/read-instant-date

delaguardo14:11:42

it can’t take an integer as an input

jumar14:11:44

java.util.Date has a constructor that accepts the time directly:

(java.util.Date. 123456789)
#inst "1970-01-02T10:17:36.789-00:00"

👍 1
Benjamin13:11:05

what is the closes to call/cc or goto in clojure?

ghadi13:11:36

there is nothing like that. what are you trying to do?

Benjamin13:11:14

(let [data (resolve-data ...)]
  ...)
resolve-data has different user error conditions I want to say "the x you provided is not in the database" 1 idea I have is to make resolve-data return a map of {:data , :error, }. With call/cc I could make resolve-data immediatly return a user error to the outside

andy.fingerhut14:11:11

There are Java exceptions in Clojure/JVM, but some people prefer returning error status values for things you would expect to be normal, which looking for but not finding an entry in a database sounds like it might be

phronmophobic18:11:35

for error handling, you can try https://github.com/IGJoshua/farolero, which implements conditions and restarts

skylize14:11:09

How do I alias a function, including its docstring and preferably also any other metadata?

(defn foo "I'm a function" [] :bar)

(def fee foo)

(repl/doc foo)
; => ([])
;   I'm a function
; nil

(repl/doc fee)
; => nil

delaguardo14:11:32

(alter-meta!
 (def fee foo)
 (constantly
  (meta #'foo)))
but this will copy all metadata from foo to fee. including line, column, name etc. From tooling (clj-kondo, cider) point of view those references will be identical. In most cases it is better to copy only chunks of original meta like docstring and arities

Alex Miller (Clojure team)14:11:53

the real question is - why not use the original function?

skylize14:11:10

Thanks. I think I got it. Pretty verbose.

(alter-meta!
 #'(def fee foo)
 #(update-in % [:doc] (:doc foo)))
Just thought of this, and I don't really understand why it doesn't work.
(def fee #'foo)

skylize14:11:43

The original function is fine. Just wanting to offer a stylistic alternative name.

delaguardo15:11:08

if you want only the docstring to be copied — then there is simpler approach:

(def ^{:doc (:doc (meta #'foo))} fee foo)

delaguardo15:11:35

(def fee #'foo) doesn’t work because metadata attached to the symbol fee not to the content it is referring to which is var foo

skylize15:11:54

Oh. Thanks. I tried that first, but it wasn't working. I must have had something typed wrong.

skylize15:11:00

How can I replicate (def ^{:doc (:doc (meta #'foo))} fee foo) using with-meta?

delaguardo15:11:18

you can’t. with-meta does not mutate the object passed as an argument.

(let [x [1 2 3]
      y (with-meta x {:foo 42})]
  [(meta x) (meta y)])
;; => [nil {:foo 42}]

skylize15:11:51

Hmmm. I was hoping that with-meta would make it easier to write a version of this macro that isn't broken. (Big fan of defing functions as compositions of other functions.

(def foo = (bar (baz))
So it would be great to be able to easily add docstrings during def in a general case.)
(defmacro def-with-doc [name docstring body]
  `(def ~name ~body)
  `(alter-meta! #'~name
                (fn [m] (assoc m :doc ~docstring)))
  `(var ~name))
And if that worked, direct aliasing could then be done with
(defn alias-fn [name target]
  (def-with-doc name (meta #'target) target))

delaguardo16:11:42

it would be easier to write alias as a macro than a function.

Alex Miller (Clojure team)16:11:26

the use case there is a little different (cross namespace, with a watcher to transfer val changes), but lots of similar stuff

skylize19:11:18

Thanks. That looks does like a great reference. I got it working perfectly with...

(defmacro def-with-doc [name docstring body]
  `(do
    (def ~name ~body)
    (alter-meta! (var ~name)
                  (fn [m#] (assoc m# :doc ~docstring)))
     (var ~name)))

(defmacro alias-fn [name target]
  `(def-with-doc ~name (:doc (meta (var ~target))) ~target))

(defn foo "a function" [x])
(alias-fn foo fee)
(def-with-doc fiz "foo . fee" (comp foo fee))

(meta #'foo) ; => {... :doc "a function ...}
(meta #'fee) ; => {... :doc "a function ...}
(meta #'fiz) ; => {... :doc "foo . fee" ...}
But then I realized I might also be interested in an argslist. This works if I quote out the argslist when calling the macro. Is there some way to accept an unquoted list of undeclared symbols like defn does?
(defmacro defcomp [name docstring arglist body]
  `(do
     (def ~name ~body)
     (alter-meta! (var ~name)
                  (fn [m#]
                    (merge m# {:arglists (list ~arglist)
                               :doc ~docstring
                               })))
     (var ~name)))

(defcomp fiz
  "fee . foo"
 '[x]
  (comp foo fee))
; => {... :arglists ([x]) ...}

Or Halimi15:11:26

Hi guys! I’m doing a bit leetcode style to know Clojure better and I have a question to convert string to ( or ) depends if the character appear once or twice. I tried to use ->>for it but I get clojure.lang.LazySeq instead of the answer I want -_^ \

Alex Miller (Clojure team)15:11:42

try pr-str instead of str

Or Halimi15:11:03

Amm doesnt seem to work. It doesnt change the seq to str correctly

Or Halimi15:11:34

here is my code

(defn number-to-parenthesis [number]
  ( if(> number 1) \) \( ))

(defn encode-dups [text] 
   (let [freq (clojure.core/frequencies text)]
      (->> 
        (seq text)
        (map #(number-to-parenthesis (get freq %1) ))
        (str)
      )
    )
)

MegaMatt16:11:43

I’m guessing you need to do (apply str) as the last thing in your thread last macro

skylize21:11:04

seq is meaningless here. frequencies works fine on a string directly (treating it as a seqable of chars).

(frequencies "foo")  ; => {\f 1, \o 2}
frequencies returns a Map where, in this case, the Character is the key and the count is the value. So when you call map, the transform function receives a pair of [char count]. (With "pair" meaning a sequence of 2 values.)
(map identity (frequencies "foo"))  ; => ([\f 1] [\o 2])
I will leave implementation of wrap-dups for you to solve. I suggest ignoring the rest of the exercise while working on it, and just focus on what wrap-dups returns. You might discover you can knock that out pretty quickly now. Notice the double square brackets. This syntax https://clojure.org/guides/destructuring the 1 argument (a pair) into a binding for each value in the pair.
(defn wrap-dups [[character count]]
  ...)

(map wrap-dups (frequencies "foo")) ; => (\f "(o)")
The only remaining issue is you are passing a sequence to str. So it will just print that sequence. In this case, it's actually even worse. t this stage you have lazy sequence, . So it gives you a string of the sequence... Actually it is even worse because at this stage you have a lazy sequence. Rather than realizing the lazy seq, str just calls .toString on the class, returning an internal representation along the lines of "clojure.lang.LazySeq@23cd2f12". What you really want is to spread the values of your sequence into the argument list, which is what apply does.
;; Each argument is provided separately.
(str \f \o \o)          ; => "foo"

;; Give it a list, and it makes a string of the list.
(str [\f \o \o])        ; "[\\f \\o \\o]"

;; Use each value in the sequence as an argument to `str`.
(apply str [\f \o \o])  ; => "foo
With it all complete, you get
(defn encode-dups [string]
  (->> (frequencies string)
       (map wrap-dups)
       (apply str)))

(encode-dups "foobarbazbuz") ; => "f(o)(b)(a)r(z)u"
Still not sure this will actually solve your leetcode. Depends what the the requirements are, which you specified rather vaguely. But based on your sample code (particularly your choice to use frequencies) this seems to be what you are after.

🙏 1
Or Halimi08:11:19

Thank you for the answer! I will sit on it later and see what we can do about it 🙂

dumrat17:11:21

hi, interop question: I'm looking at this: https://github.com/SolaceSamples/solace-samples-java/blob/main/src/main/java/com/solace/samples/java/HelloWorld.java And trying to convert this into clojure. Code (without password):

(ns dumrat.transs.solace
  (:import [java.util Properties]
           [com.solace.messaging.config
            SolaceProperties$AuthenticationProperties
            SolaceProperties$ServiceProperties
            SolaceProperties$TransportLayerProperties]
           [com.solace.messaging.config.profile ConfigurationProfile]
           [com.solace.messaging MessagingService]
           [com.solace.messaging.resources TopicSubscription])
  (:require [clojure.reflect :as r]
            [clojure.pprint :as pp]))

(def solace-connection-properties
  {:host ""
   :username "solace-cloud-client"
   :password "xxx"
   :vpn "testservice"})

(def solace-topic "try-me")

(defn- create-properties [solace-props]
  (let [p (Properties.)]
    (.setProperty p SolaceProperties$TransportLayerProperties/HOST (:host solace-props))
    (.setProperty p SolaceProperties$ServiceProperties/VPN_NAME (:vpn solace-props))
    (.setProperty p SolaceProperties$AuthenticationProperties/SCHEME_BASIC_USER_NAME (:username solace-props))
    (.setProperty p SolaceProperties$AuthenticationProperties/SCHEME_BASIC_PASSWORD (:password solace-props))
    (.setProperty p SolaceProperties$ServiceProperties/RECEIVER_DIRECT_SUBSCRIPTION_REAPPLY "true")
    p))

(defn create-messaging-service [properties]
  (.. (MessagingService/builder ConfigurationProfile/V1)
      (fromProperties properties)
      (build)
      (connect)))

(defn disconnect-messaging-service [messaging-service]
  (.disconnect messaging-service))

(defn create-message-receiver [messaging-service topic receivefn]
  (let [receiver (.. (.createDirectMessageReceiverBuilder messaging-service)
                     (withSubscriptions (TopicSubscription/of topic))
                     (build)
                     (start))]
    (.receiveAsync receiver receivefn)
    receiver))

(defn close-receiver [receiver]
  (.terminate receiver))

(defn- reflect-object [o]
  (pp/pprint (sort-by :name (:members (r/reflect o)))))


(comment
  (def messaging-service (atom (create-messaging-service (create-properties solace-connection-properties))))

  ;; Error here
  (def receiver
    (create-message-receiver @messaging-service
                             solace-topic
                             (fn [message]
                               (println "message received: " message))))
  #__)
And I get error when creating receiver:
; Syntax error (IllegalArgumentException) compiling at (form-init11310799634162923623.clj:58:5).
; No matching method withSubscriptions found taking 1 args for class com.solace.messaging.DirectMessageReceiverBuilder$DirectMessageReceiverBuilderImpl
This is what reflection says:
(defn- reflect-object [o]
  (pp/pprint (sort-by :name (:members (r/reflect o)))))

(reflect-object (.createDirectMessageReceiverBuilder @messaging-service))
=>
...
{:name withSubscriptions,
  :return-type com.solace.messaging.DirectMessageReceiverBuilder,
  :declaring-class
  com.solace.messaging.DirectMessageReceiverBuilder$DirectMessageReceiverBuild
erImpl,
  :parameter-types
  [com.solace.messaging.resources.TopicSubscription<>],
  :exception-types [],
  :flags #{:varargs :public}}
 {:name withSubscriptions,
  :return-type com.solace.messaging.MessageReceiverBuilder,
  :declaring-class
  com.solace.messaging.DirectMessageReceiverBuilder$DirectMessageReceiverBuilderImpl,
  :parameter-types
  [com.solace.messaging.resources.TopicSubscription<>],
  :exception-types [],
  :flags #{:public :bridge :synthetic}
So there are two withSubscriptions methods. But Clojure tells me that it seems to think I'm trying to invoke a single arg fn. I'm confused. What's the reason for this error?

Bob B17:11:55

I might be wrong here, but I only see the vararg method in the javadoc - I think the java version might get away with using the synthetic method, but I think from Clojure we need to use the vararg signature, which means passing an array, as described in <https://clojure.org/guides/faq#varargs>

👍 1
dumrat17:11:25

Makes sense, but doesn't seem to work.

(defn create-message-receiver [messaging-service topic receivefn]
  (let [receiver (.. (.createDirectMessageReceiverBuilder messaging-service)
                     (withSubscriptions (object-array [(TopicSubscription/of topic)]))
                     (build)
                     (start))]
    (.receiveAsync receiver receivefn)
    receiver))
Invoking gives error:
; Syntax error (IllegalArgumentException) compiling at (form-init11310799634162923623.clj:58:5).
; No matching method withSubscriptions found taking 1 args for class com.solace.messaging.DirectMessageReceiverBuilder$DirectMessageReceiverBuilderImpl

dumrat18:11:15

This works: (withSubscriptions (into-array TopicSubscription [(TopicSubscription/of topic)]))

Hachmaninow17:11:45

I am looking for a good idea how I could split my environments conveniently in the development process. I would like to differentiate three environments: 1) prod 2) test 3) dev. The environment controls the database connection being used and can be controlled per environment variable: - I would start my development REPL with ENV=dev. - I can run tests per command line with ENV=test. - And for production I set the correct environment in Docker ENV=prod. Very easy so far, but what would be a good practice to run the tests in my REPL and make sure to use test and not dev (since I’d like to preserve my more complex database state on dev and not interfere with the tests). I could imagine using some dynamic variable for this purpose, but maybe there is some better idea?

Ferdinand Beyer17:11:51

Hard to answer without knowing how your architecture looks like. It seems that you are relying a lot on global state. Ideally, your tests would not rely on global state, but you pass input to your functions in tests or set up some well-known test environment, maybe using fixtures

robertfw18:11:16

To extend on what Ferdinand says, getting a stateful system which you can pass to your functions is often solved by using a state management library like https://github.com/stuartsierra/component or https://github.com/weavejester/integrant/.

robertfw19:11:58

I haven't seen init before, I will have to give it a look!

cddr10:11:21

Another option is to get the database config through something like aero which provides for "profiles". In the aero config you can write basically a case statement with a branch for each profile for those variables that are profile specific

Hachmaninow20:11:58

Thanks a lot for the advice. Much appreciated and will take a look!

Ben Lieberman19:11:36

I'm a bit confused by this:

(str/blank? "hello") => false ;; okay
(cond-> "hello"
str/blank? (println " is not blank")) => prints "hello is not blank" ;; what?
This result seems to directly contradict my reading of cond->'s doc string

ghadi19:11:58

call macroexpand on the form and you’ll see what you are missing

👍 1
💯 1
Ben Lieberman20:11:55

I'm not sure I really see it. Looking at this

(let*
 [G__10080 "hello"]
 (if
  str/blank?
  (clojure.core/-> G__10080 (println "is not blank"))
  G__10080))
I though that maybe bc I was using println it was returning "hello" and then printing "is not blank" but I switched it to using str and that has the same result

ghadi20:11:50

(if x ) is always true if x is true, right?

ghadi20:11:07

str/blank? is truthy

ghadi20:11:21

note it's not calling (str/blank? "hello")

👍 1
Ben Lieberman20:11:34

oh gosh is it not calling the function facepalm

ghadi20:11:00

cond-> threads the expression on the right of the test, but doesn't thread anything into the test itself

Ben Lieberman20:11:40

oh well that's very silly of me. Thanks @U050ECB92

walterl20:11:24

https://github.com/worldsingles/commons has condp->: > an extension to cond-> that threads the expression through the predicate(s) as well as the result(s).

👀 1
seancorfield20:11:51

This exact same confusion cropped up in another thread over the weekend I think...

seancorfield20:11:38

...and, yes, it's exactly why we added condp-> and condp->> to our little "commons" library (although we've pretty much stopped using it at work as it does not improve readability).

👍 1
Ben Lieberman20:11:23

I think the part of the docstring that reads "for which the corresponding test expression is true..." is what threw me off

Ben Lieberman20:11:50

I interpreted that as "runs the test on the threaded thing"

seancorfield20:11:27

(cond-> "hello"
  (some? str/blank?)
  (println "yup, str/blank? is something!"))
🙂

🤝 1
fadrian21:11:11

I've written some code that uses the file-seq function to walk a directory and return the files therein. However, this function does not follow symbolic links. Is there any way to get this function to follow symbolic links?

fadrian21:11:16

The documentation for the function suggests that one should use nio to do "complicated" stuff like this, but this seems like overkill for a simple task like this.

ahungry22:11:24

-rw-r--r-- 1 mcarter mcarter 2 Nov  7 17:29 a
lrwxrwxrwx 1 mcarter mcarter 1 Nov  7 17:29 b -> a

user=>        (map (fn [f] (.getCanonicalPath f)) (file-seq (java.io.File. ".")))
("/tmp/foo" "/tmp/foo/a" "/tmp/foo/a")
^ would that suffice? File class "getCanonicalPath()" method? - you can see in my sample of symlink b pointing to a, you end up with the result of the expanded/resolved path

andy.fingerhut00:11:03

Symbolic links are not necessarily simple to handle. It is easy to make cycles containing symbolic links, which would cause the most obvious style of directory recursive traversal to go into an infinite loop, if you do not add special checks to prevent it

fadrian04:11:52

Thanks, @U96DD8U80 - that looks like it would work well enough. I'll try it out and see if I can get something like that working. @U0CMVHBL2 - I understand what you're saying. If I was working with a general file system, I'd have to do this. Luckily, this is for code that only allows the underlying file structure to be a DAG, so any symbolic links are checked for being back pointers before they're created and disallowed if they are. Thanks to both of you for your advice.