Fork me on GitHub
#off-topic
<
2022-02-20
>
Richard Bowen00:02:16

Hey, how difficult do you find updating your applications Java version? Is the process smoothe or do you find a lot of breakage when jumping up versions?

Alex Miller (Clojure team)00:02:59

Java 9 had a number of significant changes from Java 8 - that's probably been the toughest update for most people

Richard Bowen00:02:21

I see, so from 9 to 17 is easy?

souenzzo01:02:05

in my experience: • java8->java9 - few breakages, most solved by bump one library, or add an opt-in dep • java9->java17 - no breakages

souenzzo01:02:34

this report is the same in two distinct 50~100kLOC projects

seancorfield03:02:54

We have some legacy code that still has to run on Java 8 for... reasons... and I will say that 8->9 was the biggest update (our app can't run on 9). All our other code that made it to 9, also made it easily to 10, 11, 12, 13, 14, 15, 16, 17 (which is what we're on in production for everything except that legacy app). We went 8 -> 11 -> 17 for those apps.

jumar05:02:18

Same for us - rarely a problem

genekim04:02:27

I just started using Azul Java 17 on my dev machine, up from Azul Java 11. Performance seems the same for a job i do daily, which I consider a relief. (At first I thought there was a 50% performance degradation. Turns out I accidentally installed an x86 JVM instead of ARM. Whew!)

Richard Bowen01:02:25

Do you know of any platforms that specialize in hosting for Java/Clojure application?

emccue03:02:59

Heroku has had hosting for clojure apps from the start, but that hasn't updated with support for deps.edn stuff quite yet (though there is a workaround)

jumar05:02:39

Many cloud providers have decent support for java apps including aws

Ben Sless05:02:38

@U5NCUG8NR stretch goals for farolero?

Joshua Suskalo07:02:33

I guess I don't understand what the goal would be with something like this. Like clojure's not statically typed, so the pure/impure distinction is less relevant or at least less externally obvious and not visibly viral. Besides the fact that you can just do all this with conditions and arguments, so would it just be like an addon library that has a bunch of effects in it already?

Ben Sless07:02:39

Not necessarily something that has to be built into farolero, but could definitely be built with it. No reason why you couldn't run type inference over Clojure code, either, although the language is dynamically typed

Joshua Suskalo15:02:00

Right, I guess I see that you could do this, and it would be equivalent to the effect system that's being used, but what I was saying is that I don't actually see what there is in Koka that can't be more or less directly translated to farolero code. Like their example on the front page is just

