Fork me on GitHub
#clojure
<
2017-05-18
>
josh.freckleton01:05:46

I have a sorted list of things, say: [4, 9, 100, 327] and I want to fill in all the gaps in the range [0,1000] with 1s, so, [1,1,1,4,1,1,1,1,9,...] (the sorted "things" are actually records, sorted by one key) the collection will be quite big... I built an awkward loop for it, but... whats an elegant functional way to do that?

danielcompton01:05:36

Maybe create a vector of 1000 1's, then update the keys for the ones that you have?

josh.freckleton01:05:23

the 1000 is more like 1-10M, and you mean update it at it's index? That sounds slow, no?

danielcompton01:05:19

Seems the same as the loop method though?

josh.freckleton01:05:22

(oh also, i don't have the index numbers)

josh.freckleton01:05:50

my loop method was to create a range of the keys and then walk the keys at the same time as walking my "populated" records

josh.freckleton01:05:03

seems pretty imperative

danielcompton01:05:14

Can you restate the actual problem with records? I don't really understand it

josh.freckleton01:05:05

sure, thanks, one sec

josh.freckleton01:05:34

so a generic "empty" record, no more than a date, fills in when a date isn't present for a thing

josh.freckleton01:05:02

dates can be joda times, and I work with them through clj-time

josh.freckleton01:05:15

they can also be present many times

john01:05:15

not the prettiest, but you get the idea:

(defn distribute [r]
  (flatten 
    [(take (first r) (cycle [0]))
     (map 
       (fn [x y] [x y]) 
       r 
       (map 
         #(take % (cycle [0])) 
          (map #(* -1 %) 
            (map #(apply - %) 
              (partition 2 1 r)))))]))

john01:05:36

there's probably something more idiomatic than the cycle jazz

john01:05:18

oh, you wanted 1s. you get the idea though

josh.freckleton01:05:51

sure, I'm looking that over now, I like it

josh.freckleton01:05:01

prob use repeat instead of cycle

john01:05:37

ah, right. I always confuse that with needing a fn with repeatedly

john01:05:55

should probably decompose those into transducers too

josh.freckleton01:05:57

constantly, repeat, repeatedly

josh.freckleton01:05:01

they always throw me off 🙂

john01:05:37

Getting rid of the flatten would be ideal too

josh.freckleton01:05:04

ya, the partition is what stood out to me, I think I can do something with that

josh.freckleton01:05:17

and adapt the rest

josh.freckleton01:05:27

thanks for helping me see this problem thru another lens!

josh.freckleton01:05:27

can partition-by take advantage of a collection being sorted?

john01:05:28

lol, I'm using lumo too, so the (* -1 %) is "cross platform" 😉

john01:05:01

mmm, with respect to the method I showed, I think all you want is the diff between them, right? Not sure where you're going with partition-by

john01:05:29

uh, actually, looking at the output, I don't think my function worked...

john01:05:43

dropped the last number I think

john01:05:52

somethings going on there

josh.freckleton01:05:24

oh it's fine, my use case was a bit different than the example I originally gave i think I lost some detail in distilling it to that problem but your answer helped

noisesmith01:05:45

@josh.freckleton you could save a lot of space overhead by making a deftype that uses an underlying collection, or fills in a default if not found

noisesmith01:05:13

you'd just need to make sure you implement the right protocols for seq access and associative lookup

danielcompton01:05:17

yeah I was thinking about representing missing ones that way

danielcompton01:05:24

or just via nil punning

noisesmith01:05:37

well, get does take an optional not-found arg

noisesmith01:05:47

so you could just use get to implement the stuff at the bottom

danielcompton01:05:48

yeah that kind of thing

attentive02:05:20

Hey gang, can someone explain why, for example, this is the case? I'm a bit lost in the semantics of it.

(apply '/ '(16 8))
; => 8

noisesmith02:05:50

@attentive symbols when used as functions attempt to look themselves up

noisesmith02:05:34

turns out '/ was not something you could look up in your list (or any list, incidentally) so it returns nil

noisesmith02:05:17

err, no, sorry - thanks to apply it tries to look up '/ in 16, and 8 is the default (not found) argument to get

attentive02:05:16

Ah, right—the symbol acts as a keyword-style map lookup, but in a non-map, and then returns the second (default) arg?

noisesmith02:05:24

right, exactly

attentive02:05:28

No wonder I was confused, that is bizarre

noisesmith02:05:33

it is very strange

noisesmith02:05:01

the most evil version of this: ('or true :not-found)

noisesmith02:05:15

because sometimes your test case makes it look like it's working...

noisesmith02:05:56

my favorite example of how weird get is:

noisesmith02:05:51

oops, missed one, but you get the idea

attentive02:05:43

I feel like the get-alike behaviour should check for an IPersistentMap interface or something similar, but I guess it would degrade performance?

noisesmith02:05:52

I have no idea

plins03:05:41

hello everyone, i know jdbc is synchronous, but is anyone aware of somekind of thread pool which can be consumed by an async api? (a dedicated threadpool for database access)

mbjarland07:05:47

another noob question, any concise way of splitting a vector at a given set of indicies?

cjmurphy07:05:55

For multiples splits (indicies) you could reduce over or use loop recur recursion.

mbjarland07:05:40

@cjmurphy thanks, yeah I was going down the recur path, was hoping for something built in, but that works

qqq08:05:01

is transit/read safe on malicious input?

john16:05:39

qqq: my understanding is that transit uses EDN, which is a non-executable format.

qqq08:05:18

I have a webserver. I want to use transit/read

qqq08:05:25

However, the user can craft whatever input he/she wants.

qqq08:05:28

Is transit/read safe ?

cmal08:05:40

Hi! I want to do some changes on the figwheel-sidecar project and push my version to the clojars. But when deploying, it tells me that I don't have access to the 'figwheel-sidecar' group. How can I successfully deploy it so that I can use it for myself? Thank you!

slipset08:05:54

So, I guess you’d use a file:// repo and deploy to there.

lxsameer08:05:10

hey folks, how can I get the current leiningen profile ?

chrisetheridge08:05:15

@cmal i found this site yesterday: https://jitpack.io/ (not sure how well it works). but allows you to deploy jars straight from your GitHub

stain09:05:00

@cmal you would have to change the project.clj to use your own namespace, e.g. cmal/figwheel-sidecar (good practice is to match your github username aka the whole thing matches where your fork is in github)

matan09:05:10

A small problem, with laziness and doall:

matan09:05:38

The idea was to avoid huge collections dumped to stdout in error messages (more idiomatic ways or library tools welcome). The issue is that the collection doesn't get realized with this, so only an object name is printed to stdout.

matan09:05:56

After some troubleshooting, I am quite in the dark.

leonoel09:05:07

doall realizes a collection

leonoel09:05:45

here you pass it a string, which is an eager collection so doall is effectively a no-op

matan09:05:52

I tried doall in front of all places I could think of, getting the same result.

matan09:05:40

what is the full set of places where a doall wrapper would work for this example? I have probably not mastered this aspect as of yet, so thanks for your help!

mpenet09:05:15

well as soon as you do (count coll) it will likely realize it anyway, so it's seems flawed in many ways

mpenet09:05:44

(doall s) on a String instance doesn't make much sense too

matan09:05:59

@mpenet I am not sure how count relates here

mpenet09:05:23

the goal of the whole function seems not to have to realize the whole coll in logging (I think)

matan09:05:00

or at least not to dump its entirety to the log (the latter more than the former)

leonoel10:05:21

you really don't need doall in that case, because str will realize the seq

ilevd10:05:02

How can I write this http://stackoverflow.com/a/14987992 on Clojure? .class expression?

borkdude10:05:15

@ilevd (.getDeclaredFields Charset)

borkdude10:05:49

@ilevd A class name gets resolved to the class object in Clojure

borkdude10:05:47

@ilevd So:

(def cs (.getDeclaredField Charset "defaultCharset"))
(.setAccessible cs true)

ilevd10:05:03

@borkdude Hmm, I'm trying with doto

(doto (.getDeclaredField Charset "defaultCharset") (.setAccesible true) (.set nil nil))
and it's not working

borkdude10:05:47

You have a typo

ilevd10:05:11

Yes, thanks

cmal11:05:51

@biscuitpants @stain Ahh... Thank you! I'll try those.

cmdrdats11:05:38

does anyone know if there exists some code to optimistically convert SQL queries to honeysql?

tatut11:05:21

cmdrdats: a sort of interpreter for SQL?

cmdrdats11:05:53

ye - really just want a shortcut to mostly get the sql translates to honeysql

cmdrdats11:05:08

so that I don't have to hand-rewrite from scratch. It's somewhat painful

tatut11:05:42

sorry, don’t know of such a tool… but you can always mix&match honeysql with hugsql/yesql/jeesql type of libraries

tatut11:05:49

so you can use your hand written SQL

cmdrdats11:05:03

I've got it currently working as sql in the system just fine, but want to change generally to honeysql, because we just like it better - so looking for a tool to ease the process a bit

tcrawley11:05:44

@cmal the preferred method for forks is to use the org.clojars.your-username group - see https://github.com/clojars/clojars-web/wiki/Groups#personal-groups

cmal11:05:52

@tcrawley Thank you! It works!

cmal11:05:17

How can the figwheel-sidecar project be running on my own machine with my Emacs so I can debug into it? I tried to cider-jack-in but how to let the clojurescript project depends on it and let Emacs run the local version?

chrisetheridge12:05:44

@cmal do lein install in that project folder. make sure to bump the version though, in both your project and the project.clj. your lein will then pickup this local jar, and use it instead

cmal12:05:55

@biscuitpants Should I run lein install in the figwheel-sidecar project, or the project I want to use my version of figwheel-sidecar?

chrisetheridge12:05:07

in your fighweel-sidecar project

chrisetheridge12:05:16

and then your other project will use your local one

cmal12:05:40

So lein install just replace the figwheel-sidecar jar used by my project, so my project will use this jar generated by lein install?

chrisetheridge12:05:56

no, lein install will install that jar locally, so another project can use it. once it is installed, running lein repl in another project will retrieve that dependency (your custom sidecar, for instance) and use the local version

chrisetheridge12:05:00

you can also use checkouts, if you want

cmal13:05:04

Thank you very much! @biscuitpants But how can I run a repl and debug in my version of figwheel-sidecar?

chrisetheridge13:05:37

you can run the repl in a project that uses your custom version of sidecar

chrisetheridge13:05:01

checkouts may actually be better, if you’re making a few systematic changes. that way you don’t need to restart the repl each time you want to try out your new version

cmal13:05:57

Thank you! I'll read the docs about this and try it out!

chrisetheridge13:05:05

sure 🙂 shout if you get stuck!

chrisetheridge13:05:11

what are you trying to change / fix, if i may ask?

cmal13:05:01

Ok, I'll shout loudly! I want to add a proxy to lein-figwheel to let me get access to a remote API. Otherwise it will give me a CORS error note.

Tim13:05:22

how come this doesn’t work: (def (symbol "f") 12)

CompilerException java.lang.RuntimeException: First argument to def must be a Symbol, compiling:(/private/var/folders/7x/bdhllf3x3hj6rq19xn1rbh000000gn/T/form-init4103840200690063741.clj:1:1) 

bronsa13:05:04

def is a special form, the first argument is not evalauted

genmeblog14:05:49

@tmtwd or use macro

user=> (defmacro make-var [name val]
  #_=> (let [s (symbol name)]
  #_=> `(def ~s ~val)))
#'user/make-var
user=> (make-var "aaa" 444)
#'user/aaa
user=> aaa
444

Tim14:05:39

excellent, thank you all

Tim14:05:45

very informative

genmeblog14:05:08

of course you have to (somehow) put for inside macro

genmeblog15:05:47

(defmacro make-vars
  ""
  [names val]
  `(do
     ~@(for [x names]
         (let [s (symbol x)]
           `(def ~s ~val)))))

