Fork me on GitHub
#beginners
<
2020-08-24
>
fredmonroe02:08:09

what's a preferred library method for formatting long floating point numbers and or currency strings? i'm seeing a lot of choices and am wondering if there is something most people choose?

Alex Miller (Clojure team)02:08:24

the JDK has an extensive library built into it for both of these

Alex Miller (Clojure team)02:08:52

java.text.NumberFormat/java.text.DecimalFormat are good classes to look at - both of these have support for locale-specific formatters and custom stuff

Trung Dinh03:08:46

hi, quick question please, I’m trying to use memoize with :ttl following example from https://github.com/clojure/core.memoize/wiki/Ttl but when (require '[clojure.core.memoize :as memo]) I got file not found , not sure what did I miss? thanks

Trung Dinh03:08:51

e.g., this works well without require any thing… (def m-fib (memoize (fn [n] (condp = n 0 1 1 1 (+ (m-fib (dec n)) (m-fib (- n 2))))))) But when I try to put in :ttl, it can’t find the namespace…

seancorfield03:08:39

memoize is a built-in (`clojure.core`) function.

seancorfield03:08:19

In order to use the Contrib libraries, such as clojure.core.memoize you need to add them to you dependencies in your project.

seancorfield03:08:29

Are you use lein or Clojure CLI (with deps.edn).

seancorfield03:08:53

For lein you need [org.clojure/core.memoize "1.0.236"] in your :dependencies vector; for CLI you need org.clojure/core.memoize {:mvn/version "1.0.236"} in your :deps map.

👍 3
3
Trung Dinh05:08:25

thanks @U04V70XH6, all good now, didn’t notice the difference btw built-in function and Contrib libraries

chucklehead03:08:37

I'm working with XML and was considering using multimethods with dispatch based on tag name. I have several tags/types that share the same schema but have different names in various messages. Would something like this be the right way to create a common handler for those tags? 

(derive :ns/a :common/thing)
(derive :ns/b :common/thing)

(defmulti parse-element :tag)
(defmethod parse-element :common/thing
  [element]

  ...)

hiredman05:08:15

I would pull the body implementation of parse-element as a defn for any shared implementations and have the defmultis call that before using derive

Schpaa07:08:29

Is there a shortcut to make a map out of some identifiers/values; say: this that those -> {:this this :that that :those those} ?

Kevin07:08:14

This is one way, maybe someone has a simpler version:

(into {} (mapv (juxt keyword identity) ['this 'that 'those]))

Schpaa07:08:42

I like that one

Lu07:08:31

Mmm zipmap?

gon07:08:35

(defmacro gkzipmap [& args] (zipmap (map keyword '~@args) ~@args))``

😀 3
Endre Bakken Stovner08:08:39

According to http://purelyfunctional.tv, this should fail silently:

(async/go (throw (RuntimeException. "Hello")))
However, when I try it, I get a stack-trace. Why?

Endre Bakken Stovner08:08:56

Exception in thread "async-dispatch-4" java.lang.RuntimeException: Hello
	at core_async_playground.errors$eval16095$fn__16096$state_machine__13197__auto____16097$fn__16099.invoke(form-init855929826269882431.clj:1)
	at core_async_playground.errors$eval16095$fn__16096$state_machine__13197__auto____16097.invoke(form-init855929826269882431.clj:1)
	at clojure.core.async.impl.ioc_macros$run_state_machine.invokeStatic(ioc_macros.clj:973)
	at clojure.core.async.impl.ioc_macros$run_state_machine.invoke(ioc_macros.clj:972)
	at clojure.core.async.impl.ioc_macros$run_state_machine_wrapped.invokeStatic(ioc_macros.clj:977)
	at clojure.core.async.impl.ioc_macros$run_state_machine_wrapped.invoke(ioc_macros.clj:975)
	at core_async_playground.errors$eval16095$fn__16096.invoke(form-init855929826269882431.clj:1)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
#object[clojure.core.async.impl.channels.ManyToManyChannel 0x77ac7cd6 "clojure.core.async.impl.channels.ManyToManyChannel@77ac7cd6"]

Endre Bakken Stovner08:08:37

I tried removing all the content in my .lein/profiles.clj and I still got an error stack trace.

delaguardo08:08:01

this exception is not thrown

(try (async/go (throw (RuntimeException. "Hello"))) (catch RuntimeException e (prn "!!!")))
you can try to silence it and you will see that this exception is returned from the channel

Endre Bakken Stovner08:08:36

Thanks! The more I play around with core.async, the more it feels like "I don't know, and I don't wanna know" XD

delaguardo08:08:37

correction: it is thrown but in another thread

Endre Bakken Stovner10:08:01

But is the reason it is shown to throw something that Clojure has become updated or what? I do not understand why multiple sources say it should fail silently when it does not 🙂

delaguardo10:08:27

it is shown only in repl. as an example:

(def x (async/go (throw (RuntimeException. "Hello"))))

delaguardo10:08:23

the repl will show result of expression. in that case it would be ’#x or something similar

delaguardo10:08:50

so your sources right but requires a little bit more information about “fail”

delaguardo10:08:29

“fail” here means “unexpected result” and “silent” — if that happens deep in call-stack the only way to notify caller - examine the result

Endre Bakken Stovner10:08:04

Okay, I will have to play around with it some more to understand. In a leiningen project this also shows an error on the command line (`$ lein run`):

(defn -main [& args]
  (let [x (async/go (throw (RuntimeException. "Hello")))]))
(That is, not in the REPL).

Jan K12:08:08

The exception is printed to stderr by the default exception handler of the ThreadGroup that go blocks are using. You can override that by using Thread/setDefaultUncaughtExceptionHandler

Jan K12:08:27

I think this behaviour was changed recently, so tutorials are probably out of date.

Endre Bakken Stovner16:08:59

Thanks to you too! Now I understand much better.

Endre Bakken Stovner10:08:09

I have this piece of code in a go-block:

(if (< val (@store :last-found))
  ;cleanup like close channel and such
)
Is there a way to ensure only one thread enters that if?

Ben Sless11:08:44

You can create an atom which every task increments when it starts and decrements it after it finishes. Only enter the cleanup piece of code if the atom is zero. Another option is, if you run all your tasks in go blocks or threads, is use the facts they return channels, then write code which waits for takes from all tasks to complete (meaning they're done). Then wait for that code to complete. You're guaranteed to have it complete only after all tasks have finished.

👍 3
Ben Sless11:08:13

Toy example:

(require '[clojure.core.async :as a :refer [go chan <! <!! >! >!! thread]])

(defn task [args]
  (Thread/sleep 500)
  (println args)
  args)

(defn drain
  [ch]
  (go
    (loop []
      (when-let [x (<! ch)]
        (recur)))))

(time (<!! (drain (a/merge (map (fn [x] (go (task x))) (range 32))))))

Ben Sless11:08:44

Note that the go-blocks thread pool has only 8 threads by default, so the above code will take ~2seconds to run

👍 3
Trung Dinh13:08:54

hi again, a question about error handling please, how do you do it when doing filter? e.g I have 2 vectors and want to filter one based on elements of the other like this:

(def tmsall [{:givenName nil :surname nil :email "[email protected]"}
             {:givenName "Alice" :surname "Spring" :email ""}
             {:givenName "Bob" :surname "Summer" :email "[email protected]"}
             {:givenName "Carol" :surname "Autumn" :email "[email protected]"}
             {:givenName nil :surname nil :email "[email protected]"}
             {:givenName "Danny" :surname "Winter" :email "[email protected]"}
             ])

(def tslack ["Bob Best Summer" "Alice Spring"])

(try (filter (fn [m] (some (fn [s]( and (clojure.string/includes? s (:givenName m))
                                   (clojure.string/includes? s (:surname m)))) tslack))
             tmsall)
     (catch Exception e (str "exception: " (.getMessage e))))
the clojure.string/include? will throw java.lang.NullPointerException , I tried the try…catch above but not seems to work. And second question, if I just want to skip such Exception and run the filter with the rest of the elements, is it possible to do that? Thanks

Alex Miller (Clojure team)13:08:51

filter returns a lazy seq so it won't throw until that is later realized

Alex Miller (Clojure team)13:08:24

you probably want to move the try/catch into the fn (or better, test for nil)

dpsutton13:08:24

and you could easily add another filter to just ones with both a :givenName and a :surname

Trung Dinh13:08:08

I guess that’s why people never use try…catch with lazy seq return functions, that makes sense…, I just try putting the try/catch in the fn, and yes it catch the exception, interestingly, those nil elements also added to the final filtered elements… @dpsutton I was trying to use includes? as some of them have middle name/strange other name formats 🙂 … anyway, thanks @alexmiller, @dpsutton

Zebra15:08:27

How to refer to dev ns in comment blocks? for example when using component

(comment 
  (fn-call dev/system additional arg))

mloughlin16:08:50

is the dev namespace loaded into REPL?

mloughlin10:08:45

if the dev namespace has been loaded into the REPL (and has the system var at the time of evaluation) I think dev/system should be accessible. Maybe someone else knows otherwise?

mloughlin10:08:17

in my current REPL I created a namespace called dev with (def system "hi") , then from a separate namespace eval'd

(comment 
  (prn dev/system))) => "hi"
and it works

Zebra10:08:56

then im probably doing it wrong? any tips on how to reexport required defn?

Zebra10:08:01

i use this from reloaded template

(ns dev
  "Tools for interactive development with the REPL. This file should
  not be included in a production build of the application.
  Call `(reset)` to reload modified code and (re)start the system.
  The system under development is `system`, referred from
  `com.stuartsierra.component.repl/system`.
  See also "
  (:require
   [ :as io]
   [clojure.java.javadoc :refer [javadoc]]
   [clojure.pprint :refer [pprint]]
   [clojure.reflect :refer [reflect]]
   [clojure.repl :refer [apropos dir doc find-doc pst source]]
   [clojure.set :as set]
   [clojure.string :as string]
   [clojure.test :as test]
   [clojure.tools.namespace.repl :refer [refresh refresh-all clear]]
   [com.stuartsierra.component :as component]
   [com.stuartsierra.component.repl :refer [reset set-init start stop system]]
   [{{main-ns}}]))

;; Do not try to load source code from 'resources' directory
(clojure.tools.namespace.repl/set-refresh-dirs "dev" "src" "test")

(defn dev-system
  "Constructs a system map suitable for interactive development."
  []
  (component/system-map
   ;; TODO
   ))

(set-init (fn [_] (dev-system)))
i can refer to dev/system in repl prompt but not in comment blocks

mloughlin10:08:43

how are you evaluating the comment expression?

Zebra10:08:10

cursive evaluate

mloughlin10:08:38

I don't know Cursive, but with my editor that type of evaluation just sends the s-expression to the REPL like a copy/paste almost. What happens if you run require from your comment before trying to eval the system variable? (comment (require 'dev) (dev/system ...))

mloughlin10:08:46

(also, what error message do you get?)

Zebra10:08:58

it works

3
stopa19:08:23

Hey team, noob question -- how would you debug this? Context: I am trying to upload a file to google cloud. I look at https://googleapis.dev/java/google-cloud-storage/latest/com/google/cloud/storage/Bucket.html#create-java.lang.String-java.io.InputStream-com.google.cloud.storage.Bucket.BlobWriteOption...- And try to replicate it:

(def bucket (-> (StorageClient/getInstance) .bucket)
;; #object[com.google.cloud.storage.Bucket 0x4f155e75 "Bucket{name=journaltogether.appspot.com}"] 

(def input-stream (FileInputStream. (:tempfile file)))
;; #object[java.io.FileInputStream 0x59d8712 "java.io.FileInputStream@59d8712"]
And now I try:
(.create bucket "foooo.png" input-stream "image/png" (into-array Storage$BlobWriteOption []))
This tells me:
No matching method create found taking 4 args for class com.google.cloud.storage.Bucket
Buut, it does seem to have these: -- How could I debug further?

dpsutton19:08:33

Clojure is dynamic and doesn’t have the type information. There are four overloads for this method but clojure needs to figure out which of those four to invoke. If you type hint your args you can give clojure more information so that it can choose which of the four to call.

❤️ 3
seancorfield19:08:06

You might be able to get away with just hinting ^.InputStream input-stream ^String "image/png" but you might also need to hint the fourth argument which is a bit trickier/less obvious.

❤️ 3
stopa19:08:57

Okay getting close!

stopa19:08:26

(defn upload-att [{:keys [filename ^String content-type tempfile] :as att}]
  (let [bucket (.bucket (StorageClient/getInstance))
        ^String blob-name (str (UUID/randomUUID)
                               "/"
                               filename)
        ^InputStream input-stream (FileInputStream. tempfile)
        ^Storage$BlobWriteOption options  (into-array Storage$BlobWriteOption [])]
    (.create
      bucket
      blob-name
      input-stream
      content-type
      options)))
I typed all of these guys, but it still seems to be having trouble. My intuition is that I typed something incorrectly. maybe options?

stopa19:08:18

Aha!

(defn upload-att [{:keys [filename content-type tempfile] :as att}]
  (let [bucket (.bucket (StorageClient/getInstance))
        blob-name (str (UUID/randomUUID)
                               "/"
                               filename)
        input-stream (FileInputStream. tempfile)
        options (into-array Bucket$BlobWriteOption [])]
    (.create
      bucket
      ^String blob-name
      ^InputStream input-stream
      ^String content-type
      options)))
Did the trick. Thanks for the help team!

mloughlin10:08:45

if the dev namespace has been loaded into the REPL (and has the system var at the time of evaluation) I think dev/system should be accessible. Maybe someone else knows otherwise?