Fork me on GitHub
#clojure
<
2018-10-27
>
Joshua Suskalo03:10:33

The clojure function repeatedly is a function which returns a lazy sequence of explicitly side-effecting functions. As a result, chunking said sequence would violate the principle of least surprise, because more things would be evaluated than are consumed from the sequence. In the case of using something like take-while on the return values of repeatedly, can you expect that the function will only be called enough times to complete that call, and no more?

seancorfield04:10:18

@suskeyhose

user=> (def n (atom 0))
#'user/n
user=> (defn f [] (swap! n inc))
#'user/f
user=> (take-while (fn [q] (< q 10)) (repeatedly f))
(1 2 3 4 5 6 7 8 9)
user=> @n
10

Joshua Suskalo04:10:56

Thanks, that does make it very clear.

seancorfield04:10:14

The REPL is always your friend here. I didn't know for sure, so I tried it.

πŸ‘ 4
Joshua Suskalo04:10:53

Yeah. It was a question that hit me while I was helping someone in another avenue and didn't have access to a repl.

seancorfield04:10:18

Do you have a smart phone? A web browser?

Joshua Suskalo04:10:33

fair enough, I haven't seen if http://repl.it works on mobile

seancorfield04:10:38

There's a ClojureScript REPL app for the iPhone πŸ™‚

Joshua Suskalo04:10:49

Oh, nice, I might try that

Joshua Suskalo04:10:13

oh, wait, that iPhone only?

Joshua Suskalo04:10:39

looks like it. I definitely don't have an iPhone.

seancorfield04:10:03

And the irony is Android is Java-based πŸ™‚

Joshua Suskalo04:10:20

Yeah. It's too bat skummit isn't being maintained.

seancorfield04:10:19

@suskeyhose I can't test on Android but http://repl.it works on MS Edge on iOS, if that's any help πŸ™‚

seancorfield04:10:11