yedi15:05:57

how do i remove a namespace from a keyword?

zylox15:05:13

programmatically or refactoring

zylox15:05:15

(-> :hi/there name keyword)
=> :there

zylox15:05:33

might be a quicker way though

metametadata17:05:44

I'm wondering is there any shorthand for (doall (for [x y] (side-effect-and-return-value x))?

tanzoniteblack17:05:58

metametadata: just to make sure, you want to run over the entire collection at once, but you also want keep the return values?

tanzoniteblack17:05:25

I generally just do (mapv side-effect-and-return-value y); mapv is identical to map, but returns a vector and is non-lazy

tanzoniteblack17:05:50

if you don’t care about the results, then you can do (run! side-effect-and-return-value y)

metametadata17:05:16

yes, I do want to gather all the return values after doing all side-effects at once

metametadata17:05:16

ok, I think I could use mapv for simple cases

metametadata17:05:31

but it's not symmetrical as soon as there's :when in for used 🙂

tanzoniteblack17:05:36

correct, there’s no direct equivalent to for that’s not lazy. Your options tend to be to use doall, mapv, into, loop/recur, run!, or reduce. All of them are good choices depending on your use case 🙂 If you like for with :when, then doall or (into [] ...) are probably what you’re looking at

metametadata11:05:09

