Fork me on GitHub
#clojure
<
2017-03-10
>
paulocuneo00:03:43

Hi! Are [org.clojure/tools.analyzer.jvm "0.7.0"] and [org.clojure/clojure "1.9.0-alpha14"] compatibles ? When i add jvm analyzer dep i have a cast exception clojure.lang.Keyword cannot be cast to java.util.Map$Entry at clojure.core$imap_cons.invokeStatic (core_deftype.clj:143) clojure.core$imap_cons.invoke (core_deftype.clj:132) clojure.lang.Var.invoke (Var.java:383) logic.check.Rec.cons (check.clj:130) clojure.lang.RT.conj (RT.java:667) maybe its lein?

paulocuneo03:03:07

I was extending List and Map$Entry, guess extend-type is non-deterministic with interfaces.

(extend-type java.util.List
  IMap
  (-map [ a ]
    (map -map a)))
(extend-type java.util.Map$Entry
  IMap
  (-map [ a ]
    [(-map (first  a)) 
     (-map (second a))]))

alexmiller03:03:54

If there are multiple extensions and one derived from the other, the derived is picked

alexmiller03:03:12

If they are unrelated then it's undefined

alexmiller03:03:29

And in practice either can happen

paulocuneo03:03:31

I missed that part of the docs, thanks @alexmiller.

tbaldridge03:03:33

@paulocuneo and in practice I've even seen it change at runtime as in one call will go one place, another another place. I think whenever something new is registered it can move things around

tbaldridge03:03:08

so yeah, best practice: stick with extending once per "level"

alexmiller03:03:11

Multimethods have the ability to define preferences

alexmiller03:03:26

So that's another option

paulocuneo03:03:28

@tbaldridge i guess as concrete as posible, for now i added MapEntry.

(extend-type clojure.lang.MapEntry  
  IMap
  (-map [o ]
    [(-map (first o)) (-map (second o))])) 

paulocuneo03:03:01

Many thanks!!

shaunlebron06:03:22

What does the “dup” in *print-dup* stand for?

shaunlebron06:03:15

oh, as in “I want this exact value to be duplicated when it’s read back in”?

bcbradley07:03:47

when you perform java reflection to get the parameters of a method and ask the parameters for their types, if it is a object type it will give you the fully qualified class name like java.lang.String. I need this as a string though, so I use clojure's str on it, but when I do I get "class java.lang.String" for example. Is the "class" part of the name? In other words, if i were to use with-meta to specify a :tag for type-hinting purposes, would it be confused at the "class" part? If it would be confused, why did the "class" part get tacked onto the front in the first place?

rauh07:03:39

@bcbradley Just use .getName instead of str

bcbradley07:03:32