(but I will admit that's a fairly painful way to type in Clojure and try stuff out!)

zilti11:10:20

Given a macro in the form of (defmacro getclass [instance] ...), what's the best way to get the class of the instance given as argument? How do I resolve the argument the way a function does it?

kulminaator11:10:54

javafx has been moved out of the sdk in java11 right ? so what do people do to create custom ui's with clojure then ... back to awt or going for platform dependent openjfx ?

zilti12:10:17

@kulminaator JavaFX is available as a library for all platforms, all you need is to add a dep in Leiningen/Boot/Gradle.

kulminaator12:10:03

i was thinking of tinkering something together that i could just distribute as a standalone app

zilti12:10:25

OpenJDK comes with a tool for exactly that πŸ˜›

kulminaator12:10:09

but then it would have to bundle in the support for each platform or i should make different builds for different platforms πŸ™‚

kulminaator12:10:22

or am i on the wrong track on that one

zilti12:10:37

Well if you want a standalone uberjar, that's possible with the dependency javafx.

zilti12:10:03

With the javapackager on the other hand, yes, you'll have to create one bundle per platform

kulminaator12:10:17

i probably need to do some testing and background research for myself on the topic

zilti12:10:37

The thing at the moment is that the OpenJFX version that's distributed as library, and as part of e.g. Oracle JDK 9/10, is, honestly, crap. At least on Linux. Good luck showing a dialog window that isn't completely weird. JavaFX 8 is fine though.

kulminaator12:10:25

have you tried out how big the uberjar of that would be ?

zilti12:10:02

That actually depends on how much of JavaFX you include. Here's the maven artifacts: https://search.maven.org/search?q=g:org.openjfx

kulminaator12:10:08

doesn't seem too bad though

:dependencies [
                 [org.clojure/clojure "1.9.0"]
                 [org.openjfx/javafx "11" :extension "pom"]
                 [org.openjfx/javafx-controls "11"]
                 ]
results in 13 megabytes jar file

kulminaator12:10:11

i was prepared for worse

pauld13:10:05

My uberjar with all of javafx 11 is 45 MB.

zilti12:10:40

Ah, neat. Yes, that's a very acceptable jar filesize.

kulminaator12:10:12

but i'm not sure if this is "enough" for an app, i just tried to get the first sample that i found to work and i failed miserably

zilti12:10:06

I never got it to work with adding the org.openjfx/javafx dependency, I simply added the individual parts I needed. Apparently at least Boot can't deal with that pom-only dependency, and iirc both Lein and Boot use the same library underneath for deps

zilti12:10:34

javafx-base and javafx-controls should be enough to get up a window with some basic controls

kulminaator13:10:40

thanks for the info πŸ™‚

zilti13:10:17

Hm, now that's a bit embarassing... I'm using Clojure since 8 years now, and I still don't fully get how macros work...

zilti13:10:58

It's probably a simple, silly thing, but could someone look over this?

clojure
(defmacro connect
  "This macro is used to make use of functional interfaces."
  [instance method args & code]
  `(let [functional-method# (first (clojure.lang.Reflector/getMethods ~(class instance) 1 ~(str "set" (camelcase (name method))) false))
         functional-para# (first (.getParameterTypes ^Method functional-method#))]
     (~(symbol (str ".set" (camelcase (name method)))) ~instance
        `(proxy [~functional-para#] []
           (~(symbol (.getName functional-method#)) ~~args
             ~~@code)))))

zilti13:10:36

The problem seems to be at ~~args. It somehow resolves that and reports it can't resolve the symbol, but it's meant to be included verbatim into proxy.

zilti13:10:16

It works when I have only one level of nesting, but I need two here

zilti13:10:38

Hm, alright. Now I'm getting an NPE, but that probably has a different reason... back to debugging

tristefigure13:10:13

I've been using this https://github.com/TristeFigure/shuriken#macroexpand-do every now and then when developing macros

zilti14:10:02

Oh, that looks handy πŸ™‚

zilti14:10:34

Hm interesting. Apparently it's not possible to call Java methods inside a macro.

zilti14:10:59

Ah nah, that's a shuriken bug.

tristefigure14:10:29

forgot to tell you about that part hehe. Can you pm a copy-paste of the bug por favor ?

zilti14:10:44

"java.lang.RuntimeException - Unable to resolve symbol: setOnAction in this context clojure.lang.Util.runtimeException (Util.java:221)"

zilti14:10:56

There's supposed to be a period in front, so .setOnAction

tristefigure17:10:22

Thanks, the bug is fixed !

zilti18:10:10

Also, macroexpand-n seems to be broken, as in it, I guess, first does all the macroexpands, and then starts to print the expansions. So as soon as there's any kind of error in the macro, it becomes useless. macroexpand-do on the other hand prints straight away.

schmee16:10:38

eeeehh…?

user=> :9f22
:9f22

user=> :tag/9f22
Syntax error reading source at (2:0).
Invalid token: :tag/9f22

emccue18:10:46

what happens with ::9f22

schmee18:10:04

seems to work!

emccue18:10:54

curious, away from comp

andy.fingerhut18:10:56

When I type it in a REPL in the user namespace, I get back :user/9f22

andy.fingerhut18:10:18

I believe while that behavior violates the official docs about what is allowed in a keyword (i.e. it leads with a digit), the Clojure/Java implementation does not give an error for it, partly because of the extra compute time required to check it, and now from years of allowing it, partly because of backward compatibility of not breaking library code that relies on it.

andy.fingerhut18:10:31

Oh, but reading back now, yeah, a little incongruous that :user/9f22 is an error on attempting to read it.

andy.fingerhut18:10:41

"Trying things that are not supported in the official docs" combined with "some things are allowed by the implementation despite not being officially supported" are colliding here @schmee

andy.fingerhut18:10:59

I'd recommend strings for storing things like "9f22" especially if you don't need namespaces.

schmee18:10:57

agreed, that’s my usual approach, but in this case I need it as a Datomic attribute

schmee18:10:25

I worked around it by prefixing the values with id_, which is gross but works

andy.fingerhut18:10:10

Sounds like a good idea

andy.fingerhut18:10:49

I found the ticket I was thinking of where they did in 2013 temporarily tighten up the reader to disallow all keywords starting with a digit, then reverted it after it broke things they didn't want to break: https://dev.clojure.org/jira/browse/CLJ-1252

seancorfield19:10:33

I remember that! One of the libraries I worked with a lot broke when they tightened that screw...

thegeez19:10:45

I'm trying to run a socket repl through the clojure cli, how do I enable the jvm-opts from a deps.edn such as this one: https://github.com/seancorfield/dot-clojure/blob/master/deps.edn#L54

thegeez19:10:18

clj -R:socket doesn't seem to work

hiredman19:10:23

you want -A

thegeez19:10:42

hmm

clj -A:socket
Exception in thread "main" java.io.FileNotFoundException: -A:socket (No such file or directory)

hiredman19:10:11

what is the output of clj -h

hiredman19:10:56

my guess is your clj command isn't the new tools.dep enabled one

thegeez19:10:26

...
The dep-opts are used to build the java-opts and classpath:
 -Jopt      Pass opt through in java_opts, ex: -J-Xmx512m
 -Ralias... Concatenated resolve-deps aliases, ex: -R:bench:1.9
 -Calias... Concatenated make-classpath aliases, ex: -C:dev
 -Spath     Compute classpath and echo to stdout only
 -Srepro    Use only the local deps.edn (ignore other config files)
 -Sforce    Force recomputation of the classpath (don't use the cache)
 -Spom      Generate (or update an existing) pom.xml with deps and paths
 -Sverbose  Print important path info to console
...
nothing about -A there

dominicm19:10:03

Very old version. What's -Sdescribe giving you?

hiredman19:10:05

you have an old version

thegeez19:10:49

Thanks, after an update clj -A:socket works

hiredman19:10:50

-A is kind of the "do what I expect" flag, where as otherwise you have to build up what you want using the other flags

hiredman19:10:42

but your older version also didn't have -O which is the flag you would have needed without -A

aengelberg19:10:45

any ideas why this is the case?

user=> (= (java.util.ArrayList.) [])
true
user=> (= #{(java.util.ArrayList.)} #{[]})
false

andy.fingerhut19:10:04

because mutable Java collections still use Java .hashCode as a return value for (hash x)

andy.fingerhut19:10:45

but as of Clojure 1.6 and newer, immutable Clojure collections use a modified hash function for (hash x) that a close associate of your helped develop πŸ™‚

andy.fingerhut19:10:50

This article has some gotchas and details on Clojure = and hash that gives a link to a Clojure ticket raise for this: https://github.com/jafingerhut/thalia/blob/master/doc/other-topics/equality.md

andy.fingerhut19:10:03

The recommendation I've heard (and pass on there) is to avoid using mutable Java collections inside of Clojure collections.

aengelberg19:10:39

according to that, > Clojure's = is true when called with two collections, if: > Both arguments are sets (including Java sets implementing java.util.Set), with = elements, ignoring order.

aengelberg19:10:50

which is true here

andy.fingerhut19:10:52

Ah, and here is the ticket it eventually references: https://dev.clojure.org/jira/browse/CLJ-1372

andy.fingerhut19:10:17

Yes, that is the short summary. Does it also have a bullet list item that mentions the exception about mutable Java collections? If not, I can add one.

andy.fingerhut19:10:40

and those are not "official" docs -- just info collected via OCD

andy.fingerhut19:10:02

I don't see it until much later in the doc, except for a vague hint in the first paragraph. I can make that more explicit.

andy.fingerhut19:10:03

I will add it as the first bullet under "Exceptions, and possible surprises"

aengelberg19:10:19

That would be nice. Not sure how best to word it, maybe "When using non-Clojure collections as keys in a Clojure hash-based collection (map or set), it won't appear equal to a similar collection with Clojure counterparts, due to the difference in hashing behavior. (link to ticket)"

andy.fingerhut19:10:01

Basically, when hash was improved to avoid some bad performance cases due to hash collisions when you use things like short vectors of integers as set elements or hash keys, hash was not changed to add explicit updates for all java.util.Set, java.util.List, etc. classes, too, primarily I believe because it was not easy to see how to do so without making the performance of hash potentially much worse when called for all types.

andy.fingerhut19:10:25

Yeah, there is a whole subsection on it later in the doc, but it belongs early and more prominently.

aengelberg19:10:28

Yep, I do recall those changes, but didn't realize until now that it adversely affected Java collections

andy.fingerhut19:10:12

It didn't change the hash return value for Java collections, but that is the root of the behavior you are seeing (that it didn't)

andy.fingerhut19:10:11

And of course, even though I have edited that document at least 5 times, there is still room for improvement πŸ™‚

aengelberg19:10:24

> avoid using mutable Java collections inside of Clojure collections you're right, but in this particular use case I'm trying to write generative tests for an EDN serializer I've written, which has special behavior for Java collections, coercing them into Clojure collections, and I'm having trouble testing equality during the codec roundtrip.

andy.fingerhut19:10:27

Sounds like the code you are writing to coerce Java collections to Clojure collections will help other people using the lib avoid this issue, but you get to face it head on.

aengelberg19:10:25

Clojure's equality semantics are super magical for the most part, I think the only times I'm ever frustrated by them is writing test cases. Typically, the system under test works perfectly fine but the test cases themselves get false negatives. The main offenders are Java collections, regexes, and NaN. πŸ™‚

andy.fingerhut19:10:17

Given the lib you are writing, you may want to familiarize yourself with other corner cases mentioned in that doc. And if you find anything that isn't listed there, please let me know -- I would like to add it.

andy.fingerhut19:10:42

e.g. open an issue on the repo

aengelberg19:10:16

OK, I will do that if I find more interesting cases.

aengelberg19:10:21

thanks for the tips!

andy.fingerhut19:10:30

you are welcome

andy.fingerhut19:10:05

bullet added as first in the "Exceptions and possible surprises" list, with wording similar to what you suggested.

πŸ‘ 4