Thank you for the elaborate answer! I've also found the advice to use transducers: https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects

bcbradley19:05:26

what could cause clojure to not be able to see a static field?

bcbradley19:05:19

I've got something that looks like this

(def keyword->joystick-hat
  {:centered GLFW/GLFW_HAT_CENTERED
   :up GLFW/GLFW_HAT_UP
   :right GLFW/GLFW_HAT_RIGHT
   :down GLFW/GLFW_HAT_DOWN
   :left GLFW/GLFW_HAT_LEFT
   :right-up GLFW/GLFW_HAT_RIGHT_UP
   :right-down GLFW/GLFW_HAT_RIGHT_DOWN
   :left-up GLFW/GLFW_HAT_LEFT_UP
   :left-down GLFW/GLFW_HAT_LEFT_DOWN})
(def joystick-hat->keyword (map-invert keyword->joystick-hat))
with this in ns macro:
(ns glfw.enumerations
  (:require [clojure.set :refer [map-invert]])
  (:import org.lwjgl.glfw.GLFW))
with this as project.clj:
(defproject vulkan-hello-world "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.lwjgl/lwjgl "3.1.1"]
                 [org.lwjgl/lwjgl "3.1.1" :classifier "natives-linux" :native-prefix ""]
                 [org.lwjgl/lwjgl "3.1.1" :classifier "natives-macos" :native-prefix ""]
                 [org.lwjgl/lwjgl "3.1.1" :classifier "natives-windows" :native-prefix ""]
                 [org.lwjgl/lwjgl-glfw "3.1.1"]
                 [org.lwjgl/lwjgl-glfw "3.1.1" :classifier "natives-linux" :natives-prefix ""]
                 [org.lwjgl/lwjgl-glfw "3.1.1" :classifier "natives-macos" :natives-prefix ""]
                 [org.lwjgl/lwjgl-glfw "3.1.1" :classifier "natives-windows" :natives-prefix ""]]
  :main ^:skip-aot vulkan-hello-world.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})
