Fork me on GitHub
Ben Grabow04:11:36

I am getting started with Cursive. I have opened a leiningen project, and I want to start a REPL from the project's root dir, but when the REPL starts it appears to eval the <project-name>/core.clj file. If I have errors or incomplete code in that file the eval will error and the REPL won't start. Is there a way I can prevent the REPL from eval'ing the file on startup?


@ben.grabow I'm not familiar with Cursive but @cfleming is very helpful over in the #cursive channel (he's not in this channel).

šŸ‘ 4

@stardiviner Incanter requires a very old version of java.jdbc -- it hasn't been updated to work with any recent versions.


I suspect you'll need to choose something way back in the 0.2.3-ish range?


Is there a way to stick Incanter to use specific java.jdbc version? I think Incanter should do this by default.


Isn't this should be dependency handling when released? Hmmm, first time meet this problem.


Well, Incanter depends on clojureql and here's the java.jdbc dependency:


If you remove org.clojure/java.jdbc from your project.clj, it will use the one that Incanter expects.


But you can't have two different versions -- and you'll be forced to use that very old version's API in your own code too for that project.


I added a comment to that GitHub issue.


It's interesting that clojureql went five years with no changes (from 2013 until early 2018). It was updated to make it work with Clojure 1.9 but the other dependencies weren't updated.


Maybe the maintainer doesn't update a lot. This is really bad of locking support on new release of esstensioanl basic libraries like jdbc.


I want to convert a date string into a date object to be used by clj-time.core/date-time. Here is my try: (.format (new SimpleDateFormat "yyyy-mm-dd hh:mm:ss") "2018-02-01 12:11:05") But it reports error: `java.lang.IllegalArgumentException Cannot format given Object as a Date`. Seems my syntax is wrong.


I don't know Java. don't know whether it is correct for invoke method like that.


@stardiviner To be fair, most libraries don't change their API as much as java.jdbc has had to change over the years. When I took it over, it was a fairly old-fashioned API, relying on global dynamic variables which doesn't compose well (if you needed to interact with more than one DB) and options weren't consistently passed through everything -- and it used named arguments (with keywords) instead of option maps.


But I signaled deprecation long in advance of removing anything and I kept the old API around in a separate namespace for a while as well. I hoped everyone would update their libraries to the modern API over a period of a few years šŸ˜


@stardiviner Regarding clj-time, you can use clj-time.format and parse the string into a date/time object.


BTW, if you're starting a new project from scratch on Java 8 or later, I would recommend not using clj-time and instead using Java Time (built-in), via the wrapper if you really want to avoid Java interop.


(and the clj-time readme makes that clear)


This is terrible. I wish those libraries can update on new API. Is there a way to find out all clojure libraries rely on jdbc, then send email or IM notifications to authors/maintainers?


Thanks for suggestion.


Hum, I wonder in this case, had you changed the name of the lib, then both versions could have co-existed on the classpath


Reminds me of that Rich Hickey talk


About breaking changes being new names, and not version change


Well, predates that talk by many years šŸ™‚


I took it over (as contrib.sql) back in 2011.


When it was part of the monolithic contrib library and everything got split up and renamed.


Haha, I know, not criticizing, just realizing some of the value of that talk. Though for some reason, the JVM core devs are very against that idea.


I asked the Clojure/core folks for feedback on the API and what it would take to get it to 1.0.0 -- and they wanted the dynamic variables removed and didn't like the namespace organization so I made a lot of changes...


Oh, wow I see. So Incanter transitively rely on a really really old version of it


Incanter was pretty much abandonware for quite a while...


Maybe that's a bit harsh šŸ™‚


It has been maintained, but it's kinda stayed on an old stack of libraries so the underpinnings haven't changed much for years.


I've always been annoyed by not allowing two versions on the classpath. I still am not sure why Java doesn't want to allow it. With the new modules in Java 9+ they went as far as having a check to see if you were appending versions to the end to trick the module system in allowing it.


I think it's a security argument


Two versions of a library? I thought it was allowed on the classpath, but you only got the first one?


Well, ya, I think that's true. But I meant more, that the JVM would be smart enough to know which class came from what versions and both could co-exist


Loading two different versions of a library that might end up passing references to data objects to each other (either directly, or indirectly via how they are used in the application) is pretty much a guaranteed recipe for disaster, I would think.

Lennart Buit07:11:45

Well, they probably didnt want modules to become OSGi~like?


OSGi... shudder