(defn traverse [xs]
  (when (seq xs)
    (far/signal ::yield (first xs))
    (recur (rest xs)))

(defn -main []
  (far/handler-bind [::yield (fn [i] (println "yielded" i))]
    (traverse [1 2 3])))
And their more complex example in the docs which uses ctl instead of fun is this:
(defn traverse [xs]
  (when
      (and (seq xs)
           (far/restart-case (far/signal ::yield (first xs))
             (::far/use-value [v] v)))
    (recur (rest xs))))

(defn print-elems []
  (far/handler-bind [::yield (fn [i]
                               (println "yielded" i)
                               (far/use-value (<= i 2)))]
    (traverse [1 2 3 4])))

Joshua Suskalo15:02:21

Like the only bits here that could really stand to be made more convenient is to make something that's like cerror but instead of binding continue it binds use-value.

Joshua Suskalo15:02:42

And I'm not sure what adding static types/type inference over this gives you.

emilaasa10:02:02

Does anyone have a presentation slide tool to recommend? I normally do most things on paper first (sticky notes on a board), then refine into some markdown text documents and then I take all the markdown and force feed it into Keynote manually. It's the final step I take the most issue with at the moment - is there not a better tool than this? 🙂 I've tried RevealJS which I found usable but pretty complex. Any other ideas?

Ben Sless11:02:20

My presentations are often simple so tools which export to reveal are good for my use case

FlorianK11:02:01

Do you know [Remark](https://remarkjs.com/) ? It's basically a way to make your presentations in markdown, and style them with css. I haven't used it for more than 1 or 2 basic presentations, but so far I like it

respatialized14:02:29

Obligatory mention of http://pitch.io ; their product is really good and they're a Clojure shop

p-himik19:02:45

Suppose we have a table where rows are possible values of variable A and columns are possible values of variable B. Cells are some actions that need to be taken if the variables A and B both have corresponding values. What would be the best way to represent it in code in such a way so that it's immediately obvious what's going on and it's impossible to just miss a particular combination of values? I remember seeing an ASCII table parser, but I do not want to use that. :D

adi20:02:22

Do you mean something like this (from the https://github.com/clojure/core.match/wiki/Basic-usage#matching-literals)?

(let [x true
      y true
      z true]
  (match [x y z]
    [_ false true] 1
    [false true _ ] 2
    [_ _ false] 3
    [_ _ true] 4
    :else 5))

adi20:02:16

Hm... thinking literally literally... If the a/b pairs and corresponding operations are all known in advance, then, what if...

(def ab->op
  {[:a1 :b1] :op-name1
   [:a2 :b2] :op-name2
   [:a3 :b3] :op-name3
   [:a4 :b4] :op-name4
 ;;;
   })

(defmulti exec-op
  (fn [a b]
    (ab->op [a b])))

(defmethod exec-op :op-name1
  [{}]
  ...)
?

p-himik20:02:46

What you describe is an execution strategy, not a representation one. The ab->op map above is missing [:a1 :b2], along with other clauses - that wouldn't be known without actually running the code. My goal is to grasp what's up, without running anything. The perfect scenario would allow even showing it to a non-programmer and making them understand it without delving into Clojure details.

adi20:02:55

> The ab->op map above is missing [:a1 :b2] Oops, sorry about the confusing notation. I meant it to be a total map of all a/b values, (with the assumption that it is a pre-computed table with all mappings known in advance). :op-name would be some human-readable / descriptive name for what to do for a given a/b pair. In my head, such an ab->op table is the human-language explanation. I'm not sure any more what you actually seek.

p-himik20:02:13

I was simply doubting that tables are all there is to it. :) Seems like I was doubting without a good reason. A ab->op map might be exhaustive indeed, but it's a 1D structure representing an inherently 2D entity. It might contain all the information, but it's not obvious. Compare e.g. a table 100x100 in Excel with such a map of 10000 entries.

hiredman20:02:03

We have a place where we don't use our fancy case macro, and instead use a multimethod, and then have a top-level doseq over the state machine states asserting that the multimethods dispatch table contains all of the states

adi20:02:48

> but it's a 1D structure representing an inherently 2D entity Yup, sad limitation. I think it's really a cube though... there are 3 dimensions [a, b, op]. It's a job for an APL :)

adi20:02:24

> a table 100x100 in Excel with such a map of 10000 entries Implicitly I hoped it was something smaller, say 10a x 10b x 1op at most.

p-himik20:02:56

Realistically, it's even smaller. Well, usually. But it's still easy to miss 1 case out of 100. Or keep staring at it for a minute before you find it.

adi21:02:35

> Compare e.g. a table 100x100 in Excel with such a map of 10000 entries. Now I'm having a very bad thought... what if the a/b/op table is stored as a table in a DB and read as configuration?

p-himik21:02:39

A very bad indeed. :D

adi06:02:02

Sigh, yes... "It's just Database" doesn't have quite the same ring as "It's just Data".

lilactown19:02:20

sounds like pattern matching w/ exhaustiveness checking

1
hiredman19:02:59

You can use case on a vector of a and b values

hiredman19:02:48

Case won't ensure you have all the possibilities, but if you get a possibility that is handled it will throw

p-himik20:02:44

Right, thanks. Hmm, maybe it should just be a test...

lilactown20:02:08

maybe a macro + some clever formatting

'(match
  a  (0     1     2     3)
  b
  ((0 true  false false false)
   (1 false true  false false)
   (2 false false true  false)
   (3 false false false true)))

p-himik20:02:58

I can already imagine myself hitting Ctrl+Alt+L and cursing. :D But yeah, it looks nice.

lilactown20:02:45

what does ctrl+alt+L do?

p-himik20:02:09

Reformatting in IDEA-based IDEs.

p-himik20:02:22

It's deep in the muscle memory by now.

lilactown20:02:50

just add some commas instead of spaces then

p-himik20:02:56

(0,,,,,1,,,,,2), got it. :D

delaguardo20:02:35

Or doseq over two lists (a and b) executing multimethod throwing on :default value.

p-himik20:02:33

The main downside is that it won't be obvious what's going on. All the possible values and combinations will be scattered quite a bit.

hiredman20:02:50

Our billing system at work has a big state machine inside it, and from that state machine we have some custom versions of case that do exhaustiveness checking

hiredman20:02:10

We have a lot of predicates defined on states for the machine, and they use the custom case implementations and throw exceptions at macro expansion time if a state isn't handled

p-himik20:02:53

Sounds neat!

p-himik20:02:04

Something like that, yes. But regular match doesn't make it obvious what the domains are and doesn't make it obvious that all the combinations are handled (assuming there's no catch-all :else).

adi20:02:55

> doesn't make it obvious what the domains are and doesn't make it obvious that all the combinations are handled I see. Can this gap be closed by defining a spec? (a https://clojuredocs.org/clojure.spec.alpha/multi-spec perhaps?) ... scratch that. This does not address the "immediately obvious, and exhaustive check" requirement.

Ben Sless20:02:21

separate your data model and execution model Your data is [{:a A :b B :action t} ...] Then just index it conveniently

Ben Sless20:02:48

I'm assuming a and b are inputs

Ben Sless20:02:29

then a map of {a {b t}}should cover efficient execution

quoll20:02:33

My first thought was a map of maps, where the inner maps get generated with a zipmap So, for instance:

(def b-vals ["one" "two" "three"])
(def a-data
 {:first-a  [op-f1 op-f2 op-f3]
  :second-a [op-s1 op-s2 op-s3]
  :third-a  [op-t1 op-t2 op-t3]})

;; messy code to converts the above vector rows into maps
(def op-table (into {} (map (fn [[k v]] [k (zipmap b-values v)]) a-data)))

Ben Sless20:02:11

(map-vals (fn [v] (into {} (map (juxt :b :action)) v)) (group-by :a recs)) ?

Ben Sless20:02:32

that can be generated from all the values of a/b so you're guaranteed to not miss anything

p-himik20:02:21

I don't think I follow that at all. How can it be generated given that a map of {:a A, :b B, :action t} is something that a user has to write, because the action t is not magically known? So far, the solutions by @lilactown and @quoll seem to be the most visually obvious - because a conceptual table is still represented as a visual table. Also, found this, which is quite similar: https://github.com/semperos/rankle/blob/master/src/com/semperos/rankle/util.clj#L90-L120 My initial assertion was that I might be missing something in terms of representation - maybe tables aren't the only choice when it comes to clarity.

adi20:02:16

Hm... thinking literally literally... If the a/b pairs and corresponding operations are all known in advance, then, what if...

(def ab->op
  {[:a1 :b1] :op-name1
   [:a2 :b2] :op-name2
   [:a3 :b3] :op-name3
   [:a4 :b4] :op-name4
 ;;;
   })

(defmulti exec-op
  (fn [a b]
    (ab->op [a b])))

(defmethod exec-op :op-name1
  [{}]
  ...)
?