(my src folder has two folders in it, vulkan-hello-world and glfw) the core app just has this in it right now:
(ns vulkan-hello-world.core
  (:require [glfw.functions :as glfw])
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  ; (when (glfw/init)
  ;   (when-let [window (glfw/create-window 640 480 "Hello World" 0 0)]
  ;     (glfw/make-context-current window)
  ;     (loop []
  ;       (when (not (glfw/window-should-close window))
  ;         (glfw/swap-buffers window)
  ;         (glfw/poll-events)
  ;         (recur)))
  ;     (glfw/terminate))))
  (println "done"))
on lein run i get the error Exception in threat "main" java.lang.RuntimeException: Unable to find static field: GLFW_HAT_DOWN in class org.lwjgl.glfw.GLFW, compiling:(glfw/enumerations.clj:22:1) I find that very odd, since the static field is defined here: https://javadoc.lwjgl.org/org/lwjgl/glfw/GLFW.html, and why isn't it complaining about the GLFW/GLFW_HAT_CENTERED or any of the others? 😱

hiredman19:05:10

well, those java docs are for a specific version of the code, does it match the code you are trying to run against?

bcbradley19:05:47

hrm, thats a great point let me bump the version

bcbradley19:05:31

yeah that was it thanks!

bcbradley19:05:35

gosh i beat my head over that

mobileink21:05:37

are we back online?

lxsameer21:05:39

hey guys, what do you use for database migration ?

mobileink22:05:50

wild turkey 101, usually.

bcbradley22:05:34

do you see anything wrong with this?

(defn create-window ^long [width height title monitor share]
  (let [WIDTH (int width)
        HEIGHT (int height)
        TITLE (str->ByteBuffer title)
        MONITOR (long monitor)
        SHARE (long share)
        RESULT (GLFW/glfwCreateWindow WIDTH HEIGHT TITLE MONITOR SHARE)]
    (free TITLE)
    RESULT))
The error i'm getting is: Exception in thread "main" java.lang.IllegalArgumentException: fns taking primitives support only 4 or fewer args, compiling:(glfw/functions.clj:22:1)

noisesmith22:05:46

@bcbradley what if you move the ^long annotation so it would be on the function name and not the arg list?

noisesmith22:05:03

or just removed it…

bcbradley22:05:26

how would i annotate the return value?

noisesmith22:05:56

why are you annotating it?

bcbradley22:05:14

because i can (or should)

bcbradley22:05:18

what is causing this problem?

noisesmith22:05:32

you can’t annotate a function to take primitives and take 5 args

mobileink22:05:38

@bcbradley i don't see anything wrong with the syntax. the message suggests that glfw places constraints you have violated.

noisesmith22:05:45

you aren’t annotating the return value, you are hinting the args

noisesmith22:05:57

@mobileink no, it is a clojure compiler restriction on args

mobileink22:05:40

noisesmith: they're not kidding when they say clojure error msgs suck.

noisesmith22:05:12

yeah - but I think spec will … eventually … help with that

mobileink22:05:25

noisesmith: let's hope so. i'm thinking spec is the killer app.

bcbradley22:05:13

how am i hinting the arguments?

bcbradley22:05:20

i thought thats where the type hint goes for the ret val

noisesmith22:05:32

@bcbradley maybe I’m misinterpreting - but the error message will go away if you remove the type hint

bcbradley22:05:39

yeah i'm aware of that

bcbradley22:05:47

but thats kind of like saying the bridge will not burn down if we destroy it

noisesmith22:05:06

and you shouldn’t just randomly hint things - hint if there’s a performance issue

noisesmith22:05:15

(or correctness issue that can only be fixed with hinting)

noisesmith22:05:30

otherwise hints just add noise (and as here, break things)

bcbradley22:05:36

thats kind of like saying that we shouldn't build bridges if we can build a dam on every river

bcbradley22:05:53

what is causing this problem though?