There was an opportunity with Jigsaw. Since they add modules which is basically an official way to mention dependencies. If they had added versions to it. Then the JVM could have loaded both, solving the problem of Jar hell for good.


There are some issues with it also though. Most of the time, you want to force everything to use the latest mintor version, like the latest backward compatible version


So that the overall size stays small, and also to get most fixes and security patches.


What I personally would have wanted, is that it doesn't allow two versions, unless you explicitly whitelist them.


In which case it does

Lennart Buit07:11:03

also, do you tho, if someone fails to adhere to semantic versioning and breaks compatibility in a point release, your system kills itself


Well, I think the idea is that. You decide to depend on the latest version. And you use it in your code. One of your dependencies unfortunately depends on an older version. So now you are forced to choose which one to actually use. If you force the new, it might break the lib expecting the old (in case the new was not backward compatible). If you force the old, you're stuck on old features. So, what I wanted was to give me the choice. I can go new, old or say just load both, let me use the new and the dependency can continue to use the old.


The latter though is impossible right now based on the way the JVM works.

Lennart Buit07:11:43

yeah but I was specifically wondering on having librariers, lets say a version 1.0.x, which - accidentally - received an incompatible change in a bugfix release, like 1.0.7

Lennart Buit07:11:32

if all your components suddely start to use the highest within a major, so 1.0.x, your system dies. I think upgrading versions should always be a choice that needs a developer


Ya, that's also a problem, but I feel it is a separate issue.


I'm talking say you defined a dependency on X v1.2.4 And you also have a dependency on Y 2.3.1 But Y 2.3.1 uses X v0.5.3 You can not start a JVM instance in this case. It will either fail, or force the use of either 1.2.4 or 0.5.3 for both your code and Y's.


This is different then specifying a dependency on say X v1.2.N where every time you build it automatically pull in the latest minor version. That is risky, for what you described.


The reason you can't start a JVM with both, is that the class names and package names conflict. And when your code says import com.x.Class the JVM doesn't know if it needs to load the class found in 0.5.3 or the one in 1.2.4

Lennart Buit07:11:22

but with clever namespacing that would be solvable


Well, that's basically one of Rich's point: Never ever break backwards compatibility, and if you do, that's not a new version, that's a whole new package with a whole new name, namespace, git repo, etc.


He argues version upgrades should always be backward compatible. So you don't need SemVer, because that implies that it isn't always safe to upgrade.

Lennart Buit07:11:30

well I think there is a middle ground there


At least that's what I understand from the talk. And then he goes on thinking about if this was true, then how could we detect backwards incompatible changes, and could we depend on functions instead of packages, and even have a Sha on them, so if the code path you depend on hasn't been touched, you know that it will still work, etc.

Lennart Buit07:11:12

I need to watch that talk before I can judge


Haha, ya, its one of his more open ended. He doesn't really propose a concrete solution, just challenges the audience to think outside the box about the problem.

Lennart Buit07:11:05

I have made enough mistakes programming to know that I want a way to break compat for private libraries


Where would you middle ground it?

Lennart Buit07:11:35

without forking the entire lib and start maintaining two


Oh, I don't think you would maintain two. You'd deprecate one, and the new one with a different name is now where all changes happen.

Lennart Buit07:11:33

well with some form of versioned imports, versioned modules you know


Kinda like how when you go: v1.2 to v2.0 you rarely go back and release a v1.3

Lennart Buit07:11:51

I really want com.blab.blab.blab but preferrably v2.0

Lennart Buit07:11:43

yeah, but if half your apps are still depending on v1.2 you sometimes have to fix bugs in both your 1 and 2 branch

Lennart Buit07:11:24

so you inevidable get 1.2.1 and 2.0.1

Lennart Buit07:11:41

this grows linerally with your breaking changes


Ya, I mean the idea I think is more related to the problem I described. If the namespace is the same, you force people to upgrade. Where as if you change the namespace, new and old can co-exist.

Lennart Buit07:11:01

yeah, Iā€™d suggest solving it on the JVM level, Rich by forking your own libs right ^^


JVM level would be best, but the core JVM team have opposed the community about this quite strongly. Everyone wants it, but they don't want too.


I still recommend watching the talk. It's one of those that had grown on me more and more. At first I was like, what's wrong with SemVer! and slowly I'm thinking, this isn't dumb, maybe we do need something else.

šŸ‘ 4
Lennart Buit07:11:12

I will!

šŸ‘ 4

I checked out read the, Found this example: (local-date "MM/yyyy/dd" "09/2015/28"). I use it like this: (local-date-time "yyyy-mm-dd hh:mm:ss" "2018-02-01 12:11:05"). Don't know why it failed.