If i use get name on an array type i get java.lang.String[] rather than [Ljava.lang.String

bcbradley07:03:54

the latter is expected by clojure

bcbradley07:03:37

thanks i didn't know class was a function in the core library

rc114010:03:15

hi all , anyone have an example how to use sente (https://github.com/ptaoussanis/sente) with json instead of transit ,

keymone11:03:45

how many futures is too many? are they heavy on the system?

bcbradley11:03:32

futures aren't heavy

bcbradley11:03:05

if you have so many futures it is slowing down your code, and you are sure you need the futures (or some other abstraction) in your code because it allows you to separate what happens from when it happens, you won't be able to find any other abstraction that gives you that separation at a lower cost except callbacks

keymone11:03:15

so they are similar to (go) blocks?

bcbradley11:03:19

but chaining callbacks leads to callback hell, and the difference between futures and callbacks in terms of cost is so negligible it would be hard to imagine any case in which callbacks would be measurably faster than futures

keymone11:03:30

in terms of costs

bcbradley11:03:35

no much much lighter

bcbradley11:03:47

core.async has something like go blocks

bcbradley11:03:59

unless that is what you were referring to

keymone11:03:04

yes, that’s what i was referring to

bcbradley11:03:13

futures are extremely lightweight

keymone11:03:37

ok, that’s all i wanted to know, thanks

bcbradley11:03:46

you may want to look at promise'

bcbradley11:03:50

it is similar to future

bcbradley11:03:48

basically your future is someone else's promise

bcbradley11:03:57

and your promise will be someone else's future

bcbradley11:03:57

when you make a future, you are saying that it will eventually have a value, and its up to someone else somewhere to provide it

keymone11:03:04

how possible is it that future will not be started immediately?

bcbradley11:03:12

when you make a promise you are saying that you will produce a value, but its up to someone else some where to refer to it

bcbradley11:03:36

the concept of running it in parallel with other stuff isn't mentioned because it isn't part of the abstraction

bcbradley11:03:50

you can figure the value of a future however you want

bcbradley11:03:01

a promise can be kept however you see fit

keymone11:03:04

so if i need a future to (Thread/sleep 100) and then do something, what is the probability that Thread/sleep itself won’t be invoked for some significant time before the future is scheduled?

bcbradley11:03:01

i wouldn't rely on probabilities, but with the exception of some edge cases its likely that it would be roughly 100 ms

bcbradley11:03:10

technically always more than 100ms

bcbradley11:03:37

most operating systems time slice processes

bcbradley11:03:49

the time quantum adds some delay

keymone11:03:11

i’m just wondering if it could happen that if i never deref a future and just discard it - it won’t be scheduled

bronsa11:03:12

unless you explicitely cancel the future before the work has started

bcbradley11:03:35

i'd like to point out that this is a clojure optimization

bcbradley11:03:45

and probably a common optimization across implementations of future

bcbradley11:03:54

but it isn't part of the theory of futures or the abstraction itself

bcbradley11:03:21

its perfectly valid to have a future where it won't invoke the body until asked

bronsa11:03:46

also I wouldn't say that futures are lightweight is true, they're still real threads

bcbradley11:03:08

i'd say they are lightweight in clojure

bcbradley11:03:44

because and will cache the result and return it on all subsequent calls to deref/@

bronsa11:03:55

not more lightweight than any other java thread, certainly not than go blocks

bronsa11:03:19

well, we've got different definitions of lightweight

bronsa11:03:39

i'm talking about memory footprint which is what people usually talk about when comparing green/real threads

bcbradley11:03:42

because of the way clojure is structured around values and immutability, its more likely that the caching behavior of futures can actually be used

bcbradley11:03:52

its hard to use this sort of thing in a state oriented language

bcbradley11:03:11

if you can't get good use out of the caching aspect of futures, it might be wiser to reorganize your program around agents

rauh11:03:32

@bcbradley future used the agent threadpool internally 🙂

bcbradley11:03:04

i didn't know that

bcbradley11:03:15

honestly i just use a lot of promises in my code, arrays of promises, seqs of promises

bcbradley11:03:29

sets of promises

bronsa11:03:41

i really don't see how any of that is relevant TBH. futures are not lightweight and spawning too many futures can definitely negatively impact memory footprint/performance as they run in an unbound threadpool

bcbradley11:03:07

alright i'll be more specific then

bcbradley11:03:12

make a future isn't lightweight

bcbradley11:03:20

derefing it is

bronsa11:03:38

don't think that's what @keymone was asking tho

yonatanel11:03:56

Manifold can help, especially when dealing with blocking calls, or 'send-off' when using agents.

bcbradley11:03:03

i misunderstood because i was thinking about how I use them not how he might potentially be using them

bcbradley11:03:04

thats my fault

bcbradley11:03:35

i'd also like to point out that a future isn't THAT heavy

bcbradley11:03:38

even on creation

bcbradley11:03:54

because its not like it occupies X resources forever

bcbradley11:03:04

a data structure that stores data occupies the data it takes up forever (until you remove it)

bcbradley11:03:16

a future occupies cpu resources until it resolves

bcbradley11:03:18

then it doesn't

tomaas12:03:05

hi, using this lib https://github.com/nberger/ring-logger how can I make log to file?

tomaas12:03:15

java -jar app.jar > file.txt does not do that

keymone12:03:28

i find myself wrapping a lot of stuff in (go) blocks when i do core.async, like every time i have anonymous function that does send or receive - am i doing this wrong?

yonatanel12:03:55

@tomaas How do you use ring-logger? You have to add it as middleware

tomaas12:03:28

like it is said in the lib's docs (jetty/run-jetty (logger/wrap-with-logger my-ring-app) {:port 8080})

tomaas12:03:41

it logs the production jar well on the console

tomaas12:03:56

but i'd like to redirect it to log to a file

nberger12:03:55

@tomaas: ring-logger supports different "logging backends". By default it comes with clojure.tools.logging, which in turn is an interface that delegates to a specific logging backend, knowing how to talk to the most common used java logging libraries (slf4j, commons.logging, log4j and others), so you have to use & configure one of those and tell them to log to a file

nberger12:03:49

There is also clj-logging-config that can help you in configuring those logging backends. And then you could even use ptaoussanis/timbre, a "Pure Clojure/Script logging library" for which there is ring-logger-timbre. But still then you'll have to configure it to log to a file.

yonatanel12:03:05

@nberger Once stdout logging is in place, shouldn't redirecting it to file just work?

nberger12:03:25

Hmmmm actually yes 🙂. Be careful to redirect both stdout and stderr as needed

yonatanel12:03:48

In my case ubuntu upstart/systemd is taking care of it

nberger12:03:10

@tomaas: what happens when you run your jar with the redirection? Do you still see messages in the console that you'd like to be sent to the file, or are the log messages being sent to a different file?

yonatanel12:03:21

@keymone It can be fine as long as you understand when you want to wait on things or apply backpressure in your system instead of blindly grow an unbounded task queue.

wei12:03:03

has anyone written a case-insensitive map? not just down-casing, I’d like to preserve the capitalization of the keys

tomaas12:03:30

@nberger, exactly, I still see the messages of ring-logger in the console and they do not get written to file. However the (prn "...") in clojure code are written in that file

keymone12:03:15

yonatanel: so say i have (go (do-stuff)) <- i can run >! and <! in do-stuff, but if i do (go (do-stuff #(>! channel))) - this is not allowed. is it a normal pattern to just wrap anonymous call in a go like so: #(go (>! …))?

nberger12:03:13

@tomaas ok, then the issue is that logging is being sent to stderr. Try with java -jar app.jar 2> file.txt to send only the err output to the file or java -jar app.jar > file.txt 2>&1 to send both to the file

tomaas13:03:06

nice, I was just about to try that

tomaas13:03:09

thanks a lot!

joshkh13:03:13

when calling use from the repl on a given namspace, should i then have access to any namespaces required by that namespace?

joshkh13:03:24

for example, gen can't be resolved when i call (use 'ms.specs) on the ns (ns ms.specs (:require [clojure.spec :as s] [clojure.spec.gen :as gen]))

keymone13:03:03

@yonatanel i don’t understand the difference between put! and >!

jstew13:03:37

@joshkh No. in the ms.specs namespace, require only aliases clojure.spec.gen as gen for that namespace only. use-ing that namespace from another namespace doesn't alias it in the namespace you use it from.

joshkh13:03:21

okay thanks! the evidence was pretty clear, i just wanted to make sure i wasn't missing something 🙂 is there a convenient way to pull in the aliases?

jstew13:03:25

https://clojuredocs.org/clojure.core/require should make that more clear than I could 🙂

joshkh13:03:04

that's quite a useful description, thanks!

jstew13:03:41

@joshkh, yes, you can call require in the repl (require [clojure.spec.gen :as gen])

joshkh13:03:27

roger that

qqq14:03:55

given a function g, is there a short hand for (fn [x y] (g y x)) ? basically swap the arguments before passing them to g; g is guaranteed to take precisely 2 inputs

shdzzl14:03:41

@qqq #(g %2 %1)?

slipset14:03:07

@qqq the question pops up every so often. @shdzzl s solution is good, a macro involving reverse on the args is also possible.

slipset14:03:53

@seancorfield tends to say that they have this implemented at his company.

slipset14:03:51

And, @alexmiller is normally not interested in adding such a function to core ;)

shdzzl15:03:49

Yep, no flip in clojure's standard library that I'm aware of, but if you need a more general solution:

(defn flip [f]
  (fn [& args]
    (apply f (reverse args))))

qqq15:03:53

@qqq @shdzzl @slipset : yeah, I guess I was epecting there be a "functional level appraoch" where it's (a->(b->c)) -> (b->(a->c))

qqq15:03:10

right, haskell's 'flip' was what I was expecting

shdzzl15:03:44

@qqq ^ That'll work.

alexmiller15:03:56

@slipset Rich has vetoed this in the past

qqq15:03:19

oh, this is hopeless then

qqq15:03:30

probably not worth trying to impeach Rich over "not adding flip to clojure.core"

alexmiller15:03:49

he feels it is not clojure style

qqq15:03:03

I guess without type checking, flip / point-free programming becomes relaly hard to debug

slipset15:03:15

FWIW I've come to believe that the need for flip is a smell

alexmiller15:03:37

in the vast majority of cases, I agree

alexmiller15:03:05

in Clojure that is, not talking about Haskell or other langs

qqq15:03:18

maybe I'm doing thing swrong, but often in my code for (map f lst), the "lst" is simple, and the "f" is a complicated (fn [] ... ) that is multipline

qqq15:03:32

and things would be prettier in

(flip map lst (fn [] ...
  ...
  ...
)

slipset15:03:02

No, you probably want to use ->> with map

qqq15:03:36

(->> lst
  (map (fn [] .....
   ...
  ...
)))
?

dpsutton15:03:02

(->> simple-col
    (map (fn [e] (complicated (body ...)))

alexmiller15:03:44

if your map fn is complicated, maybe make it a defn, not an anonymous function

slipset15:03:52

I'd then extract the anon fn

qqq15:03:08

even if I never use the anon fn anywhere else?

alexmiller15:03:33

you can make as many functions as you want :)

slipset15:03:44

Then you can test your fn with one "thing"

qqq15:03:37

facepalm .... I'm not even writing unit tests

qqq15:03:02

maybe this is why my code falls under it's own complexity at around ~3k lines of code

slipset15:03:33

Generally I tend to write functions that operate on one thing, then map/filter/etc in a separate function

shdzzl15:03:52

Clojure has good namespacing, naming single-use functions is OK most of the time. A lot of Clojure's function manipulation happens at application rather than definition, is another takeaway here. More so than in Haskell at least. Threading macros are amazing.

kauko15:03:06

How do I test assertions in macros? I have a macro that is supposed to fail under certain conditions, and I want to write a test that passes if it does fail.

rauh15:03:52

@kauko (is (thrown? c body)) checks that an instance of c is thrown from body, fails if not; then returns the thing thrown.

kauko15:03:19

Yeah but the macro fails during macro expansion

kauko15:03:44

So if I write (is (thrown? AssertionError (macro-that-fails))), the code doesn't run

rauh15:03:53

Have you tried calling macroeexpand-1 manually?

kauko15:03:23

Hmmm, let's see

kauko15:03:36

(is (thrown? AssertionError (macroexpand-1 '(failing-macro)))) runs, but the test fails as that returns nil. When I run the expansion in my repl, I do get the assertion error

kauko15:03:32

ah, it needed to be java.lang.AssertionError 🙂

kauko15:03:36

thanks again @rauh!

kauko15:03:25

It made complete sense in the end. I guess I was just so used to thinking of macroexpand as something you call from the repl, it didn't occur to me I should use it here

tbaldridge15:03:35

@kauko the general advice about macros is this: Do practically nothing inside the macro itself. Instead have the macro dispatch to a defn where all the actual work is performed. Now you can test the assertions in the defn like any other function

tbaldridge15:03:57

See the source of clojure.spec for this. Tons of macros, each is paired with a foo-impl defn

kauko15:03:16

That's a great tip, thanks!

tbaldridge15:03:46

also easier to debug in the repl that way

kauko15:03:55

I'm actually just learning about macros, I've been working with clojure for 2 years now but never really written any

kauko15:03:02

so that's a great learning point, thank you 🙂

moxaj15:03:09

@tbaldridge I was wondering about the -impl stuff, thought it was because of private vars

jstew15:03:04

I've found that I barely ever need macros unless I'm making a library or something. You can get really far without having to write a macro!

shdzzl15:03:54

The only times I have ever used macros, it was to move quoting out of user space. I don't think I've ever written a macro that was longer than a single line.

bcbradley15:03:49

what is the fully qualifed class path of a templated class?

bcbradley16:03:11

like org.lwjgl.system.Configuration<T>

bcbradley16:03:18

do you need the <T> ?

bcbradley16:03:03

like if i wanted to construct an object of that type in clojure using interop would i say (org.lwjgl.system.Configuration<java.lang.String>. param1 param2 param3) (or whatever in place of java.lang.String)?

bronsa16:03:28

templates don't exist at the jvm level due to erasure

bronsa16:03:43

clojure doesn't have to deal with templates

bcbradley16:03:26

what can i do to instantiate a templated class then?

bronsa16:03:47

omit the template bit

bronsa16:03:04

it only exist at compile time in java, you don't need it in clojure

bcbradley16:03:28

(org.lwjgl.system.Configuration. param1 param2 param3)?

bcbradley16:03:43

god i never knew lwjgl was so enormous

bcbradley16:03:52

there are like 10 thousand functions here

yonatanel16:03:01

@keymone put! doesn't have to be inside a go block, and it doesn't block. Seems like it's simpler and you can just use that to pass values around until you figure out your backpressure policy

msuess18:03:05

@bcbradley have you looked at libgdx? I've never used it from clojure but it's a pleasant wrapper around lwjgl, among other things. Might be overkill for your use case though

bcbradley18:03:36

its not really a wrapper

bcbradley18:03:42

and its a java framework

bcbradley18:03:58

play-clj is a wrapper around libgdx

msuess18:03:03

Yeah I was oversimplifying a bit

msuess18:03:18

Nice, I'll have to check that one out

bcbradley18:03:56

i'm making a clojure idiomatic wrapper around lwjgl, but most of lwjgl's functionality is procedural or state machine oriented

bcbradley18:03:01

i'm going at it in two steps

bcbradley18:03:38

one library will be a dumb simple wrapper for me and others who may want to build on it without dealing with oblique interop syntax and the ridiculous naming conventions of lwjgl

bcbradley18:03:44

the other will be the utility library

msuess18:03:02

Are you planning on wrapping the open GL part in a pure way?

bcbradley18:03:05

basically there will be a dumb-wrapper which is as impure as the specification is

bcbradley18:03:25

then there will be a second wrapper which depends on that one which provides as pure a view of open-gl and the other libraries that i can

bcbradley18:03:10

I suspect i won't be able to enforce a lot of clojure's ideas about immutability onto opengl, even if i make the second wrapper really smart

bcbradley18:03:31

but i can try, and anyone who thinks they can do it better can use the first "dumb-wrapper" to do it painlessly

aaron-santos18:03:31

@bcbradley do you have a repo I can bookmark? I’m interested in eventually porting a lib I have. It would be nice to depend on a clojure library than use lwjgl directly.

bcbradley18:03:50

basically the only job of the first wrapper is to provide a straight view of open-gl, open-cl, open-al and all those others without having to say http://org.lwjgl.opengl.GL/glBlah()

bcbradley18:03:54

i mean there are like 3 gls there

bcbradley18:03:56

its a pain in the ass

bcbradley18:03:15

i haven't put anything on github yet, i'm still working on programmatically converting lwjgl into a clojure-y version

bcbradley18:03:52

i'm using java reflection and interop, and some other stuffs like trying to take the type information and add it to the clojure version using (with-meta ...) which works like type-hinting

tbaldridge18:03:18

Well if you import GL, it's basically just (GL/glBlah foo bar)

bcbradley18:03:46

yeah but thats still two GLs

bcbradley19:03:39

besides there are some things in lwjgl that just have silly naming conventions. I forget which class, but one of them i remember has every method start with "m"

aaron-santos19:03:56

do you have any plans to consolidate/make it easier to access constants in GL11, GL12, ….? I find myself doing absolutely awful things to stay sane when working with gl constants (https://github.com/aaron-santos/zaffre/blob/master/src/zaffre/glterminal.clj#L108)

bcbradley19:03:12

what do you suggest?

wei19:03:41

how might one implement a case-insensitive map? one that preserves the case of the keys, not just downcases them

dpsutton19:03:12

so the keys remain untouched and have a case but the lookup is case agnostic?

dpsutton19:03:49

and if you do it, you'll have to live with a fundamental incorrectness factor by breaking the bijection between keys and what maps to them:

user> {:a 1 :A 2}
{:a 1, :A 2}

dpsutton19:03:15

so what would this return for you? (case-insensitive-get {:a 1 :A 2} :a)

juliobarros19:03:29

Anybody ever use something called JEXL (http://commons.apache.org/proper/commons-jexl/) in java land? Ever use it with clojure? I need to allow similar user entered expressions in a clojure program and would love to learn from others’ experiences. Thanks in advance.

dpsutton19:03:12

but again, you need it to be correct only on your domain, so if this isn't a problem for you it's not a problem

exit219:03:06

Anyone have any insight on when Clojure Conj will be going on this year? @dnolen Maybe?

alexmiller19:03:22

@njj we should be announcing it soon, but I believe it will be DC-area

exit219:03:55

@alexmiller Awesome thanks dude

dpsutton19:03:57

dang. i was rooting for new orleans

alexmiller19:03:15

@dpsutton I personally would love to do one there :)

dpsutton19:03:29

you should speak to whoever is in charge then 🙂

dpsutton19:03:44

ie, just mutter to yourself "we're doing it in nola"

alexmiller19:03:29

well that’s how we got Austin :)

dpsutton19:03:43

big fan. i got to go to that one because i could drive

dpsutton19:03:54

your conferences are incredible

exit219:03:08

Stoked, this will be my first one

alexmiller19:03:21

I think a Chicago conj would be cool too

shaun-mahood19:03:25

We're up to confirmed 3 Clojure devs in Edmonton if you want to come north... (but please don't since I want to come south again)

bcbradley19:03:07

@wei make a new map from the old one and share structure

dpsutton19:03:09

how would that give case insensitive lookup?

lvh19:03:05

Wooo Chicago.

wei19:03:15

is it possible to change the value of a static private final field in Clojure? perhaps using reflection?

alexmiller19:03:49

you can call the Java reflection APIs

alexmiller19:03:18

whether or not you’re allowed to do that has actually gone back and forth over various JDKs

alexmiller19:03:30

it should go without saying that you shouldn’t do that :)

wei19:03:02

fair enough. thanks for the encouragement 🙂

alexmiller19:03:49

static final fields are often inlined directly into the code using it, so often changing it does not necessarily have any effect

dpsutton19:03:14

> encouragement

bcbradley19:03:52

@wei you can generate a new map that is the same as the old one except convert every key to lowercase. During your conversion, if you happen to have two keys that get converted to the same thing then you can handle the collision right there with an exception or whatever.

chrisn19:03:32

I have a function that I need to generate but that I would like to have full type hints for. Actually I would like to generate a map of them and then use them in some code in a type hinted way. So there are two problems, first to generate a function that takes and returns a primitive and then the next would be to call that function.

alexmiller19:03:57

what do you mean by “generate”?

chrisn19:03:05

So first problem looks like: (defmacro [] `(^long (fn [^long data] data))

chrisn19:03:23

sorry:

(defmacro [] `(^long (fn [^long data] data))

chrisn19:03:42

I can't seem to get the with-meta and unquot semantics to play well together.

alexmiller19:03:44

you typically need to use with-meta, vary-meta etc inside macros

wei20:03:02

@bcbradley right, it would be great if the resulting structure had map semantics though. just wondering if anyone has published something similar

alexmiller20:03:09

@chrisn I don’t know what the outer ^long is doing

chrisn20:03:15

But I can't see how to do this with both the argument and the return value.

chrisn20:03:26

My syntax may be shot

alexmiller20:03:37

the thing you want to create is (fn ^long [^long data] data)

dpsutton20:03:07

@wei i think he's saying just make a map with all downcased keys based on your incoming map and just make sure to lookup in it with downcased keys

wei20:03:52

oh I see, but then if you printed out the map you’d lose the case on the keys

wei20:03:17

it would be nice to create single map-like thing with case-insensitive lookup like @bcbradley's downcased key map, but preserving the case on the keys when you iterated through it

dpsutton20:03:51

yeah i can see that

dpsutton20:03:11

that's why i was suggesting the potemkin thing or looking at deftype and implement the functions that you need

wei20:03:28

potemkin looks promising, thanks!

dpsutton20:03:39

can you show me the code when you finish it?

dpsutton20:03:45

i'm interested to see your final implementation

wei07:03:09

oh I ended up just downcasing since it worked (if not ideally) for my case. I’ll probably take another stab at it sometime though, will ping you then!

chrisn20:03:17

Then put that in a map. Then a the callsite get it out of the map and call it in a correctly typehinted way.

dpsutton20:03:22

which would certainly be map semantics

dpsutton20:03:18

i was looking into deftype and how to implement the interfaces like ILookup or whatever it is

dpsutton20:03:42

and hijack valAt but i'm not too sure how to do this kinda thing. Not sure how popular potemkin is here but you could check out https://github.com/ztellman/potemkin#def-map-type

alexmiller20:03:51

@chrisn I don’t know that I have time to give you a proper example. I usually end up looking at prior examples to familiarize myself. You might look at some of the things in clojure.core. you’ll want to use meta with the :tag and :rettag keys

chrisn20:03:17

OK, no problem. Then I guess use with-meta at the callsite

chrisn20:03:24

(defmacro create-simple-fn
  []
  `(fn ^long [^long ~'elem-idx]
     ~'elem-idx))

(def test-map {:a (create-simple-fn)})

(defn call-test-map-fn
  ^long [^long arg]
  (let [fn-data (test-map :a)]
    ;;currently I have boxing and unboxing here
    (fn-data arg)))

chrisn20:03:31

Something like that but without the boxing.

bronsa20:03:49

anon fns will force boxing of the return type anyway

chrisn20:03:01

Not necessarily

alexmiller20:03:12

yeah, I was wondering about that

bronsa20:03:18

yeah they will

chrisn20:03:21

I was hoping not\

bronsa20:03:32

prim type hints in anon fns are mostly useless if you want to avoid boxing

chrisn20:03:04

There isn't a way to force the compiler to look for the correct override?

bronsa20:03:12

not at the moment

chrisn20:03:25

k. That sames a lot of time actually 🙂

netsu20:03:53

Okay, (count "💩") ; => 2 it's a proper bytes count. Just curious about how to get UTF-8 symbols count?

bronsa20:03:07

they're not no-ops, as they will force casting of the return type to a long before boxing and they will unbox the args after invocation

bronsa20:03:31

but you can achieve the same w/o type hints using the long function and not have to deal with metadata in macros

chrisn20:03:35

Then how does defn work?

bronsa20:03:43

defn attaches that metadata to the var

bronsa20:03:47

the compiler can look it up at compile time

alexmiller20:03:01

@netsu count is not going to very meaningful with strings like that. look at the Java String apis for that kind of stuff

chrisn20:03:32

So there are two problems. First, generate a function that doesn't do boxing.

chrisn20:03:36

Second, call it.

chrisn20:03:03

@bronsa you are addressing the second. But defn still needs to be able to generate a function that if the proper overload is called it doesn't do boxing.

bronsa20:03:34

yeah fn generates the unboxed overload with type hinting

bronsa20:03:39

which your anon fn will do too

bronsa20:03:01

the metadata on the var however is the only thing that allows the compiler to use that overload

chrisn20:03:04

Oh I thought defn mapped to fn

alexmiller20:03:09

@netsu String “length” is really the number of Unicode code points and the emoji counts for 2

chrisn20:03:12

(def (fn [blah blah] ))

bronsa20:03:22

unless you manually (.invokePrim ^IFn$LLL my-fn ..) but bleh

chrisn20:03:22

I am looking at it right now

bronsa20:03:48

@chrisn it does but it also adds some metadata to the var, and the compiler looks that metadata up when invoking a Var

chrisn20:03:14

OK, then there is a way to get fn to geenerate the unboxed version.

bronsa20:03:31

ther'es just no way to automatically invoke an anon fn through the unboxed path

hiredman20:03:32

the problem is the generic ifn call path doesn't allow that, so stop using ifns, generate custom interfaces with the types you want and call those

bronsa20:03:55

it's not generating the method that's the problem, the problem is at the callsite

chrisn20:03:29

I like @hiredman's solution; it is probably the most sane.

hiredman20:03:42

at some point I had a prototype of a library that would generate typed interfaces and use those for calls internally in a given bunch of code, so you could write kernels of optimized code, and then externally would present the conventional vars and ifns

bronsa20:03:54

with the addendum that it's significant extra work so make sure you actually need that optimization

chrisn20:03:28

I am iterating through 10000s of indexes potentially on multiple things in tight math code.

chrisn20:03:36

And things can have different index strategies.

alexmiller20:03:38

once you get a certain way down that path you start to consider just calling asm directly and generating bytecode :)

chrisn20:03:06

Lol, after this I need to do a similar thing for c++ and cuda

chrisn20:03:13

I will get close enough to asm anyway.

bronsa20:03:24

I was playing with adding a special form to tools.emitter a while ago that would allow injection of custom bytecode in the middle of a clj statement

bcbradley20:03:32

how fast do you want math to be?

bronsa20:03:40

SBCL allows that and it allows for some funny optimizations

bcbradley20:03:42

consider using open-cl

chrisn20:03:52

Then I have another path of custom kernels.

alexmiller20:03:54

or of course, just write this part in Java

chrisn20:03:56

That isn't an option right now.

chrisn20:03:12

Then I have to write different things for the different mathematical types. Or use a code generator for java.

alexmiller20:03:25

Clojure is an excellent Java code generator :)

chrisn20:03:28

I have gone down these paths. The clojure needs to be fast but also I need to be able to add primitive types

chrisn20:03:43

Sure, but type hinted macros get you close and you don't have a 2 step compilation process.

bcbradley20:03:45

or you could use interop and get open-cl via jogl

chrisn20:03:30

This is for a neural networking library @bcbradley and I don't want to write convolution kernels in opencl.

chrisn20:03:01

What opencl needs are javacpp bindings.

hiredman20:03:47

actually that gist reminds me, you could also generate anonymous fns that implement the primitive interfaces, and then invoke the primitive interfaces directly, instead of just calling them like ifns

hiredman20:03:01

(which as established doesn't work)

bronsa20:03:24

that's what i was suggesting earlier, but.. not that pretty

gfredericks20:03:47

has anybody written a script to refactor out :refer :alls? or have a guess how difficult an even-just-mostly-correct impl would be?

bronsa20:03:40

what clojure could actually do, is allowing type hinting of callsites to emit conditionally typed callsites, e.g.

^long (f ^long a ^double b)
could emit code that looks like
if (f instanceOf IFn$DLL) (IFn$DLL)f.invokePrim((long) a, (double) b)); else f.invoke(a,b)

gfredericks20:03:37

@hiredman slamhound doesn't rewrite the non-ns-code at all currently, does it?

hiredman20:03:53

no, you would get a big :refer [...]

gfredericks20:03:02

oh actually that's okay

hiredman20:03:25

if I recall, I mean, I haven't used it much

hiredman20:03:22

https://gist.github.com/hiredman/62f11dc663083f2127824d1c393c8f4c will tell you which vars are used in which namespaces over a bunch of code

bronsa20:03:33

which is not too dissimilar to what it already does on protocol callsites

bronsa20:03:42

but meh, probably not worth the effort

mikerod21:03:41

I find it oddly common to have namespaces (in the wild) that use clojure.string vars fully-qualified without adding a :require for them to the ns header

mikerod21:03:53

I see that one more than others. It turns out to be implicitly required automagically anyways (from some other load that happens during the RT startup I think). However, I’ve always considered it bad form to rely on any implicit namespaces to be loaded at all

mikerod21:03:09

(besides clojure.core)

hiredman21:03:42

the set of implicitly loaded namespaces has changed from release to release, if I recall, not something to depend on

alexmiller21:03:49

you should never assume something is loaded if you haven’t loaded it

alexmiller21:03:07

I have some pending tickets that reduce the implicit load set on startup

alexmiller21:03:59

I do that kind of thing at the repl sometimes, but would never do it in my source

wikibaus21:03:55

noob question - why do people complain about the clojure uberjar startup time in such a way as to suggest that it's a bad choice for backend w/ the lag?

not-raspberry23:03:58

wikibaus: maybe in the context of AWS Lambda

mikerod21:03:01

I think right now what actually happens is clojure.core requires requires clojure.string

alexmiller21:03:13

@mikerod that’s the thing I have a ticket for :)

mikerod21:03:14

@alexmiller thanks for the input

alexmiller21:03:33

and http://dev.clojure.org/jira/browse/CLJ-1891 is similar, mostly my fault that I made things worse when we added the socket server

mikerod21:03:38

I’ve done a lot of code reviews where I have to repeatedly tell people to not use fully-qualified clojure.string and clojure.set without a require

alexmiller21:03:01

I see pprint occasionally because it’s auto-referred at the repl

mikerod21:03:04

it has been a really common thing I keep finding. Implicit dependencies are bad. It’s sort of the same if implicitly depend on transitive deps in Maven etc.

mikerod21:03:45

Ah yeah, the socket one makes sense too. Thanks for the links. Good to know this has been brought up before.

shaun-mahood21:03:02

I think I started doing it based on some example somewhere - not sure why, but I had this assumption that it was the normal way to use clojure.string and clojure.set - I guess I have some fixing to do!

alexmiller21:03:33

I’m glad we’ve had this chat

zylox21:03:31

can confirm

uwo21:03:29

noticing java.math.BigInteger doesn’t print as e.g. 42N but rather 42. I know casting with bigint will cause it to print properly, but is there another way? These are values I’ve just pulled out of datomic

hiredman21:03:34

the clojure printer never prints java.math.BigIntegers as '42N'

hiredman21:03:06

'N' is for clojure.lang.BigInt

hiredman21:03:13

java.math.BigInteger's gets handled by the printer for java.lang.Number, which just calls toString on the number

uwo21:03:20

I’m attempting to round trip datomic query -> print to edn -> transact. Printing the fields that are marked as bigint in my datomic schema does not include ‘N’ for the reason you mention. Then transacting the slurped edn data fails because the bigint fields are not marked as such. Again, I know I can manually cast to bigint, but is there another way? like perhaps hijacking print?

hiredman21:03:02

are you sure the transaction is failing because of the numbers?

hiredman21:03:29

numbers outside of the range of a long without an 'N' are still read in as BigInts

uwo21:03:48

that’s just the thing. the attributes that supposed to be bigints are missing the ‘N’

hiredman21:03:09

are supposed to be bigints how?

uwo21:03:58

they are marked as such in my datomic schema. and when they are read out of the data base they have type java.math.BigInteger

uwo21:03:25

when printing them to an edn file, I lose that info, because the print-method, as you described

stuartsierra21:03:05

You can probably override print-method

uwo21:03:05

cool, just stumbled on that. something like…?

(defmethod clojure.core/print-method java.math.BigInteger [b, ^Writer w]
  (.write w (str b))
  (.write w "N"))

hiredman21:03:29

but clojure doesn't read numbers with a trailing 'N' as a java.math.BigInteger, so if datomic is holding out for an exact type match, that will still fail

tbaldridge21:03:54

I would really think that transact would silently convert a long to a big int.

stuartsierra22:03:50

Datomic will not convert long to bigint, but it will accept Clojure bigint 1234N as a value for :db.type/bigint.

uwo22:03:44

@hiredman querying datomic for a bigint attr returns java.math.BigInteger, but transacting a clojure.lang.BigInt for the same attr works fine. So, there is an asymmetry

jmp22:03:26

Hi Clojurians! A noobie to clojure here. And to programming in general. I've heard that Clojure drifts away from using inheritance. Is this true? And how do you solve classic inheritance issues in Clojure? Any examples would be appreciated. Thanks!

not-raspberry23:03:34

juanmp: you can't have a problem with inheritance if you don't use it. It is possible to program without it, even without classes. Most business problems can be solved with a bunch of data transformations and some shared, atomically updated state. Inheritance seems to be more useful when implementing programming languages (see Clojure collections type hierarchy) than writing programs. Clojure has a "value-based" hierarchy system but I'm yet to see it used.

fellshard23:03:57

Inheritance is one approach to solving a more general problem. Clojure - like most other functional languages - prefers smaller composable parts to direct inheritance. This closely resembles the 'delegation over inheritance' recommendation in OO circles.

jmp01:03:39

Thanks guys! Any examples on how to deal with common “inheritance” examples such as the car rental thats usually used in OO?

seancorfield01:03:47

What “car rental” example is that?

jmp12:03:13

A friend of mine that that OOP says there’s a typical test for students to teach them polymorphism and inheritance. I was wondering just any common OOP inheritance issues done in a clojure way

jmp12:03:18

Sorry if I’m unclear. Since I’m new to all this my notions and terms are quite vague.

seancorfield00:03:47

I don’t know what the car rental example would be so it’s hard to say how that would be implemented in Clojure. However, most of the OOP inheritance examples I see — particularly those aimed at students — are just plain wrong. Many times the “issues” are simply that inheritance is the wrong approach to use in the first place.

seancorfield00:03:54

One such “classic example” is Employee / Manager which is often shown as an inheritance relationship, but it’s very easy to show how that breaks down in the real world. It’s a problem better solved with composition: Employee has-a Role, rather than Manager is-a (kind of) Employee. Since Clojure deals with (un-encapsulated, immutable) data separate from functionality, composition is a natural choice there, and either protocols or multi-methods are the natural choice for variability. Those are often complected in OOP languages.

seancorfield00:03:38

For background, I started out in plain ol’ procedural languages, then spent several years with functional languages — before starting to learn OOP in the early 90’s. I built large production systems with OOP for about two decades before coming back to FP… and I do not miss the incidental complexity of those OOP languages and all the “design patterns” that evolved to address shortcomings in OOP and OO languages! 🙂

jmp14:03:10

Thanks @U0HJNJWJH , lots to read on there!

jmp14:03:23

And thanks @seancorfield Oh that makes more sense. One other example I’ve heard is having the 'Pac-man ghosts’. All four of them are units, are ghosts, have a colour, and have movement. But all four of them have different colours and different movement patterns. I had a friend showing me how he would solve this with inheritance in C#. But I wanna figure out how this would be solve with a Clojure approach to it, just making a good solution.

seancorfield17:03:15

Three solutions spring to mind, from "most OOP" to least: 1) use a protocol (Ghost) with a move function and a record for each ghost that implements move 2) use a plain map (or record) for each ghost and a multimethod for move based on the :color element of the map 3) use a plain map for each ghost and have :move as a key whose value is the move function variant.

seancorfield17:03:32

Only 2) specifically ties the color to the move pattern. The other implementations could have color and movement be independent.

seancorfield17:03:46

Refactoring between the three is easy -- it's mostly a matter of where/how you define the move functions -- so I'd probably start with 3) and see whether I needed 2) or 1) later.

jmp22:03:20

Thank you @seancorfield ! This is more what I was looking for, to shift the way of doing things to fit more a functional-clojurey way 😄

tbaldridge23:03:10

@juanmp many "inheritance issues" are caused by OOP. Clojure doesn't really have these issues, since it solves most programming problems in a different way: by putting data first.

tbaldridge23:03:24

@juanmp but if you have some examples, we could probably show you how we solve them differently in Clojure.