noisesmith22:05:53

no, hints shouldn’t be used in the general case

bcbradley22:05:10

i realize clojure is a dynamically typed language and you should only optimize with hints to the compiler when you have a performance issue

bcbradley22:05:23

what i don't understand is how a "hint" can make the compiler give up compiling

bcbradley22:05:31

seems like a design flaw

bcbradley22:05:54

does anyone know WHY the compiler freaks out when you give it a hint in this case?

noisesmith22:05:55

hint it Long instead of long

dpsutton22:05:27

so it's an incorrect constraint that runs into a proper rule?

noisesmith22:05:42

@bcbradley my hunch is that only certain arity counts are supported to return primitives due to how IFn is defined

noisesmith22:05:11

because of the way IFn works, clojure.core has to declare (in java code) every arity and return type separately

bcbradley22:05:19

combinatorial explosion

noisesmith22:05:43

they stopped at an arbitrary arg count

bcbradley22:05:47

i would prefer if the compiler just ignored my hint in this case, not freak out and give up

noisesmith22:05:53

another fix would be to put a bunch of args in a list / vector

bcbradley22:05:03

because who knows, someone might someday figure out a way to solve this issue by redefining Ifn or something

noisesmith22:05:11

@bcbradley but then you think you are getting an optimization that is silently not provided

bcbradley22:05:21

doesn't ahve to be silent

bcbradley22:05:26

give me a warning

noisesmith22:05:33

I think it’s better to explicitly say “I can’t actually optimize this” - this is why we have recur instead of implicit self-call tail optimization also

noisesmith22:05:09

nothing would have prevented implicit self-call optimization, but requiring that you ask explicitly and erroring if it wouldn’t work is better

bcbradley22:05:05

the way it is now, i'll have to remove all primitive hints on return values for functions that have more than 4 arguments because of the way IFn is defined. If the compiler were to instead just ignore my hint and give me a warning telling me that the hint i provided cannot be used, then i would not have to touch my codebase-- it would also mean that in the future if IFn is ever redefined or some sort of changes elsewhere are made to circumvent the combinatorial explosion aspect that essentially prevents us from having primitive return values with arbitrary arity functions, then i again don't have to touch my code base because the hints I provided are still there-- only now the compiler will use them and has no need to warn me

bcbradley22:05:37

because the compiler freaks out instead of giving me a warning, I have to remove the hints now, and I would have to add them again in the future if these issues were ever addressed

bcbradley22:05:42

that sounds like a poor design choice to me

noisesmith22:05:58

or you could wrap up some of your args in a collection to keep the arg count shorter

noisesmith22:05:10

if you put them in eg. an int-array it should still perform well

dpsutton22:05:19

wow. now i understand a bit of how much would be involved in adding more primitive types

bcbradley22:05:50

that sort of defeats the point though

bcbradley22:05:50

its not a terribly big issue its just annoying to have to go through 7 thousand lines of code and remove type hints on return values

bcbradley22:05:58

a hint should be just that-- a hint

bcbradley22:05:01

not a contract

noisesmith22:05:40

I agree that type hints are complected with optimization promises that actually change how code is compiled and that’s a bit weird

dpsutton22:05:02

that makes me think that the name "hint" is deficient rather than the implementation

noisesmith22:05:12

@bcbradley my response to the situation is to avoid primitive type hints in particular, due to the various weird ways they can break code

noisesmith22:05:27

and to avoid type hinting at all unless I can prove I need it via profiling

bcbradley22:05:39

i'm not able to profile my code

bcbradley22:05:53

i'm building a library that wraps glfw and vulkan through lwjgl

bcbradley22:05:07

i don't know what the performance profile will be because i don't know how it will be used

bcbradley22:05:21

the best thing i can do is make it as lean as i can and help the compiler as much as i can

noisesmith22:05:28

write it in java

bcbradley22:05:43

doesn't make a lot of sense to do that becaues it is already written in java

bcbradley22:05:47

thats what lwjgl is

noisesmith22:05:53

seriously - if your top priority is that things be lean, doing htat in java is much less of a headache

noisesmith22:05:03

then maybe your lib doesn’t need to exist?

bcbradley22:05:22

the lwjgl is just a thin wrapper over the native c libraries

bcbradley22:05:29

for instance, there is no automatic garbage collection

bcbradley22:05:35

so you have to free everything manually

noisesmith22:05:42

OK - and if the domain calls for perf, wrapping it just-because seems an odd choice

bcbradley22:05:43

the library i'm making handles that aspect of it for you

bcbradley22:05:59

so my library isn't going to be "just java in clojure"

bcbradley22:05:08

that i agree would be idiotic

noisesmith22:05:23

well - keeping the clojure lean is going to be a pain in the ass, especially if you have no reasonable way to profile

noisesmith22:05:33