If Rich had given his talk years ago, I would have just introduced a new namespace to java.jdbc not ever broken backward compatibility. Hindsight is 20/20.


@stardiviner because mm and MM mean different things.


yyyy-MM-dd hh:mm:ss is probably what you want.


Good old Rich šŸ˜›


If only he had a portal gun to go back in time

Lennart Buit07:11:55

@seancorfield can you link that talk?


@seancorfield I use (local-date-time "yyyy-MM-dd hh:mm:ss" "2018-02-01 12:11:05") still failed. Seems related with this issue


So I have to switch to to clj-time. I have converted date-time string to numbers with:

 ;; TODO: convert a LazySeq into all literal numbers.
  (map #(Integer/parseInt %)
       (str/split "2018-02-01 12:11:05" #"-|:| "))))


But in upper code snippet, I need to flatten a sequcne (2018 2 1 12 11 5) into 018 2 1 12 11 5. Don't know how.


Maybe there is simpler way to convert this date-time string. But don't know how.


I'm just reading through the change log for java.jdbc, which is an interesting trip down memory lane: 0.2.3 -- 2012, June. 0.3.0 Alpha 1 -- 2013, April. Deprecates the old API (but keeps it in the namespace, and just adds the new functions). 0.3.0 Beta 1 -- 2013, November. Breaking change, moves deprecated API functions to new namespace (so user code can just change the require and continue using the old API). 0.5.6 -- 2016, April. Announced that deprecated namespace would go away in 0.6.0. 0.6.0 Alpha 1 -- 2016, April. Removal of deprecated namespace. 0.6.0 (Gold) -- 2016, May. Only the new API remains. I would have considered a four year migration plan to be slow enough. But Rich is right that purely additive change is better.


Is there any way I can run a REPL or basic IDE on Android? I have one but it's laughably old (clojure 1.2 ~ 1.4 ?)


Weird, I have one on 1.7 just called Clojure REPL. But it seems to no longer be on Google Play Store


The app on the Google Play Store may not support the latest versions of Android. The app I have runs clojure 1.7 and still seems to work with android version 9


clojurescript would be fine too if not


@stardiviner Use the parse function in clj-time.format instead of the split/`map` stuff!


@stardiviner (local-date-time "2018-02-01T12:11:05") would work.


(going back to


It's kind of a pain that it expects the T for ISO-8601 but it does work.


Ah, your format needed HH instead of hh:

boot.user=> (jt/local-date-time "yyyy-MM-dd HH:mm:ss" "2018-02-01 12:11:05")
#object[java.time.LocalDateTime 0x40c66b55 "2018-02-01T12:11:05"]


Date/time formatting and parsing is horribly confusing -- blame Java.


(sorry, took me a while to get a REPL fired up on my Windows laptop -- given that WSL is broken in the latest Insider build and all of my Clojure dev stuff is done on Ubuntu via WSL normally)


(and it's bedtime here!)


Ah, looks like they fixed WSL in build 18277 (it was broken in 18272). So I'm rebooting for an updated build. Goodnight!


Anyone using rebel-readline here? I am unable to use doc. It says unable to resolve symbol


Even spelling it out completely doesn't help. (clojure.core/doc +) says No such var: clojure.core/doc


Requiring core doesn't do it either. Same thing with source as well.


user=> (require '[clojure.core :as core])
user=> (core/doc +)
Syntax error compiling at (1:1).
No such var: core/doc
user=> (core/source +)
Syntax error compiling at (1:1).
No such var: core/source

Alex Miller (Clojure team)16:11:47

those functions are in clojure.repl


Oops! I thought clojure.core is the only thing that's loaded and referred into the repl

Alex Miller (Clojure team)16:11:48

most Clojure repls auto-refer a few extra functions like those (and clojure.pprint/pprint)

Alex Miller (Clojure team)16:11:58

which is admittedly confusing


Maybe this is #architecture or #code-reviews but, it seems beginnery: I have a local cache of some xml files that I'll be processing in numerous ways. I wrote a read function to create a map with some metadata gleaned from the file path, and the slurped,'d data. Next I want to do more processing of that content.


When I write functions that do more parsing, what's the good signature? - they should take a file, read them into maps, do more parsing of the content, return an updated map? - they should take the pre-`read` file, i.e., the pre-parsed maps with metadata?


And, how come?


I think I'm getting better at knowing when to have a function that takes an item vs a collection, but I'm still working out writing functions that make for nice re-composable pipelines