Fork me on GitHub
#clojure
<
2018-07-22
>
emccue02:07:34

is there an up to date guide for clojure.java.jdbc?

emccue02:07:44

I just got bit by some old example code

emccue02:07:06

(for someone who has never used jdbc and just wants a sqlite db)

seancorfield02:07:28

@emccue The repo links to the official community docs

seancorfield02:07:28

There's a #sql channel if you need more JDBC-focused help rather than Clojure-focused help.

seancorfield02:07:11

Since I maintain clojure.java.jdbc (and honeysql now as well), I'm happy to help answer any questions you may have...

didibus05:07:25

@roklenarcic Just do (meta *ns*)

didibus05:07:49

So, I guess there's no way to have your return spec take into account the arity that was called? Say I have a function whose 1-ary returns an int?, but 2-ary returns a string?, and say my 2-ary has a bug which will return int? sometimes, any way I can spec it so check will find the bug?

Alex Miller (Clojure team)06:07:41

The :fn spec can be used to check any relationship between args and ret

didibus06:07:57

@alexmiller Oh, I see, so you're saying to use the fn spec to validate that the correct arity returns the correct value. Make sense. I guess fn can supersede ret in that case. Thanks

fantomofdoom09:07:24

Hi, anyone know best practice to avoid side-effect? (like example write data on DB or change atom state)

val_waeselynck09:07:28

Well, some side effects will be part of the essential requirements of your application, so you can't really avoid those. For the rest, write as much of your logic as possible as pure functions, accepting and returning immutable data.

fantomofdoom10:07:29