I’ll leave it at that, and you can decide if it’s worth it

bcbradley22:05:50

i really want to make games in clojure

bcbradley22:05:59

i just want to do it my way, so i guess i'll just solider on

bcbradley22:05:01

hope for the best

noisesmith22:05:33

have you looked at the kind of stuff the arcadia people have had to do to use clojure? they built their own assembler

noisesmith22:05:50

I mean - it’s some people’s idea of fun, and that’s cool

mobileink22:05:17

fwiw nothing wrong with "just java in clojure".

noisesmith22:05:35

except it’s a lot easier to just write the java, especially if you want to use unboxed primitives

noisesmith22:05:46

I’ve done both

mobileink22:05:00

i'll take a sane clj wrapper over a goofy java api any day.

bcbradley22:05:11

the context was making a clojure library that just isomorphically maps to a java library is dumb

noisesmith22:05:11

how about a sane java api?

bcbradley22:05:33

if the clojure library doesn't actually do anything, if it just calls java methods-- thats not very useful

mobileink22:05:39

i've heard of that. 😉

bcbradley22:05:03

i mean thats what the . is for

noisesmith22:05:15

@mobileink if youre task is “make a wrapper that is sane to use from clojure” and your top priority is performance and making lean code, think about clojure usability while writing it in java

noisesmith22:05:27

that’s the easier way to do it

noisesmith22:05:22

of course if performance is a lower priority, yeah, clojure is a great way to make a saner api

mobileink22:05:25

welk to be honest i'm usually more interested in conceptual clarity. some java apis have some don't.

mobileink22:05:53

but you cannot beat clojure for conceptual clarity.

mobileink22:05:41

and i'll admit that performance is usually the last thing i think of. you can always optimize, later.

mobileink22:05:22

out of curiosity: how often do you come across a java api that is unclunky, such that a wrapper would be a straight translation?

bcbradley22:05:14

i don't know of any

noisesmith22:05:18

technically most of kafka was written in scala, but I think it comes close, and joda time

noisesmith22:05:33

I’m aware that there are people that disagree with me about these things

noisesmith22:05:31

also ExecutorService and ScheduledExecutorService - almost always better and simpler to use directly compared to the many wrapper libs

mobileink22:05:48

yeah, de gustibus non disputandem etc. but ihave not come across many, not that i have looked very hard.

noisesmith22:05:57

Process / ProcessBuilder

bcbradley22:05:10

i wouldn't mind using clojure's java interop to work with vulkan if it were there were a pure java vulkan implementation but there isn't (and I don't expect there to be)-- working with java in clojure is fine, but working with what is basically c in java in clojure is not fine, and I just wanted to have a library to hide the ugliness, manual memory management, and etc

bcbradley22:05:56

on top of that the lwjgl libraries use "buffers" instead of pointers, which means they basically have a subclass for every corresponding c struct that represents the "array" form of that struct

bcbradley22:05:02

its just a lot of ugliness tbh

noisesmith22:05:24

how many of those could you hide without degrading performance?

bcbradley22:05:45

without degrading performance significantly? probably all of them

bcbradley22:05:53

there will be some degradation though, just because thats how it is

bcbradley22:05:15

there is something i won't be able to hide though, and thats the ugliness inherent in vulkan itself

noisesmith22:05:39

arcadia made a special compiler, using their own assembler, in order to make clojure code that doesn’t use the gc

bcbradley22:05:56

that sounds interesting

noisesmith22:05:25

it’s the thing that makes me wonder if the manual memory management isn’t serving an important purpose

bcbradley22:05:59

i'll give it a look later, right now i'm in the process of finishing this library as I've already finished the code for it and just need to run it through the compiler to make sure I didn't mess up

bcbradley22:05:20

or catch gotchas like the type hint issue above

mobileink23:05:17

@noisesmith nit: the examples you gave are "core" java, and i agree the for some of them you do not need a clojure wrapper. 3rd party libs are a whole 'nother story, though.

noisesmith23:05:12

not all of them are core java

noisesmith23:05:23

and all of them are libs that people commonly use clojure wrappers for

mobileink23:05:52

ok "core" might be too strong.

mobileink23:05:46

take String as an example. no need for a wrapper, except that we want one, in the end.

misha23:05:53

greetings! what are tradeoffs of this

(let [last-temp-id (atom -1000000)]
  (defn tempid []
    (swap! last-temp-id dec)))
compared to following?
(def ^:private last-temp-id (atom -1000000))

(defn tempid []
  (swap! last-temp-id dec))

bcbradley23:05:50

never seen anyone close over a locally bound var like that

mobileink23:05:40

my understanding is that it is considered bad form to put def or defn inside a let.

tanzoniteblack23:05:12

I do that on occasion. I just guarantees that nothing except that function has access to that mutable value

misha23:05:21

yeah, I heard it too, but why exactly (even in such trivial case)?

