Fork me on GitHub
#beginners
<
2020-05-01
>
sroller00:05:35

Question to date and time data: is clj-time still the recommended lib for this? There's conflicting information on the interwebs about what to use. Apparently Java has improved a lot and it's now recommended to use java.time directly?

seancorfield00:05:38

I'm one of the maintainers of clj-time: please don't use it -- either use Java Time directly via interop, or one of the wrappers recommended in the clj-time readme.

bfabry00:05:28

lol, you need to make that note at the top even bolder and bigger Sean 😛 and red! maybe some <blink>

sroller00:05:54

@seancorfield thanks. I'll look into clojure.java-time then. I assume I can extract day, month, year etc from a value?

seancorfield00:05:08

I'll change the wording from "consider using" to "please use" perhaps.

sroller00:05:17

@bfabry yeah - perhaps in RED 🙂. Stackoverflow is full with old answers from 7 years ago. nothing new though.

sroller00:05:30

obviously not current anymore.

sroller00:05:52

the Clojure Cookbook also recommends clj-time 😄

bfabry00:05:11

ah yes SO is particularly vulnerable to bit-rot. once that answer is ticked as accepted it probably will never change

seancorfield00:05:26

Yeah, a lot of tutorials out there refer to clj-time -- I really don't know why it was so popular. Joda Time was already an immutable, value-based time library. clj-time really only adds a bit of syntactic sugar.

Bill Phillips00:05:54

Sugar tastes good.

😆 12
sroller00:05:17

@seancorfield Thanks for the help. I now can parse "2020-0430T20:27:56Z" :_)

sroller00:05:51

why does the first expression works but not the second? (= 2020 (t/as (t/zoned-date-time "2020-04-30T20:29:00Z") :year)) (= (list 2020 4) (t/as (t/zoned-date-time "2020-04-30T20:29:00Z") :year :day-of-month))

sroller00:05:03

from my ns (:require [java-time :as t])

seancorfield00:05:41

Day of month is 30, not 4.

seancorfield00:05:50

user=> (t/as (t/zoned-date-time "2020-04-30T20:29:00Z") :year :month-of-year :day-of-month)
(2020 4 30)
user=>

sroller00:05:05

I'd like to filter all datetime value of a specific date. I thought I'd extract year, month and day and then compare two lists, no?

sroller00:05:31

oh, I see what you mean.

sroller00:05:38

but the compare fails anyway

sroller00:05:30

ah, please disregard my last message - error between the ears

seancorfield00:05:43

Using Java Time directly is just as easy:

user=> (def year-month (juxt #(.getYear %) #(.getMonthValue %)))
#'user/year-month
user=> (year-month (java.time.ZonedDateTime/parse "2020-04-30T20:27:56Z"))
[2020 4]
user=>

seancorfield00:05:43

Or if you want the day as well

user=> (def year-month-day (juxt #(.getYear %) #(.getMonthValue %) #(.getDayOfMonth %)))
#'user/year-month-day
user=> (year-month-day (java.time.ZonedDateTime/parse "2020-04-30T20:27:56Z"))
[2020 4 30]
user=>

seancorfield00:05:47

There are plenty of good reasons for not bothering with a simple wrapper, and just using interop.

💯 4
seancorfield00:05:51

Unless you want to write platform-independent code, in which case cljc.java-time or juxt/tick could be used.

seancorfield00:05:05

Yeah, well, some people seem to think such code is worthwhile. I don't do cljs so I don't care 🙂

potetm01:05:40

Now that I think about it, it’s odd that I feel that way. In theory, they’re like letters or numbers. Their properties are derived from real life. But, if we’re honest, I’m pretty sure dates are a damn mess in every ecosystem.

potetm01:05:41

Even in a single ecosystem…. good lord java has 3 built-in competing date/time libs. And that doesn’t even include JodaTime!

bfabry01:05:16

they're not really "competing". but yeah time is hard. can't wait till we have to deal with vehicles that travel at an appreciable fraction of the speed of light someday (if it ever happens)

seancorfield01:05:30

Oh I agree, dates are the worst. We've been doing Clojure for a decade and we started with date-clj rather than struggling with Java since we were on Java 6 or Java 7 back then. We added clj-time at some point and started migrating to that, but I think that just made things more complex. Since we moved to Java 8 we started using Java Time (and clojure.java-time). We're on Java 11 right now and still have all three Clojure libraries in our code base 😐

😨 4
potetm01:05:46

Same story. Same ending.

seancorfield01:05:21

I am slowly replacing the first two with the third (and, yes, using the clojure.java-time wrapper a bit)... but we have nearly 100K lines of Clojure at this point so it's slow going...

👍 4
potetm01:05:43

It’s the sort of work that’s impossible to prioritize highly. Things somehow work, despite the inconsistencies. So, yeah, in small chunks over long periods of time is the only way to go.

potetm01:05:05

@bfabry you have not seen what I have seen. If they’re not “competing,” I don’t even wanna know what the proper word is. I’m sure it’s absolutely filthy.

seancorfield01:05:27

(and of course when you interact with JDBC, you tend to need java.util.Date anyway)

potetm01:05:01

Or do you mean java.sql.Date ? Or, wait, maybe java.sql.Timestamp troll

seancorfield01:05:48

They're both subtypes of java.util.Date 😛

seancorfield01:05:30

I just love how java.sql.Date extends java.util.Date and then disables all the time stuff! 😢

potetm01:05:55

yep, I was fixing to say, “only in the most technical sense!” lol

potetm01:05:24

it’s an absolute…. riot? yeah let’s say riot

sroller13:05:09

Seems I hit a hornet's nest with my date & time related question 🙂.

lol 4
seancorfield15:05:50

@U0TSNMT53 it's always a good discussion to have -- I think a lot of devs don't realize what a mess it can be and how complex date/time stuff really is (esp. timezones).

seancorfield15:05:25

We have all our servers set to UTC, our JVMs set to UTC, and our database set to UTC -- all to avoid timestamp complexities!

💯 4
👍 4
bfabry01:05:00

well there is one you're supposed to use, and 3 others that have writing on their main webpages saying "don't use this". so that's what I mean by not "competing". They're certainly all "there" and "conflicting" and "annoying" 🙂

potetm01:05:19

My fav is how, no matter how much I want to use java8 dates, I’m bound to use java.util.Date by the clojure reader now.

alexmiller01:05:07

You can change the data reader map to read tags however you want

potetm01:05:27

Oh well, no going back now. c’est la vie

hiredman01:05:36

The reader is specifically designed not to tie types to tagged literals

hiredman01:05:21

Like, the whole point is tags are semantics, not types

hiredman01:05:37

The default type the reader uses for #inst tags is Java util date, but given a reader you control you can make it anything else

potetm01:05:41

@hiredman you underestimate the dual power of ecosystem and inertia

potetm01:05:03

Can you? Yes. WIll you? Maybe later 😛

dpsutton01:05:17

and what will your deps do

💯 4
hiredman01:05:06

Use inst-ms a lot

potetm01:05:15

Reality is: I’m never gonna override #inst. I might do a custom tag, but probably not that either. Because I have #inst.

hiredman01:05:52

clojure.core/inst-ms

potetm01:05:34

HOW DID I NOT KNOW THIS FN???

8
potetm01:05:44

I FEEL BETRAYED BY ALL OF YOU

potetm01:05:58

@hiredman is my only real friend

seancorfield01:05:54

user=> (inst-ms (java.util.Date.))
1588296744460
user=> (.getTime (java.util.Date.))
1588296759500
user=>

potetm01:05:21

except inst-ms works on java.time.Instants

seancorfield01:05:44

Ah yes, just figured that out.

hiredman01:05:45

inst-ms ends up being a Rosetta Stone when you get a date, but are not sure what the exact type will be

alexmiller01:05:46

Doesn’t it work on impls of the Inst protocol?

IvanS09:05:04

Hello, please what's wrong with (use '[ring.util]) ? I want to import all functions from ring.util

practicalli-john12:05:21

use brings along more than the functions in the namespace, it also brings the dependencies that the other namespace has. At some point you will get conflicts and it could be time consuming to resolve. The require with :as or :refer keeps your code very clean and specific in terms of what it pulls in to a namespace.

practicalli-john12:05:16

One theory is that topojson files are not working with Vega-lite, perhaps something about the topology they add. But I am still testing this idea

noisesmith15:05:21

@U05254DQM I think that was meant for a different slack :)

practicalli-john15:05:12

@noisesmith Oh yes it was, unless you can help me find some GeoJSON files that work with Vega-lite and match up to the http://Gov.uk data 🙂

noisesmith15:05:33

haha I''ll see what I can do

noisesmith15:05:59

nothing, I can do nothing, hope that helps

4
practicalli-john15:05:44

Next time you are at the Office of National Statistics, please ask them why there GeoJSON files dont work with vega 😂

Lennart Buit09:05:58

It makes it harder to understand where a function is coming from. Is it clojure.core? Is it code in my current namespace? or is it used? So people tend to prefer to add an alias, so that its immediately obvious that its coming from somewhere else. There are a few exceptions, clojure.test is usually required refer’ing all (at least on JVM). And you also have the option to refer specific functions with require.

Anthony Khong10:05:08

Hello, I’ve been stuck with this problem for a bit. Is the following possible? If so, could you please point me to the right direction? I’ve got a verbose function like:

(defn word2vec [{:keys [max-iter
                        step-size
                        window-size
                        max-sentence-length
                        num-partitions
                        seed
                        vector-size
                        min-count
                        input-col
                        output-col]
                 :or   {max-iter 1,
                        step-size 0.025,
                        window-size 5,
                        max-sentence-length 1000,
                        num-partitions 1,
                        seed -1961189076,
                        vector-size 100,
                        min-count 5
                        input-col "sentence"
                        output-col "features"}}]
  (-> (Word2Vec.)
      (.setMaxIter max-iter)
      (.setStepSize step-size)
      (.setWindowSize window-size)
      (.setMaxSentenceLength max-sentence-length)
      (.setNumPartitions num-partitions)
      (.setSeed seed)
      (.setVectorSize vector-size)
      (.setMinCount min-count)
      (.setInputCol input-col)
      (.setOutputCol output-col)))
The thing is that the method name is always just the camel-case version of the arg name, and each argument is repeated four times. I feel like it should be possible to write a macro such as:
(defstage word2vec
    Word2Vec
    {:input-col "sentence"
     :output-col "features"
     :max-iter 1,
     :step-size 0.025,
     :window-size 5,
     :max-sentence-length 1000,
     :num-partitions 1,
     :seed -1961189076,
     :vector-size 100,
     :min-count 5})
I’m stuck in the part where I’m converting the key to the camelcase method - something like:
(defmacro set-param [spark-stage k v]
    (let [method `(symbol (str "set" (->PascalCase (name ~k))))]
      `(. ~spark-stage ~method ~v)))

  (def stage (Word2Vec.))
  (def k :max-iter)
  (set-param stage k 5) ;; throws java.lang.IllegalArgumentException
but it obviously doesn’t work! Is this the wrong approach altogether?

sroller12:05:13

@seancorfield I rather stick to the Clojure wrapper as long as possible. I avoided Java for the recent 25 years and I'd like to keep it like that 🙂. Thank you for explaining the options though. I know that I have go out to Java anyway. When I started looking into Clojure I was a bit taken back by the attitude "it's better than Java here and better than Java there". I don't care about Java ;-).

😄 4
g14:05:02

hey everyone, trying to figure out what’s going on with my setup as follows:

(with-redefs-fn [sut/a (fn [& args] (prn "hi"))]
  (sut/b "test!"))
where b calls a. however, when i run this, a has its original value, namely that before it was redef’d. why isn’t it rebinding?

g14:05:48

i think i figured it out. b'

g14:05:00

is calling a through another thread, which perhaps is messing with the binding

mloughlin15:05:08

with-redefs-fn isn't thread safe

✔️ 4
noisesmith15:05:00

yeah, if b creates a thread, with-redefs-fn needs to be forced to wait until b's thread finishes somehow (or you need something better than with-redefs-fn - eg. a dynamic binding wouldn't have this problem as they are shared with future threads and you can propagate them to other threads as needed)

g15:05:43

i was looking at alter-var-root .. and then attempting to set it back afterwards (this is in a test)

g15:05:57

i’d rather not change it to be dynamic just for the test

g15:05:08

but i do like that better

noisesmith15:05:51

@gtzogana you can use something similar to the cljs async wrapper for tests - which makes sure a form doesn't exit until a callback is called. You can make it yourself like this:

(let [done (promise)]
  (with-redefs-fn [sut/a (fn [& args]
                           (deliver done true)
                           (prn "hi"))]
    (sut/b "test!")
    @done))
with-redefs-fn will not exit (and thus a will stay in place) until a tells it it's ok via done

noisesmith15:05:23

if a needs to be called n times, you need a different place to deliver to done, or a conditional deliver, but the concept stays the same

g15:05:35

oh, cool!

g15:05:39

good idea, thanks

noisesmith15:05:33

there's still a gotcha: with-redefs-fn / with-redefs are able to lose your original binding permanently if used in two threads at once

g15:05:45

do you mind explaining how this happens? i noticed it in the docs but it’s not quite clear what’s going on

noisesmith15:05:36

so, (with-redefs [f g] (body)) does the following: • save the global binding of f as f-old • set the global binding of f to g • use a finally clause so that ensures no matter what, g is replaced with f-old

noisesmith15:05:32

let's imagine two threads call with-redefs: • thread a calls (with-redefs [f g] (body)) • f is now g, a has f-old of f • thread b calls (with-redefs [f h] (body)) • f is now h, b has f-old of g • a exits, automatically setting f to f-old • b exits, automatically setting f to g • the original f no longer has any gc root, and the garbage collector deletes it permanently

noisesmith15:05:28

so by calling with-redefs twice, you permanently lose the original value (if thread a exits before thread b, and their execution overlaps)

noisesmith15:05:16

classic race condition

g15:05:35

oh that is funky. thank you for the explanation!

noisesmith15:05:55

most test suites will never do this, but consider that this usage ensures your tests can't correctly be run concurrently

ghadi15:05:29

with-redefs is a strong signal that your code is missing an argument

g15:05:32

to clarify, i’m using it to inject artificial latency

g15:05:22

how so?

Johnson15:05:44

Hey, was hoping to get some help. I'm trying to use the react-particles-js library with a reagent/re-frame project, I can get it to display, but the interactivity/hover isn't working. Was wondering if there is something glaringly obvious I'd missed, pretty new to this. This is the element in question:

[:> Particles
        {:particles {:number {:value 200
                             :density {:enable true
                                       :value_area 1000}}}
         ;; This bit doesn't work:
         :interactivity {:detect_on "canvas"
                         :events {:onhover {:enable true
                                   :mode "repluse"}}}]
Thanks for your help

Johnson15:05:05

I also realise that this might be the wrong thread. Should I ask in the clojurescript thread instead?

noisesmith15:05:18

I bet #clojurescript or #reagent is more likely to have your answer

Johnson15:05:15

Okay thanks 🙂

Michael W16:05:52

What is :cljsrn I see in some cljc files on github? And how is that different from :cljs?

Sanghyun Lee16:05:09

Hello, I have a question about Cursive and Clojure test. When I def inside a deftest the IDE says the variable cannot be resolved. How could I prevent this warning? Thanks.

Patrick Farwick16:05:56

hey @hammerha, I am pretty new, but I have never seen a def defined in a defn or deftest

noisesmith16:05:13

@hammerha def is for making namespace scope definitions, if you want something to use inside ns-test use let

noisesmith16:05:50

def there will work - sometimes - but it means every time the test runs the definition changes and all tests can see the definition, probably not what you were aiming for

noisesmith16:05:24

(deftest ns-test (let [abc 1] (is (= ....)))

Sanghyun Lee16:05:37

great, thanks a lot for the answer @patrick.farwick @noisesmith!

michaelb20:05:02

if I want to work with paths in a cross-platform manner in Clojure, what is roughly the equivalent of Node.js path module?

ghadi20:05:19

java.nio.file.Path

ghadi20:05:49

pretty much every project I write has:

(defn path [s & ss]
  (java.nio.file.Path/of s (into-array String ss)))

seancorfield20:05:12

? 🙂 In response to path? 🙂

alexmiller20:05:28

in response to making functions to work with Path

seancorfield20:05:47

Ah, yeah, the whole cross-platform file pathing nightmare...

alexmiller20:05:08

seems like I spent most of this week converting between Files and Paths :)

seancorfield20:05:29

And that sometimes on Windows you get c:\path\to\file.ext and other times you get /c:/path/to/file.ext 🤯

alexmiller20:05:46

yeah, it's a minefield

seancorfield20:05:49

(I'd never seen the latter until recently)

Lennart Buit20:05:43

Anecdote: My very first programming project on university (a board game) had a bug with file paths, leading it not to be able to load taunts.txt, which it considered fatal and therefore terminated. I think it was tripping on spaces or something, but yeah, not a very beginner friendly API :’)

seancorfield20:05:22

Also, on Windows, some of the APIs treat certain filenames as reserved (try using con.clj with the File stuff 🙂 -- or prn.txt, nul.jpg, etc...)

Lennart Buit20:05:20

oh yeah, they had special meaning in dos, right?

phronmophobic20:05:41

i’ve always just worked with .Files directly via . is there a reason to prefer Path?

seancorfield20:05:53

Yup, see https://github.com/seancorfield/depstar/issues/8#issuecomment-489873152 which was the bug report against an early version of depstar that had that problem!

alexmiller20:05:57

there's a lot of interesting stuff in java.nio that depends on Path

phronmophobic20:05:15

I skimmed through the bug, but I can’t figure out how Path vs File figures in?

seancorfield20:05:03

@U7RJTCH6J I was responding to @UDF11HLKC not you 🙂

😳 4
seancorfield20:05:42

@U7RJTCH6J This article talks about the new-in-Java 7 nio stuff https://www.oreilly.com/content/java7-features/ (starts halfway down).

👍 4
alexmiller20:05:56

the main thing that kind of blew mind a little is that java 7 has both glob and regex file path matching built in since Java 7

alexmiller21:05:23

it's a pain in the ass to use well, admittedly

ghadi21:05:56

sorry to trigger you @U064X3EF3

alexmiller21:05:40

no worries :)

Adrian Smith22:05:51

I'm packaging a jar using uberjar, and getting this error when I run the resulting jar file

Exception in thread "main" java.lang.NoSuchMethodError: clojure.lang.Util.loadWithClass(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
	at inspect.core.<clinit>(Unknown Source)
When running
java -jar target/inspect.jar
has anyone got any ideas on how to debug?

dorab22:05:44

How are you creating the uberjar?

Adrian Smith22:05:42

using the following steps:

mkdir classes
clj -e "(compile 'inspect.core)"
clojure -A:uberjar --main-class inspect.core
java -jar target/inspect.jar
Taken from https://github.com/tonsky/uberdeps#creating-an-executable-jar

noisesmith23:05:17

oh, you aren't using lein, deleting my message

Adrian Smith23:05:43

ah yeah I should have written uberdeps not uberjar when explaining my problem

noisesmith23:05:51

did you miss the step where you add classes to your paths?

Adrian Smith23:05:18

my deps.edn looks like

{:paths ["src" "classes"]
 :deps {org.eclipse.lsp4j/org.eclipse.lsp4j {:mvn/version "0.8.1"}
        org.clojure/tools.nrepl {:mvn/version "0.2.13"}}
 :aliases {:uberjar {:deps {uberdeps {:mvn/version "0.1.10"}}
                     :main-opts ["-m" "uberdeps.uberjar"]}}}

Adrian Smith23:05:18

I've seen a couple of people with this issue https://www.google.com/search?q=NoSuchMethodError%3A+clojure.lang.Util.loadWithClass but didn't find any answers

noisesmith23:05:20

I've found depstar painless, it does what I want and even lets me use clojure.main as my entry point (pointing at my main ns) instead of having to aot compile

hiredman23:05:51

try delete all the files in classes

hiredman23:05:11

you also don't specify a clojure version in your deps

hiredman23:05:55

so it looks like you are compiling with one version, but what is getting packaged in the uberjar is another (older version)

Adrian Smith23:05:50

yeah that looks like it's working thank you, I'll checkout depstar at some point too seems like less steps generally

Adrian Smith23:05:43

I always get confused about when to add Clojure as a dependency in deps

Adrian Smith23:05:03

ah ok my kinda rule

seancorfield23:05:32

@sfyire If it helps in the future, if you use clj-new to create new projects, they automatically have built-in aliases to create JAR or uberjar files, and know how to deploy to Clojars etc.

seancorfield23:05:53

https://github.com/seancorfield/clj-new -- the app and lib projects it generates use (my fork of) depstar to produce uberjars (with AOT compilation etc) and thin JARs respectively. They also have :test and :runner aliases to run your tests etc.

Adrian Smith23:05:48

ah I didn't know clj-new did that, cool

seancorfield23:05:32

@sfyire Feel free to hit me up via DM if you have any questions about clj-new or depstar -- or anything in my dot-clojure repo (which is full of "interesting" stuff to add to your ~/.clojure/deps.edn file 🙂 )