I understand that the side effect is a part of programm. But my point in question is how righting designing app to maximize pure fn. (in example: fn -> fn -> fn -> write DB, all fn's is unpure)

val_waeselynck10:07:07

When practical, what you can do is represent the writes as a data structure; this way your application is pure as it only has to emit data, and the side effects are performed by generic 'infrastructure' code. Datomic is very good at enabling this sort of an architecture.

val_waeselynck10:07:29

This is often called "pushing side-effects at the edge of the system".

didibus19:07:46

Its not so much avoiding, as much as its about isolating the side effects to known places, and maximizing the amount of code that is pure, so its easier tor reason about, test and reuse.

didibus19:07:04

My advice is to try and add orchestration to your code.

didibus19:07:56

What I mean is normally people write code in a style like this: (fn[a] (b)) (fn[b] (side-effect) (c)) (fn[d] (e)) etc.

didibus19:07:40

What you want instead is pull out the logic of what to do after each step into a top level orchestration function.

didibus19:07:31

So you'd get: (fn[] (-> (a) (b) (side-effect) (c) (d) ...))

didibus19:07:58

And now a, b, side-effect, c, d, etc. simply take argument and return result, they don't forward to the next step, that's been removed. side-effect performs side-effect, but similarly, it doesn't forward anywhere after, it just returns info about its execution.

didibus19:07:53

If you do this to the extreme, your top level function would be the place where all side-effect are called and made from. This is essentially what the IO Monad is Haskell does under the hood. In Clojure, having a few layers of orchestration is generally more practical, but still try to limit the depth of it, favor breadth over depth.

didibus19:07:40

In that style, it becomes much easier to see the flow of code at a glance, by peeking at the top levels. And functions can be tested in isolation, and also be reused, because you can easily have another orchestration that uses them in a different order for example.

jumar06:07:24

@U06GS6P1N @U0K064KQV great answers! Do you know any open-source projects that could serve as good examples of the suggested approach

jumar07:07:12

Yes, that could serve the purpose. Anything on the backend side?

val_waeselynck07:07:02

@U06BE1L6T I don't have anything better than Datomic to suggest šŸ™‚

jumar07:07:49

Hmm. I was thinking more about real apps built using these technologies/approaches, but thanks anyway!

jumar07:07:19

My "problem" is that I often hear to speak about this kind of approach but rarely see it applied in practice

val_waeselynck07:07:11

@U06BE1L6T I think the story is a bit different in the backend, where there's usually no in-program state to keep. I confess that on the backend, I don't necessarily refrain from performing side-effects in the middle of request processing - I just push the side-effects towards the edge until my code feels testable enough, but I don't push them all the way towards the edges.

šŸ‘ 4
val_waeselynck08:07:52

The one place where I apply this systematically is in my Graphql-ish writes - those generated pure data that gets composed into a Datomic transaction, and optionally some side-effectful callbacks that get called after the transaction succeeds (sending emails etc.)

val_waeselynck08:07:07

can't show you any code unfortunately - all proprietary

beoliver11:07:10

I am writing a wrapper lib for a java api - I am trying to create a nice way of handling object creation.

(defmacro kv-calls [class m]
  (let [xs# (map (fn [[k arg]]
                   `(. ~(symbol (name k)) ~@(flatten (list arg)))) m)]
    `(-> (new ~class) ~@xs#)))
and then a call would look like:
(macroexpand '(kv-calls CollectionCreateOptions {:journalSize 20 :waitForSync [true 100]}))
(. (. (new CollectionCreateOptions) journalSize 20) waitForSync true 100)
(defn ^SomeClass map->SomeClass
  [options]
  (kv-calls SomeClass options))
but I get Don't know how to create ISeq from: clojure.lang.Symbol when loading the ns, as I guess it is trying to expand the symbol options - am I missing a tilda somewhere?

beoliver11:07:22

is this approach something that can only work at compile time? i.e the map needs to be present...

beoliver12:07:22

solved my own problem - might not be best practice, but it works...

(defn fn-builder [[k v]]
  (let [method-name (symbol (name k))
        args (flatten (list v))]
    `(fn [x#] (. x# ~method-name ~@args))))

(defn option-builder [object options]
  (reduce (fn [acc fn]
            ((eval fn) acc)) object (map fn-builder options)))

genmeblog18:07:13

during the compilation options is just a symbol without any meaning. For me this case is perfectly solvable with a function call

noisesmith19:07:45

an innner class is just a class with a funny name

noisesmith19:07:08

what's the inner class?

genmeblog19:07:28

(mainclass$innerclass.)

didibus19:07:38

Should just be (OuterClass$InnerClass.)

noisesmith19:07:19

if you want STYLE_DISPLAY that's likely an enum or static field and not an inner class

noisesmith19:07:29

TexConstants/STYLE_DISPLAY

noisesmith19:07:40

clojure makes things distinct that share syntaxes in java

stathissideris19:07:10

Iā€™m referring to new TeXIconBuilder()

stathissideris19:07:30

which has the funny formula. preceding it

stathissideris19:07:09

so I guess I should write something like (org.scilab.forge.jlatexmath.TeXFormula$TeXIconBuilder. f)

noisesmith19:07:32

you can import it (import (org.scilab.forge.jlatexmath TexFormula$TexIconBuilder))

noisesmith19:07:22

it's not really inside the other class, so you need the full funny looking name, and it isn't actually brought into scope in any way by importing the parent

stathissideris19:07:06

@noisesmith thank you, I will attempt this

underplank21:07:44

Hi all. Im getting a compiler error

CompilerException java.lang.IllegalAccessError: length-of-longest-key is not public, compiling:(sketch/core.clj:1:1)

underplank21:07:58

From a super simple file

underplank21:07:22

I think it actually happens when I do this

(ns sketch.dynamic
  (:require [quil.core :refer :all])
  ;; (:use [incanter.core :only [$=]])
  ;; (:use [clojure.math.combinatorics :only [combinations cartesian-product]])
  ;; (:use [clojure.pprint])
  ;; (:use [clojure.set :only [union]])
  ;; (:use [clojure.contrib.map-utils :only [deep-merge-with]])
  ;; (:import [org.apache.commons.math3.distribution ParetoDistribution])
  ;; (:import [processing.core PShape PGraphics])
  )

;; (defn setup []
;;   )


;; (defn draw []
;;   (no-loop)

;;   (color-mode :hsb 360 100 100 1.0)

;;   (background 220 200 66)
;;   (rect 100 100 400 400)
;;   (save "sketch.tif"))

underplank21:07:48

(ns sketch.core
  (:require [quil.core :as q])
  (:require [sketch.dynamic :as dynamic])
  (:gen-class))

(q/defsketch example
             :title "Sketch"
             :setup dynamic/setup
             :draw dynamic/draw
             :size [900 900])

(defn refresh []
  (use :reload 'sketch.dynamic)
  (.loop example))

underplank21:07:05

it seems like its something to do with quil?

underplank21:07:12

I get the same error in either core.clj or dynamic.clj

waffletower21:07:05

I'm interested in introspection on closures returned by functions. I found that I could do this to get the namespaced symbol of a function that returned a closure:

(defn parent-symbol
  "somewhat hacky method to get namespaced symbol for a function.
  can find the parent of closures returned by functions"
  [f]
  (let [[ns nm]
        (->> f
             str
             clojure.repl/demunge
             (re-find #"^(.+)/(.+)/.*$")
             rest)]
    (symbol ns nm)))

waffletower21:07:24

Is there a better way to achieve such?

waffletower21:07:03

Are there other public functions like clojure.repl/demunge that can be used to parse function string representations?

waffletower21:07:29

For example:

(defn returner [x]
        (fn [a] (+ x a)))
#'user/returner
(parent-symbol (returner 7))
user/returner

seancorfield21:07:46

@underplank Your code works for me with quil 2.7.1 -- what version are you using?

seancorfield21:07:15

If you're using Leiningen, perhaps try lein clean to see if you have some outdated class files lying around...

underplank21:07:32

Im using quil 2.7.1 as well.. it seems to be working. And I did do lein clean, which maybe why its fixed itself.

underplank21:07:40

thanks for checking!

seancorfield21:07:04

lein clean cures all manner of woes šŸ™‚

noisesmith21:07:43

@waffletower

user=> (parent-symbol ((fn foo [] (fn bar [] 1))))
user/eval159/foo--160

waffletower21:07:49

I hadn't tried the nested case šŸ˜

waffletower21:07:04

Need a better regex

noisesmith21:07:55

it's the same thing, just using an anonymous function at the outside instead of a defn

waffletower21:07:30

ya I read it incorrectly, I would need handling for named lambdas

noisesmith21:07:56

this is what it does for unnamed

user=> (parent-symbol ((fn [] (fn bar [] 1))))
user/eval165/fn--166

waffletower21:07:29

it behaves differently when the provider doesn't have a var

waffletower21:07:15

(defn returner [x]
        (fn luck [a] (+ x a)))

waffletower21:07:26

(parent-symbol (returner 7))
user/returner

waffletower21:07:33

So named lambdas are ok

waffletower21:07:52

I am interested in getting at the var, which I have only been able to do with eval:

(defn parent-var [f]
  (eval (list 'var (parent-symbol f))))

waffletower21:07:18

(parent-var (returner 7))
#'user/returner

noisesmith21:07:37

this is really pushing things

:user=> (reify Object (toString [this] "Bob") clojure.lang.IFn (invoke [this] (fn[])))
#object[user$eval217$reify__218 0x2c7b5824 "Bob"]
:user=> (parent-symbol (reify Object (toString [this] "Bob") clojure.lang.IFn (invoke [this] (fn[]))))
NullPointerException   java.io.Writer.write (Writer.java:157)

waffletower21:07:06

you are good at finding its shortcomings lol