noisesmith23:05:34

because hiding data is an antipattern in clojure

noisesmith23:05:53

that’s the common consensus at least

misha23:05:22

I specifically included ^:private to try to address "hiding data" argument

tanzoniteblack23:05:31

^:private just means that no one outside of the namespace has access to the function (which is actually a lie, you can access it if you try hard enough)

bcbradley23:05:51

if you declare it private, that doesn't mean you didn't hide the data

tanzoniteblack23:05:52

the let means that other code doesn’t have a reasonable means of accessing it. And yeah, it’s about “hiding” data

bcbradley23:05:57

if a tree falls and nobody hears it, it still fell

john23:05:11

and mutating the top level, when all you want to do is play with something in a particular scope, is bad form.

mobileink23:05:24

you need to tell us how you plan to use these things.

john23:05:27

so if you really know you want it in the top level, and you know what you're doing...

tanzoniteblack23:05:37

generally when I use that kind of thing, it’s when I’m memoizing (or some other form of cache) that’s specific to the function I’m working on

misha23:05:40

also, in a second case, atom is not passed as an argument either, and the fact that that does not trigger most riders' aesthetic senses, brought me here now :)

noisesmith23:05:17

@misha yeah, top level atoms are something I reject in code reviews

tanzoniteblack23:05:29

Could I implement it in a different fashion? Yes. But I like the separation of concerns the let provides. I’ll admit it does look horrible though, and it’s very rare that I do something like that.

noisesmith23:05:39

but I also don’t accept def / defn inside let

misha23:05:41

@noisesmith what are the alternatives to top level atoms?

bcbradley23:05:13

generally speaking clojure people expect functions to be pure by default, so if you create a function that returns a different value on subsequent invocations because it mutates a variable that only it can see, then you have something that isn't idiomatic clojure.

noisesmith23:05:16

providing an argument, or using a proper system initialization argument (eg. as used with stuartsierra/component)

john23:05:41

(def thing (let [a (atom 1)] (fn [...

bcbradley23:05:09

my advice is don't try to hide data. if someone wants to have some state let them control that, let them declare an atom or whatever kind of reference type

mobileink23:05:15

@misha why do you want a defn in your first example? why not (let [f (fn ... )]... f )

bcbradley23:05:23

to whatever extent is possible, just use pure functions that take data and return data

misha23:05:03

Generally, most of the above - is a sound advice: don't hide the data, pass stuff as an argument, pure functions. This particular use case, however: 1. only that function has to have an access to data, 2. need to emit unique value during lifetime of the process across all the calls. so would renaming it to next-tempid! be good enough?

bcbradley23:05:16

why not (comp hash gensym)

misha23:05:35

@mobileink not sure I understood w/o an actual example

john23:05:51

The cannonical way to do what you're wanting to do, if no other functions will be using the atom, is to use a def

mobileink23:05:59

sorry, i don't see what you're trying to do. but for the record def and defn are side-effe ctish - they intern vars.

noisesmith23:05:28

not just that, they mutate vars

misha23:05:57

@bcbradley no particular reason "why not", just trying to keep in consistent with datomic/datascript temp-ids' semantics. An actual story is longer than that, of course

wiseman23:05:04

using gensym seems a little… hypocritical here 🙂

bcbradley23:05:08

how about if instead you look at it differently

bcbradley23:05:21

instead of looking at it like a function that mutates a local atom to ensure unique ids

noisesmith23:05:29

@wiseman the fun part is that gensym does the same kind of shared implicit mutability that we are criticizing here

bcbradley23:05:31

you think about it like: lets have an infinite sequence of unique ids

wiseman23:05:58

i’ve seen & done the let-over-defn thing in lisp before, for the same reasons you’ve brought up. i’ve mostly decided it’s better to make the state accessible, though, even if its a little hidden via ^:private or whatever.

wiseman23:05:16

even if just that it makes debugging harder to not have it accessible at all.

noisesmith23:05:55

wiseman: agreed, and a pattern I like is an implementation function that takes an explicit state arg, even if that isn’t the one most users of the lib access, so that unit testing can be cleaner and simpler to implement

john23:05:25

(def tempid
  (let [last-temp-id (atom -1000000)]
    (fn []
      (swap! last-temp-id dec))))

john23:05:09

oops 🙂

john23:05:20

forgot the fn

misha23:05:55

how is this better? def as a outte-most form?

john23:05:11

the def closes over the atom

bcbradley23:05:14

defn is implemented in terms of def and fn

john23:05:21

that provides you the privacy you wanted

qqq23:05:26

Is there a way to have a macro expand to (catch Exception ) / if in CLJ (catch :default ) / if in CLJS ?

misha23:05:15

@john does it have any different/measurable implications/advantages over "let-first" my first example?

john23:05:17

not sure what you mean by let-first

misha23:05:19

it is not that triggering, when def is a top form, yes, but does it actually work in a different way under the hood? atom is still inaccessible, neither it is passed as an argument, etc

misha23:05:30

(let [last-temp-id (atom -1000000)]
  (defn tempid []
    (swap! last-temp-id dec)))

john23:05:54

oh, eh, probably not

noisesmith23:05:55

yeah, the def is just quibbling with the semantics, it doesn’t address the criticism

john23:05:06

It's just a smell

john23:05:51

Because new folks to clojure will often want to def within a scope

noisesmith23:05:21

it’s not just a smell, we actually want to avoid inaccessible state

noisesmith23:05:38

especially inaccessible state that changes over time that doesn’t have an init or reset accessible anywhere

misha23:05:59

I think @john refers to outermost let as a smell

noisesmith23:05:13

I’m saying it’s not just a smell - it also does something we don’t want

noisesmith23:05:31

and his example solves the smell but not the problem

john23:05:11

right, except when you don't. Basically, don't make impure functions if you don't have to. But when you really really have to, the def-let form above smells less than the let-def form.

misha23:05:23

I agree, that even If I understand every bit of this particular case, some reader might extrapolate a bit too much, and use same construction inappropriately somewhere else

misha23:05:21

@qqq reader conditionals

noisesmith23:05:32

(def local-state (atom 0))

(defn new-token
  [state]
  (swap! state inc))

(defn next-token
  []
  (new-token local-state))
It’s clear that local-state is an implementation detail, but there is no inaccessible local state. This simplifies testing, debugging, and extension (let’s pretend the code was doing something that is actually interesting enough to need testing and debugging and extension)

qqq23:05:23

@misha: where can I lear about how macros interplay with reader conditionals ?

misha23:05:28

@noisesmith if that's a library, I'd like to avoid extra stuff to show up in an autocomplete suggests, please :) this is where "hide that atom nobody should ever care except this 1 fn" really comes from

noisesmith23:05:01

OK, then use ^:private if you must, it’s simple to work around

noisesmith23:05:17

what I’m providing an alternative to here is the thing where let is used to hide data

john23:05:34

@noisesmith what about the argument that closing over the atom increases the semantic determinacy of the whole program, assuming others can come in underneath you and change it?

noisesmith23:05:02

we increase semantic determinacy by not using mutation

misha23:05:29

(clojure.lang.RT/nextID)
kappa

john23:05:55

I know it's not likely, but couldn't a downstream user swap! on that local-state?

noisesmith23:05:15

sure, they can also use def to replace all your functions

john23:05:13

closing over the atom just feels like a stronger guarantee, but you have me questioning my intuition 🙂

misha23:05:34

now I need to find cljs analogue and I'm good

john23:05:38

the debugging is a good point too

bcbradley23:05:31

on a scale of dirty to filthy how bad is this:

(def ids (iterate dec -999999))
(def next-id []
  (def ids (drop 1 ids))
  (first ids))

noisesmith23:05:56

don’t use def for runtime changes

bcbradley23:05:26

but duck typing

noisesmith23:05:31

(unless specifically redefining code because you are in a development environment of course - def / defn in a repl are fine)

noisesmith23:05:51

we have types that are actually good for runtime mutation

noisesmith23:05:02

that address things like data races for example

noisesmith23:05:28

two people can get the same id from that code

john23:05:38

@misha you could mangle a (gensym)

misha23:05:46

let-def looks like idiomatic clojure compared to this :D

bcbradley23:05:06

she wants to have the same semantics as datomic

bcbradley23:05:16

otherwise (comp hash gensym) would make sense

noisesmith23:05:20

can two threads get the same id back from datomic?

john23:05:28

But that's super non-deterministic.

misha23:05:23

I went with private atom. the main benefit is "I will not freak out in 2 weeks looking at (gensym) in my mundane code just to get some negative int" opieop

bcbradley23:05:46

there we go, and thats how you know you didn't drink the koolaid

bcbradley23:05:57

pragmatism trumps all day erry day

misha23:05:08

@bcbradley did you assume my gender? (I am dude, btw kappa)

bcbradley23:05:21

lol nice meme

noisesmith23:05:25

misha is a nickname for alexander in slavic countries

noisesmith23:05:34

(I almost said something myself)

noisesmith23:05:44

err, no, michael

misha23:05:04

it is slavic Michael/Mike/etc

bcbradley23:05:52

not that many man names end in a

noisesmith23:05:04

sasha - alexander

bcbradley23:05:25

i can't think of any others

john23:05:25

thing about using gensym, it's global, right? If other parts of an application call it, they inc your counter.

misha23:05:09

gensym calls (clojure.lang.RT/nextID)

john23:05:25

And if global uniqueness is what you need, that's want you want. But if it's just for this one thing, a specialized counter is preferable imo.