#clojure logs

2015-09-24

03:36craftybonesHello
03:37kungicraftybones: good morning
03:39craftybonesI'm busy solving a few project euler problems and I want to write a thing where I can something like "lein euler run 1"
04:14neurostormMy small webapp eats about 650mb memory, can i reduce this?
04:18Zeta700When using the threading operators `->` or `->>` I regularily notice how I want to construct expressions that need mixed positioning of arguments.
04:18Zeta700Not always the insertion point of a previous expression should be either the first or last arg.
04:18Zeta700Is there an idiomatic solution to this? Am I the only one experiencing it? Demo here: https://www.refheap.com/4be8fdf7f7796e0ae1f763b4a
04:26lumaZeta700, there's as->
04:36Zeta700luma: ok, I will look into that, thx!
04:38Zeta700luma: yes good, that’s it, wasn’t aware of that operator.
04:45rritochIf anyone is interested, vinyasa 0.4.2 works on windows. I'm not sure how much time it saves but it will certainly help avoid stress headaches.
07:20itrusloveare there any clojure events or meetups in London or to the west of london in the next few weeks?
07:25aneitruslove: http://www.londonclojurians.org/dojos.html
07:30itruslovethanks ane
08:16justin_smithluma: Zeta700 already left, but there is also the fact that ->> can be used inside ->
08:28justin_smithcraftybones already left, but you can specify a namespace to run as an optional arg to lein run, and put each solution in its own namespace "lein run -m euler1" "lein run -m euler2" etc.
08:30justin_smithalso for project euler you can get away with not using lein for deps "java -jar clojure.jar '(load-file "euler1.clj")'"
08:51djang0nubI'm trying to decipher some clojure code - can anyone translate this to java?
08:51djang0nub(defmethod generator SetMetaData [^SetMetaData desc overrides] 53 (gen/fmap set (gen/list (generator (.elemMetaData desc) 54 overrides))))
08:51djang0nub(defmethod generator SetMetaData [^SetMetaData desc overrides] (gen/fmap set (gen/list (generator (.elemMetaData desc) overrides))))\
08:52justin_smithdjang0nub: defmethod defines a multimethod dispatch (which is very similar to extending an existing class to an interface with only one method)
08:53justin_smithit's saying that if the dispatch for generator returns SetMetaData, run this code
08:53djang0nubjustin_smith: ok.. so defmethod generator SetMetaData means "generator" method is now being defined for SetMetaData input right?
08:53justin_smithdjang0nub: right, and usually the dispatch is based on class
08:53djang0nubok
08:54djang0nubpolymorphism
08:54djang0nubwowee
08:54djang0nubso what is this funny carat
08:54justin_smith(but it doesn't have to be :))
08:54djang0nuband desc overrides
08:54justin_smiththat's a hint to the clojure compiler
08:54justin_smith^SetMetaData desc means "desc is probably of concrete type SetMetaData"
08:55justin_smithbut it doesn't force, coerce, or guarantee this fact
08:55gilliardWhat does it actually do, then?
08:55justin_smithtell the compiler what type to expect as the most likely case
08:55djang0nubso desc is the first parameter of the function?
08:56djang0nuband what is the overrides for
08:56justin_smithyes
08:56justin_smithdjang0nub: overrides is the other argument
08:56justin_smithit will break if it gets any number of args other than 2
08:56djang0nubah ok..
08:56djang0nuband the .elemMetaData
08:56djang0nubwhat is it dotting
08:56justin_smiththen it calls gen/fmap with the args set, and (gen/list ... overrides)
08:57justin_smithdjang0nub: (.foo bar) means call method foo on bar
08:57justin_smithas in java methods
08:57djang0nubohh right
08:57djang0nubthis crazy RPS
08:57justin_smithliterally (.foo bar) is foo.bar()
08:57djang0nubRPN
08:57djang0nubok :)
08:57justin_smithand (.foo bar baz) is foo.bar(baz)
08:57djang0nubwait is (.foo bar) foo.bar
08:58djang0nubor bar.foo()
08:58djang0nubbar.foo()?
08:58justin_smithdjang0nub: argh, just woke up, you are right
08:58justin_smithbar.foo()
08:58justin_smithor bar.foo - it could be field access
08:58djang0nubright.
08:58djang0nubmakes sense now :D
08:58djang0nubty
08:59justin_smithso yeah, (.foo bar) is foo.bar() or foo.bar, and (.foo bar baz) is bar.foo(baz)
08:59justin_smithfuck I got it wrong again
08:59djang0nublol
08:59djang0nubclose
08:59justin_smith(.foo bar) is bar.foo()
08:59djang0nubthis is why lisp scares me
08:59djang0nubso many lists
08:59justin_smithhaha
09:00justin_smithdjang0nub: in the examples above, the number of parens used is identical between the clojure and java versions
09:00justin_smithdjang0nub: I got the conversion wrong because I never do the java version
09:01djang0nubnice
09:01djang0nubI'm trying to decipher this thing to rewrite it in scala
09:01djang0nubmaybe next i can move to clojure
09:02justin_smithmy problem, personally, with scala (I know you didn't actually ask) is that there are at least 10 different scalas, so all will be well until you have to read someone else's code, because everyone uses a different mix of the ten languages implemented
09:03djang0nubi was almost asking :)
09:03djang0nuband its true
09:03djang0nubthankfully we are gradually moving towards defining a "sane subset" of the language
09:03djang0nubless implicts and magic
09:03djang0nubbut its still pretty crazy out there
09:04djang0nubmust be nice on the lisp side
09:04justin_smithI think scala has a niche advantage in that it has a version that looks a lot like normal java
09:04djang0nubthe language is just done
09:04djang0nubyeah.. not just looks, you can code in java
09:04djang0nubits a great way to ramp up to more FP type stuff
09:04justin_smithwith lisps you don't spend much time thinking about the language - every blue moon you learn how to use some wacky macro, but that's about it
09:05djang0nubthat sounds great actually
09:05justin_smithdjang0nub: with clojure you can write half your code in java as well, interop between java and clojure is very easy in both directions actually
09:06justin_smithI'm sure you've learned that's not always the case with other jvm languages
09:06djang0nubi know, thats why its the only lisp ill consider learning right now :)
09:06djang0nubscala interop is definitely impossible from java side
09:07justin_smithit's also tricky from clojure (though I have an unproven suspicion it could potentially be easier from clojure than from java with the right tooling and reflection...)
09:08djang0nubhey so does clojure have the capability of implementing crazy functional stuff like type classes or semigroups or dependent types
09:08djang0nubmore i read scala more i get exposed to these fp ideas, and then peoples horrific hacks to get them to work with scala
09:08djang0nubmakes me think haskell might be the way to go for pure functional glory
09:08justin_smithdjang0nub: it's possible but in practice we don't do it, because we are not strongly typed
09:09djang0nubomg
09:09djang0nubtravesty
09:09justin_smiththough their are monads and lens libraries for those who want to go that route
09:09justin_smith*there
09:09djang0nuboh now i see why we need the compiler hints
09:09djang0nubso its kind of duck typed
09:09djang0nub?
09:10Hacker-Prosg Who is here a good Programmer c++ ?
09:10justin_smithkind of - it's the bare minimum type restriction demanded by the vm, looser than java even (eg. we don't mess with things like generics at all)
09:10pbxdjang0nub, you might be interested in http://typedclojure.org/
09:11dstocktonand then http://blog.circleci.com/why-were-no-longer-using-core-typed/
09:11pbxand djang0nub, haskell is the path of purity yes
09:12djang0nubty i will read both of those
09:12djang0nubyeah haskell is definitely pure.. i think i may need to be a monk in a monastery for a few months before I'm ready
09:13djang0nubgtg, thank you for your help @justin_smith
09:14justin_smithnp
09:47phaseNiIs there a shorthand way tocreate a map from a bunch of variables? Like the reverse of the {:keys [a b c d]} destructuring?
09:48noncomphaseNi: how would you like it to look like?
09:49phaseNi{:var1 var1 :var2 var2 ...}
09:55jeremyheilerphaseNi: maybe you just need this?
09:56jeremyheiler,(let [m1 {:a 1 :b 2 :c 3} [:keys [a b c] :as m2]] m2)
09:56clojurebot#error {\n :cause "let requires an even number of forms in binding vector in sandbox:"\n :via\n [{:type java.lang.IllegalArgumentException\n :message "let requires an even number of forms in binding vector in sandbox:"\n :at [clojure.core$let invokeStatic "core.clj" 4312]}]\n :trace\n [[clojure.core$let invokeStatic "core.clj" 4312]\n [clojure.core$let doInvoke "core.clj" -1]\n [clojure.lang...
09:57jeremyheiler,(let [[:keys [a b c] :as m] {:a 1 :b 2 :c 3}] m)
09:57clojurebot#error {\n :cause "nth not supported on this type: PersistentArrayMap"\n :via\n [{:type java.lang.UnsupportedOperationException\n :message "nth not supported on this type: PersistentArrayMap"\n :at [clojure.lang.RT nthFrom "RT.java" 940]}]\n :trace\n [[clojure.lang.RT nthFrom "RT.java" 940]\n [clojure.lang.RT nth "RT.java" 890]\n [sandbox$eval49 invokeStatic "NO_SOURCE_FILE" 0]\n [sandbox$e...
09:57jeremyheilergah
09:57jeremyheiler,(let [{:keys [a b c] :as m} {:a 1 :b 2 :c 3}] m)
09:57clojurebot{:a 1, :b 2, :c 3}
09:59jeremyheiler,(do (def a 1) (meta #'a))
09:59clojurebot{:line 0, :column 0, :file "NO_SOURCE_PATH", :name a, :ns #object[clojure.lang.Namespace 0x488e2d88 "sandbox"]}
10:00jeremyheileri don't know how reliable it is to look into the meta for names
10:00jeremyheilerfor example, let bindings are not vars, so don't have a meta
10:01snowellClojurebot!
10:01snowellYou're back!
10:05lumahow about something like this: https://www.refheap.com/109923
10:07noncom(inc luma)
10:08noncommy version is 5 lines, using reduce, yours is only 3 lines and much simpler
10:12jeremyheilernice
10:25phaseNijeremyheiler: that doesn't save me any typing at all
10:25phaseNi:P
10:26phaseNijeremyheiler: thanks though
10:33rvsvDoes anyone know why I can use a function from a repl but when I call that same function from the running application (java -jar myapp.jar) it causes a stackOverflowError? I'm totally perplexed by this.
10:33justin_smithrvsv: are you providing the same arguments?
10:34justin_smithcould the side effect of printing in the repl be doing something that prevents a stack overflow?
10:34rvsvYeah, exactly the same steps. After it broke in the application I went to the repl and stepped through with the same steps.
10:34rvsvHmm, that I'm not sure about. I'm trying to send an email using postal.
10:35rvsvI get a success code in the repl, but in the live application I get the stackoverflow, something for java.io.UnixFileSystem.getBooleanAttributes0
10:36justin_smithwait, do you mean stack overflow or stack trace?
10:37justin_smithbecause these are not the same thing at all
10:37rvsvIt's a stack trace showing a StackOverflowError
10:37justin_smithcan you paste your stack trace into refheap.com?
10:37rvsvsure, just a moment
10:39rvsvhttps://www.refheap.com/109925
10:40justin_smithwhat's your version of postal?
10:40CookedGryphonHey all. Does anyone know if there's a way to query clojure's keyword pool?
10:41rvsv1.11.4
10:41CookedGryphonI want to make a test.check generator which before it starts generating random keywords, tries out all the keywords which it knows have popped up somewhere else in the program, and so is more likely to quickly hit special cases/naming conflicts etc.
10:43justin_smithrvsv: this is a bug in that version - easy to see if you read the code https://github.com/drewr/postal/blob/1.11/src/postal/sendmail.clj#L68
10:43justin_smiththat arity of the function calls itself
10:43justin_smithand of course that would stack overflow on that line (the line in your error), line 68
10:44justin_smithrvsv: if you update to a newer version, that error is fixed
10:44rvsvRight, thanks!
10:44justin_smithalso, that is a hilariour bug
10:44justin_smithIMHO
10:45justin_smith(not that I haven't done worse myself)
10:45reiddraperCookedGryphon: neat idea
10:45justin_smithrvsv: that's why it was in UnixFile - on each iteration it tries to find a sendmail program as a file, then it recurs and tries to find one again
10:46rvsvWhich of course will blow up.
10:46justin_smithright
10:46rvsvAh, you're a lifesaver, I was pulling my hair out
10:46CookedGryphonreiddraper: so looking at it, the map is private and I can't see a way to get it out, but I think it's possible to construct without interning, so you wouldn't pollute the pool with randomly generated ones for subsequent runs
10:46rvsvI'll be more careful about just updating to whatever lein ancient says is new, and test each one.
10:46justin_smithrvsv: well, how many times does it have to say "line 68" before you look at what's on line 68 :D
10:47rvsvI know - Before I just scrolled down but when I had to copy it I was like, "hey, this goes on for a really long time". Huh.
11:07justin_smithmy coworkers would rather take my cljc files and split them into separate clj and cljs files than try to upgrade to a newer cider that supports cljc :P
11:09TEttingermy coworkers would possibly object to my lack of shoes, if I had any coworkers
11:09justin_smiththey also go around barefoot all the time
11:10justin_smithdo you use cider while being afraid to update it? maybe you guys would get along great
11:56RedNifreGood day everyone.
11:57RedNifreStarted clojure yesterday and got something working with protocols, records and macros, but my second try fails at line 1 and I don't understand why: https://gist.github.com/RedNifre/d563cc6629c515186f86
11:58RedNifreIs it possible to use records without protocols?
12:03justin_smithRedNifre: if you don't need protocols, there's not much point to using records
12:03justin_smithbut yeah, sure, go for it
12:03RedNifreoh? what do you mean?
12:03RedNifreI thought a protocol was like an interface/abstract class and a record is like an object?
12:03justin_smithRedNifre: they have only slim advantages if you are not using protocols
12:04justin_smithand the overhead of defining them is usually not going to be worth that advantage
12:04RedNifreBut what's the alternative? raw maps?
12:04justin_smithA record is not like an Object, it's like a Class
12:04justin_smithyes, the alternative is just using a hash-map
12:04justin_smithRedNifre: and that's what we use instead of records 99% of the time
12:05RedNifreah, right, I meant class. So that I can do (Room. "kitchen" "food" []) like in my example...
12:05justin_smithunless we need a feature like protocols
12:05RedNifreokay, I'll see if I can rewrite it. Still, what's wrong with my code?
12:05justin_smithRedNifre: you can't implement methods that are not part of some protocol
12:06justin_smithRedNifre: in clojure a method is always from some protocol or interface, you can't just invent them from scratch
12:06RedNifreSo... every "class" needs to implement an "interface"?
12:06justin_smithno!
12:06justin_smithyou don't need to implement methods
12:07justin_smithin fact we quite like dumb classes that don't have methods other than accessors
12:07justin_smithand you can implement as many protocols or interfaces as you like, 0 or more
12:08RedNifreSo protocols only make sense if you have multiple records conforming to it... which means in my case the record is a bad choice because the Room would be the only thing to conform to the protocol?
12:08justin_smithRedNifre: also, on line 7, "keyword" is a noop, it might as well not be there because it does nothing
12:09justin_smithRedNifre: no, it makes perfect sense to have a protocol only implemented by one record
12:09justin_smithbut really it might be easier to use a hash-map plus a function
12:09RedNifreIn line seven, the idea was to have a macro for set entries. The idea is that "stuffs" should contain the same things as "world"
12:09justin_smithRedNifre: what does "keyword" supposedly do?
12:10justin_smithbecause right now it might as well be a comment
12:10RedNifreIt turns line 10 into what you see in line 15... at least that's the idea.
12:10justin_smithit's not capable of doing that
12:10RedNifreI guess the problem is that one macro can not turn into two separate things?
12:11justin_smithRedNifre: a macro must return the thing that gets compiled
12:11justin_smithRedNifre: if it's not the last thing in the body of hte macro, it's not returned, so it's a noop
12:11justin_smithRedNifre: we have the do form, so you can emit (list 'do keyword ...)
12:12RedNifreSo you can't have a macro m that turns (m "ha") into :ha "ha" and use it in a set like {(m "ha")} to compile it as {:ha "ha"}?
12:12justin_smithand that way "keyword" gets compiled in the output
12:12justin_smithRedNifre: not without some splicing or concatenation
12:12justin_smithRedNifre: clojure expressions can only return one value, there is no clojure semantics for returning two values
12:13justin_smithexcept as separate items in some collection
12:14craftybonesYou won't be able to compile it as {:ha "ha"}. Maybe as {{:ha "ha"}}
12:14justin_smithcraftybones: he can with concatenation or splicing, eg. ~@
12:14craftybones(m "ha") -> {:ha "ha"}
12:14craftybones{(m "ha"} -> {{:ha "ha"}}
12:15craftybonessorry {(m "ha")} -> {{:ha "ha"}}
12:15justin_smith`{~@[ha "ha"] ~@nil}
12:15justin_smith,`{~@[ha "ha"] ~@nil}
12:15clojurebot#error {\n :cause "Unable to resolve symbol: ha in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: ha in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6704]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: ha in this context"...
12:15justin_smith,`{~@['ha "ha"] ~@nil}
12:15clojurebot{ha "ha"}
12:15RedNifreWell, conflating the hash key with the human readable name is a bad idea anyways.
12:16justin_smiththe ~@nil is there for sad reader-related reasons
12:16RedNifreI don't understand anything in that line.
12:16craftybones@justin_smith this will evaluate over the containing set?
12:17justin_smithRedNifre: on line 9, you create a hash map where the key is a kitchen and the value is a corridor?
12:18justin_smithoh never mind, that goes back to the previous misunderstanding
12:18justin_smith,(conj {} [:ha "ha"]) ; RedNifre this is simpler and accomplishes the same thing
12:18clojurebot{:ha "ha"}
12:19justin_smithRedNifre: so your macro can return a vector of two items, and stuffs can call conj
12:24RedNifreI took your advice and tried it without records. I have three approaches, can you give me some feedback?: https://gist.github.com/RedNifre/4a18f1d6095a0d19369d
12:24justin_smithRedNifre: except for the bracket placement that looks reasonable, - though you'd want to use defn not def for room-name, room-look, room-inventory
12:24RedNifreoh, right.
12:24RedNifrewhich approach of the three looks reasonable?
12:24RedNifreIs one of them most idiomatic or is it just personal taste?
12:24justin_smiththe second is what you would usually see with clojure code
12:25justin_smithif I understand what the three options are correctly...
12:25justin_smithsecond one being the thing starting on line 14, anding on line 24
12:26RedNifreThe first one is an explicit map, the second one builds the same map with a helper function and the last one is a list where it is informal which thing in the list means what.
12:31RedNifreYeah, with better formatting it looks fine, all without objects. By the way, is there something like gofmt to format the code automatically? I'm currently doing freestyle formatting... https://gist.github.com/RedNifre/c9bd8647827caf33a52a
12:33xemdetiaRedNifre, I usually use emacs formatting, you should see if there is a good editing mode for whatever you are using
12:33xemdetiaI don't know of any third party ones
12:33xemdetiaerr
12:33xemdetiaexternal tool ones
12:35RedNifreSo I finished the clojure part of "seven languages in seven weeks" but it was very brief. Can you recommend a good tutorial/book to learn clojure? I found several books but I can't tell which one might be best.
12:42wasamasaliving clojure
12:55hlolliCan you test for a persitent vector if it has subvectors (subvec? [[ ] [ ] ] ) ;=> true (subvec? [ ]) ;=> false ???
12:59hlolliWell I'll just do (if (vector? (get nth 0)) (blabla))....
13:00luma,(some vector? [[] []])
13:00clojurebottrue
13:00luma,(some vector? [])
13:00clojurebotnil
13:00hlollinice! thanks!!
13:03RedNifrewhy is it println and read-line instead of print-line or readln?
13:05RedNifreIf I want to create a tiny interactive program, do I use loop + recur to communicate with the user through read-line and println? Or what's the best practice here?
13:28justin_smithRedNifre: that works as long as you are not running a repl
13:29RedNifreWhat are you implying? That I should do it differently so that I can debug my program in a REPL?
13:29RedNifreOr should I just put a debug command in the loop that stops it so I can continue in the REPL?
13:29justin_smithRedNifre: I'm saying that the REPL also wants to read-lines and print and it gets messy
13:30justin_smithbut if you are directly running the interactive code, with no repl, it will work just fine
13:31justin_smithyou might want to separate the reading of a line from *in* from the "parsing" you do, in order to test things more easily in a repl
13:31justin_smithor via unit tests even
13:32RedNifreI see.
13:33RedNifreI wanted to print a little prompt with (print ">") right befor the (recur read-line) statement, but the ">" only shows up after I input something. Why is that?
13:33justin_smiththat could be as easy as writing the "parse" function to take an optional input stream (defaulting to *in*) or an alternate source of strings (defaulting to a function that calls read-lin on *in*)
13:33justin_smithRedNifre: you need to call (flush) unless the output ends with a newline
13:33RedNifreah
13:36xemdetiawasamasa, that's the first reference I saw to living clojure
13:36xemdetiais it good?
13:36wasamasaxemdetia: yup
13:36wasamasaxemdetia: got a copy here for a colleague
13:37wasamasaxemdetia: it's nice for beginners *and* is using idiomatic code
13:37xemdetiacool, looks like amazon gets more money today
13:37xemdetia(or whoever I end up buying it from)
13:39wasamasathis is sort of a preview: https://www.howistart.org/posts/clojure/1
13:39wasamasasince it's the same author
13:40wasamasathe overall style is comparable
13:42xemdetiayeah I found a sample on oreilly's site
13:42xemdetiathe chapter layout looks good
13:43wasamasaif you want something more advanced, hm
13:44wasamasathe joy of clojure
13:44wasamasabut the second edition
13:46xemdetiawasamasa, yes I had heard of that one and that's been on my to-buy list
13:46wasamasathe book describes itself as "Drinking from the fire hose"
13:46wasamasaI found it too much for someone new to the language
13:47wasamasabut maybe you are into that kind of thing :D
13:47xemdetiathat's what I have heard described here which is fine
13:47xemdetiano I just like technical books among clojure
13:47xemdetiaso if there is a better replacement for the 'beginner clojure' book that's interesting
13:48xemdetiareplacement is the wrong word
13:48xemdetiaalternative is what I should have said
13:51RedNifreregarding the REPL, when I load a file with clojure -i myscript.clj -r, the history in the REPL gets broken, when I hit the up arrow I just get ^A or something. Why is that? What's the right way to load or reload a script in the REPL?
13:51justin_smithRedNifre: clojure itself does not supply readline support
13:52justin_smithRedNifre: lein repl does supply readline, you can also use rlwrap to provide readline facilities to any program (including a clojure repl)
13:53justin_smithRedNifre: to reload a namespace use (require 'some.ns :reload) or (require 'some.ns :reload-all)
13:53justin_smiththe latter will recursively load the namespaces that ns loads as well
13:53justin_smith(recursively reload that is)
13:53RedNifrenot sure I understand. Are you saying that it's impossible to load a script into the REPL without losing the up-arrow history function?
13:53justin_smithRedNifre: no, I am saying the proper way to load a script is with require
13:54RedNifreso in order to load my.clj I need to read about namespaces first, huh? :)
13:54wasamasathat would be a good idea
13:54justin_smithRedNifre: or you can use load-file like a neanderthal, that simply unconditionally loads a file
13:54justin_smithbut you won't get far in clojure without dealing with namespaces
13:55wasamasaneanderthals use clojure?
13:55justin_smithwasamasa: yes, and they use load-file instead of require, try to keep up
13:55justin_smith:P
13:55RedNifresounds good, since I started clojure yesterday I'll do the neanderthal way today and read about namespaces tomorrow.
13:57justin_smithRedNifre: the thing is, using load-file never really comes up in normal code, and we use require and ns everywhere. But learn at your own pace I guess.
14:00RedNifreIs there a difference between a fn that takes no parameters and a quoted piece of code?
14:00justin_smithyes
14:01justin_smithone of them has been compiled, the other cannot run unless you compile it
14:02justin_smithRedNifre: clojure has no interpreter, you can only run things that have been turned into byte code
14:02justin_smiththis is done by the E (eval) in the REPL
14:03RedNifreI'm surprised, I thought "data as code" was a big thing in the Lisp world. So that doesn't work in Clojure?
14:04justin_smithRedNifre: it does, data becomes code when you compile it with eval
14:04justin_smithI'
14:04justin_smithm saying that it isn't something you can run before that eval step, which is the difference between a quoted list and a function
14:05justin_smith,'(+ 1 1)
14:05clojurebot(+ 1 1)
14:05justin_smith,(eval '(+ 1 1))
14:05clojurebot2
14:05justin_smithin normal code, eval is a sign you are a) doing something exceedingly clever or b) making a huge mistake. Often both at once!
14:06RedNifreRight... so if I have either a map of Strings to fn or String to quoted code and when the user enters a command that is found in the keys of the map I want to run the code... I should use fn as values because eval is only needed when you truly write new code at runtime?
14:07RedNifreSo the rule of thumb is "only use quoted code if there is no other way"?
14:07justin_smithRedNifre: yes, if possible have functions you can run on the user input / look up in a hash map based on user input
14:07justin_smithif you want the full power of clojure to compile code at runtime, you need to use eval on user input
14:08RedNifreno, I would never eval user input.
14:08justin_smith(well, you need read / read-string first to make compilable tokens, followed by eval, but that's the basic idea)
14:08RedNifreWhat I have now is { "help" '(println "Type 'quit' to quit.")} but {"help" (fn [] (println "type..."))} would work just as well.
14:08justin_smithyeah, if you aren't compiling input from the user, you should probably be using a proper function which is compiled before the user interaction starts
14:08RedNifreno no no, I don't want the users to enter code.
14:09justin_smithright
14:09justin_smithso yeah, hash map where values are functions sounds perfect
14:09justin_smiththey could even optionally take args for more flexibility
14:09RedNifreso it's fully compiled to bytecode, right? No data of the code is left at runtime at all? No reflection?
14:10justin_smithRedNifre: and, in fact, a fn will work better than eval, because then you don't have the resource overhead of running the compiler at runtime either
14:10justin_smithRedNifre: there is potentially reflection
14:10justin_smithit's bytecode that reflects (sometimes) on its args
14:11justin_smithRedNifre: we have a flag you can turn on for "warn on reflection", which gets triggered when the code for runtime reflection gets compiled
14:11justin_smithit's very useful for finding slow code
14:16RedNifreIs there a short form for (fn [] ...) ?
14:16justin_smith#()
14:17justin_smiththe first arg is % or %1, second arg is %2, all remaining args after last accessed are %& (one list)
14:17justin_smith,(#(+ % %3) 1 'a 2)
14:17clojurebot3
14:22RedNifreAh, great. But how do I call it? (apply bla) doesn't work and (apply bla []) is clumsy.
14:22justin_smithRedNifre: you don't need apply
14:22justin_smithjust wrap it in parens
14:22justin_smith,(#(print 'OK))
14:22clojurebotOK
14:23RedNifreoh, right! How could I forget that...
14:23justin_smithRedNifre: which leads us to another gotcha: only wrap things in parens when they are functions you want to call
14:24justin_smith(or macros or special forms etc. of course)
14:24RedNifrehm, in what situations might I accidentally put something into parens that I would not want to call?
14:24justin_smithRedNifre: I see it from newcomers all the time
14:24justin_smith(if true (1) (2))
14:25justin_smithit would work in another language, it errors in clojure, for obvious reasons
14:25justin_smithor (if (true) 1 2) - just as bad
14:26ystaeljustin_smith: obviously all non-function values should automatically convert to constant functions when invoked
14:26justin_smithystael: I've often thought that 2 should be the function that calls its arg twice
14:26ystaelooh, that's way better
14:27celwellHi, what is the best way to assoc key-value pair (kv) into map (m) iff v is non-nil?
14:28justin_smith,(let [m {} kv [:a 0]] (or (and (val kv) (conj m kv)) m))
14:28clojurebot{:a 0}
14:28justin_smiththat's how I'd do it at least
14:28celwellis a let really necessary, that seems wy too messy
14:28justin_smithcelwell: let was in order to have the names you proposed
14:29celwelloh i see, sorry jumped th gun
14:29justin_smith,(or (and (val [:a 0]) (conj {} [:a 0])) {})
14:29clojurebot{:a 0}
14:29justin_smiththere's repetition of the literals when you don't have bindings, of course
14:30RedNifreHm, in one case in my map I just have a String and it currently looks like { "special" #(str "not implemented")}. Is there an overhead involved in using str to just return the str? for some reason #("this") doesn't work when I retrieve it with ((get commands "special")) even though it would make sense to me if #("hm") was a function that returned the string. But since the syntax highlighting is messed up for #("hm") I guess that's not al
14:30RedNifrelowed?
14:30ystaeljustin_smith: note that celwell asked for "iff v is non-nil", not "iff v is truthy"
14:30justin_smith,'#("hi"0) ; see the expansion, this is why it doesn't work
14:30clojurebot(fn* [] ("hi" 0))
14:30justin_smithystael: true
14:31justin_smithystael: (some (val kv)) of course
14:31RedNifreSo is there something like #("hi") that expands to (fn [] "hi")
14:31justin_smitherr
14:31celwellI wonder if this would be a good candidate for a macro, like: assoc-if-non-nil
14:31justin_smith(constantly "hi")
14:31ystaelcelwell: function, no need for a macro here
14:32RedNifreAh, great.
14:32oddcully,(let [m {:a 1} k :b v nil] (into m (when-not (nil? v) [[k v]])))
14:32clojurebot{:a 1}
14:32oddcullyey! da bot is back
14:33justin_smithoddcully: if you used conj instead of into you could avoid creating a useless vector
14:34oddcullythat's totally true
14:35justin_smithoddcully: but you avoided my silly or / and, which is great
14:35justin_smithI forgot that (conj m nil) is just m :)
14:35celwelljustin_smith: yeah given that it just ignores nils I'm going to go with conj here, thanks
14:35oddcullybut i bet there is something better than (when-not (nil?))
14:36gfredericks,(doc some?)
14:36clojurebot"([x]); Returns true if x is not nil, false otherwise."
14:36celwellwell I'm just using (when x [:a 1])
14:36oddcully(when (some?))
14:36celwelltruthy is fine for me here
14:36oddcullygfredericks: yeah just tried that
14:45hlolliIs there any alternative to reset! function for agents? Or other function that updates all keys that match?
14:49RedNifreI'm trying to select a random key from a map. This works but is kinda clumsy, how can I improve it?: (->> world (keys) (count) (rand-int) ((vec (keys world))) )
14:50RedNifreI'm converting to vector in the end because accessing an element in a sequence with (someSequence 3) seems to not work.
14:50justin_smithRedNifre: (key (rand-nth (seq m)))
14:50justin_smithRedNifre: (v 3) only works with vectors, not other sequence types
14:50RedNifreI see, thanks.
14:51justin_smithor (rand-nth (keys m)) actually
14:51justin_smith,(rand-nth (keys {:a 0 :b 1 :c 2}))
14:51clojurebot:b
14:52hlolli`justin, did you see if you or anyone replied my question above, because my internet broke appearantly and cant see last 10 minutes
14:52justin_smithhlolli`: no, but reset! doesn't work on agents of course
14:52justin_smithand I don't know what you mean by "updates keys that match"
14:54hlolli`so I have {:a 0 :b 0} map in one agent and {:a 1 :b 1} map in other, and on some point I want all keys to be updated to the other map. And there are many keys that will take a long time to type manually (assoc map :val1 (:val1 @agent)) etc...
14:55justin_smithhlolli`: so you want something like (send map merge agent) ?
14:55justin_smith,(def a (agent {}))
14:55clojurebot#'sandbox/a
14:55justin_smith,(send a merge {:a 0 :b 1 :c 2 :d 3})
14:55clojurebot#object[clojure.lang.Agent 0x796e5ca2 {:status :ready, :val {}}]
14:55justin_smith,@a
14:55clojurebot{}
14:55justin_smith,@a
14:55clojurebot{}
14:55justin_smithI seem to have done this wrong!
14:56justin_smith,(send a #(merge % {:a 0 :b 1 :c 2 :d 3}))
14:56clojurebot#object[clojure.lang.Agent 0x796e5ca2 {:status :ready, :val {}}]
14:56justin_smith,@a
14:56clojurebot{}
14:56hlolli`hmm maybe.. let me check
14:56hlolli`this worked for me, seemingly. I think this is excacly what I needed.
14:56justin_smithyeah, I think agents may be funky in clojurebot
14:57justin_smithso yeah, send a merge, no need for the #() funkiness
14:57hlolli`yes, thanks a million, I still notice that atoms tend to be slower and more unreliable than agents in fast triggering system Im creating.
14:57justin_smithhlolli`: makes sense - iirc you change the data frequently, and agents queue up while atoms retry
14:58justin_smithso atoms get slower faster when you have frequent update
14:58justin_smithand are optimal for rare updates (thanks to their optimism)
14:59justin_smith"get slower faster" - me english good!
14:59hlolli`Im creating a note sequence index, and on some new bars, I need to reset the counter in one do loop same time I evaluate a sound. Only on the first beat (modulo 0) the atoms cant update the index fast enough.
14:59justin_smithatoms perform well for rare updates, and slow down with more heavy usage
14:59justin_smithhlolli`: makes sense, yeah
15:00justin_smithhlolli`: if you wanted to try functional programming you could make a "measure status" data structure, and make your measure rendering function return a brand new measure status
15:00justin_smithbut imperative is an easy fit for something like this too
15:01hlolli`hmmm... yes I'd like to be more functional, my code is a mix of functional style and imperative style.
15:02hlolli`how are you thinking about measure status? Where are you going to store the "status" if not in a atom/agent/ref?
15:02justin_smithhlolli`: you could write a function that takes a "state" and returns a "new state" and a measure of music data
15:03justin_smithhlolli`: you would put all the data that you are currently putting in atoms, agents, and/or refs, and put them in immutable data in "state"
15:03justin_smithand just make a new one each time
15:03justin_smithyour "driver" function (which could be the renderer itself!) would take a state, feed it to the renderer, and get back a measure of music and a new state
15:03sameerynhohey folks, is there any websocket lib around for clojure ?
15:04justin_smithsameerynho: I have had great luck with sente
15:04justin_smith(if you have cljs on the client side)
15:04sameerynhojustin_smith: i don't have cljs
15:04hlolli`ok, yes I see now, was not really getting my head around this first.
15:04justin_smithsameerynho: then in that case standard websocket usage as provided by aleph or http-kit should be fine
15:05justin_smithhlolli`: the only real difference is that your "state of the music" is all in one data structure, and instead of changing it you return a new one. It seems weird, but it's easy after you do it a couple of times, and it has some interesting advantages
15:06sameerynhojustin_smith: thanks man
15:06justin_smithand you can do this fractally - if you have a melody generator it could take an initial "state of the note", and generate a next note and a new "state of the note"
15:07justin_smithhlolli`: and since this is csound you could do this all the way down to the granular texture level :)
15:08hlolli`yes yes, I see. I will defenitely be thinking about this next days. These state functions would have to know about what I want for the future. For example when do I want to restart the note pattern, how long the note pattern is etc.
15:09hlolli`State "quote on quote". I need to think this more functional for sure.
15:11justin_smithhlolli`: right, it's named state but technically is immutable - the general idea though is that all the data you would need to track (the counter that lets you know when to restart, how long the pattern is, etc.) can all be carried in an immutable object, and you can make new ones as needed (and they will share structure with old ones as applicable because clojure's immutable values are nifty like that)
15:14RedNifreWell, day 2 of Clojure is coming to an end: I wrote a tiny text adventure in which you can look around and teleport to other places, if anyonen would like to give me feedback on the code style etc. I'd much appreciate it: https://gist.github.com/RedNifre/53bad0f35b24725de65a
15:14RedNifreIt's most likely not a viable base for a "real" game, the point of this is just to understand some Clojure concepts...
15:15hlolli`yes, this is on my todo-list for my programming.
15:15hlolli`Well, where's the line of hacking and programming? Don't want to sound too snobby :)
15:16justin_smithRedNifre: similarly to what I was mentioning to hlolli`, you could introduce player interaction with the world by making the list of exisitng objects an argument to recur (and optionally making new ones / getting rid of some at the recur point)
15:16justin_smithheh
15:17RedNifreYeah, that's planned for next time.
15:18RedNifreI also want the player to be able to pick things up or move from room to room (currently, the teleportation is basically fake, the player doesn't enter the inventory of the target room). Figuring out how to do all this in an immutable fashon will be tricky I guess.
15:18justin_smithRedNifre: I used to be a mud admin, so this brings back fun memories. You could even have a command that builds a new room or passage and inserts it into the world at the recur point
15:18justin_smithRedNifre: allowing interactive building while exploring in-game
15:18RedNifreThat's what I also thought about :)
15:18justin_smithRedNifre: not tricky at all, I just told you how to do it - make all rooms and objects be arguments to recur
15:19RedNifreAnd it could be linked to an IRC channel or be accessible via XMPP.
15:19justin_smithcool, yeah
15:20RedNifreyes, but I meant what if I had a function (defn move [world player target] ...) could I compute the new world in one step or would I first have to move the player to the new room creating a world where he is in two places at once and then create another world where he is no longer in the first room?
15:20RedNifreWell, if Clojure is good at basing new copies on old ones it doesn't matter, right?
15:21justin_smithRedNifre: you want ticks in that kind of game. Things change when ticks happen. The recur is the tick.
15:21justin_smithright, all you need to do is let the data you don't need fall out of scope
15:21sameerynhoguys, I need to create a websocket server for about 30M people, I stock between Clojure and Go? I'm new to both. Can someone help me to choose one ?
15:22xemdetiasameerynho, if you have 30M people problems what is your existing infrastructure based on
15:22RedNifreI'm new to Clojure and only had a brief look at Go. Go seems easy to pick up and scales, but it is a low level language so I'm afraid that I'll code myself into a corner with it...
15:23sameerynhoxemdetia: it's a new feature, we already have a rails application
15:23RedNifreYou might like Elixir, it's Erlang with Ruby syntax.
15:24RedNifre...but I guess it depends on the skill set of your team... or are you the only one working on it?
15:24RedNifreWhich languages do you know already?
15:25sameerynhoRedNifre: yupe I find that interesting, but the thing is I need to choose the lang wisely for example one of clojure pros is the existance of Immutant and ability to run on JVM
15:25RedNifreWho will maintain it?
15:25sameerynhoRedNifre: I'm alone, Ruby, python, php, C/C++, lisp perl .....
15:26RedNifreso what's your opinion on lisp VS ruby/python/C++?
15:26xemdetiasameerynho, if it's on your own it sounds like you should prototype both and then evaluate which one seems easier to bring to production
15:26sameerynhoRedNifre: I like lisp, and also i love Ruby
15:27xemdetiaI feel go and clojure (or java interop) could probably give you the same power
15:27RedNifreWell, Go has nearly no freedom, so it might feel very different from Ruby.
15:27sameerynhoxemdetia: that's a good point
15:27augustlis the problem shardable?
15:27sameerynhoRedNifre: what do you mean ?
15:27xemdetiaI think he's just saying go isn't dynamically typed
15:27xemdetiaand compiled
15:28RedNifreIn Ruby, you are allowed to do basically everything, including rewriting the standard library at runtime.
15:28xemdetiabut it also has a more strict project/build structure
15:28xemdetiabut that's not awful if you are also considering java
15:28RedNifreIn go you are not even allowed to use tuples because they are too tricky, it forces you to destructure tuple return types right on the spot.
15:28augustl"In ruby, you don't ask where the bathroom is, you just redefine it to be wherever you are and then shit all over everything." :)
15:28RedNifre:)
15:29RedNifreI'm also not sure how Go's "strict typing but without generics except for in the standard library" works in practice. I make excessive use of generics in statically typed languages so I have a hard time imagining how Go would work for me.
15:30augustlI have a very visual relationship with that quote. I can see it before my eyes!
15:30RedNifreOn the other hand, the standard structures in Go are indeed generic so if you stick with Clojures philosophy of avoiding objects in favour of standard structures Go might not be that bad...
15:30RedNifreBut if you like Lisp, Clojure is probably a good idea? Not sure.
15:30augustlexceptions is a killer feature for me
15:31xemdetiaI still think his main question is an infrastructure one, if he already has a legacy codebase whatever gels best seems to be the easiest
15:31oddcullyaugustl: in that metaphor perl is a doggy bag. you use it once for something and then throw it away
15:31augustlI like to just fail miserably with a loud boom, instead of trying to handle every failure case
15:31augustloddcully: haha, nice
15:32RedNifreWhat's your view on using Clojure for small command line scripts?
15:32sameerynhoGenerally I need to know technical advantages of Clojure over Go
15:32augustlif I were to do that I'd probably see if I could use nodejs instead of the jvm
15:33sameerynhoRedNifre: I rather to use ruby with Thor instead of node or jvm
15:33oddcullyRedNifre: use /bin/sh
15:33RedNifreI'm currently working on some nodejs stuff and I can not recommend it at all... or do you mean clojurescript on top of nodejs?
15:33justin_smithsameerynho: clojure has more flexible polymorphism that isn't as clumsy to use, and extensive usage of real threads. Also immutable data structures can save a lot of sanity (while costing a lot of RAM)
15:33RedNifreoddcully nah, I only use /bin/sh for very tiny things. For medium things I currently use Ruby, but Clojure might be cleaner, not sure.
15:34justin_smithpeople complain about the startup time, but when you take nrepl and lein out of the equation it's not so terrible
15:34augustlsameerynho: seems to me that if you need a lot of throughput on the JVM you should probably write it in Java due to the large corpus of knowledge on how to write Java code that makes the JVM behave
15:34oddcullyRedNifre: the point here is, that even java -cp clojure.jar might not be worth the overhead
15:34augustlsameerynho: I have no experience with high scalability myself though..
15:34sameerynhojustin_smith: Go uses the immutable data structures too , so they both share the "too much ram" problem
15:35RedNifreThe thing is that I currently use Haskell for big things and the thought of using a dynamically typed language like Clojure as a replacement is not something I'm comfortable with just yet (but, hey, it's my second day of Clojure, who knows what I'll think tomorrow?)
15:35justin_smithsameerynho: it's a difference of degree - go offers data structures that are mutable as defaults too
15:35xemdetiaFor me command line scripts are designed to be subshell'd and java is just not what you want to use for a find -exec over a lot of stuff
15:36justin_smithRedNifre: don't rule out trying OCaml as a less demanding Haskell too
15:36sameerynhothanks guys
15:36RedNifre...or Idris as a more demanding Haskell ;)
15:36justin_smithhaha
15:37oddcullywell tbh ruby (which i never did) or python are quite good for this stuff
15:37RedNifreYeah, I haven't checked out OCaml yet, it's on my list...
15:37justin_smithwell, you were talking about something dynamically typed, and though OCaml is still strongly typed, it's more friendly to our web-footed friends
15:38augustlI need to learn some type theory so I can shake the feeling "those folks know something I don't"
15:38RedNifreI only had a superficial look at Erlang, but THAT language sounds fantastic in theory: perfectly robust with the feature to swap code without downtime, you declare reboot policies for crashes etc...
15:38justin_smithRedNifre: as long as you don't need high performance, yeah
15:39RedNifreReally? I thought Erlang scales... easy to spread across many machines etc... not sure though.
15:39oddcullynow it's out
15:39justin_smithRedNifre: oh, now we have to define performance, "as long as you don't need low latency and high throughput, it is great"
15:39oddcullymay the bots kick me out of this chanel
15:39justin_smithhaha
15:40RedNifreI never saw Groovy outside of Gradle... didn't the inventor say that if he had known about Scala earlier he wouldn't have invented Groovy because Scala is better in every way?
15:40snowellI've loved what I've used of Groovy/Grails
15:40justin_smithRedNifre: scala comprises 5 languages better than groovy, 5 that are worse, and 5 that nobody has discovered yet
15:40snowellIf that scala quote is true…that's pretty hilarious
15:40oddcullygroovy is the java minus ide-generated-bs
15:41snowellAnd plus some nice syntactic sugar
15:41xemdetiawell I believe it, I mean I was messing around a ton trying to get ECL to work before I really dived into clojure properly
15:41RedNifreI use Kotlin for java minus ide-generated-bs :)
15:41augustlI think it's more balanced than that, it's not that Scala is better in every way according to the creator, but Scala would have been good enough so he wouldn't have bothered
15:41snowellI mean, how many languages do you know with an operator called the spaceship operator? :D
15:42ystaelsnowell: originates with Perl, I believe?
15:42RedNifreOh, that one exists outside of Ruby? :D
15:42oddcullyystael: perl... we had earlier ;p
15:42augustlsnowell: and s/map/collect as well as s/reduce/inject
15:42snowellOkay, fine, I never said groovy was the ONLY one
15:42augustlthat's a spaceship thing apparently
15:42snowellgrumble grumble
15:43xemdetiaI spend most of my time writing perl right now actually
15:43xemdetiaworks on anything
15:43oddcullyaugustl: once you get the juniors out of the state of using each for _everything_ its even ok with the java folks
15:43augustl:D
15:44augustlcurrent day job is spent writing a groovy project built with gradle that depends on clojure 1.7 for the data structures ;)
15:45RedNifreI have to admit that I'm actually coding in Java... yeah, there aren't much options for Android... but the new Kotlin release shouldn't be too far off...
15:46RedNifreThat reminds me: I heard some rumors about Clojure on Android being the next focus of the Clojure project after ClojureScript is mature, is that true?
15:46RedNifre...not that I could possible introduce Clojure at the office, but at least I could use Clojure for private projects.
15:47xemdetiawasn't there a talk about someone who pulled that off like 2 or 3 years ago
15:47justin_smithRedNifre: there are at least a couple of ways of using clojure on android, but I don't know if it will ever be even as high a project for the core cognitect clojure team as cljs is
15:47justin_smithI mean we still have a vestigal clojure-CLR too
15:47RedNifreYeah, but that doesn't count... I mean, you can even write Android apps in Haskell but not really... as long as it's not connected to the standard API in a useful way it's a non-starter.
15:48justin_smithRedNifre: well both versions I know of (using the android vm and using cljs) will tie into the core android functionality just fine...
15:48RedNifreHow well does Clojure mix with Java in practice? Could you put your main business logic in a Clojure jar and use that one in your Java Android app?
15:48justin_smithclojure has and will always prioritize direct access to host functionality
15:49justin_smithRedNifre: calling clojure from java, and java from clojure, are both very very easy
15:49justin_smithas in, you could probably fool someone into thinking no language barrier was being crossed, as long as they didn't look at the source
15:50oddcullyRedNifre: that quote of the author of groovy have to be taken into persepctive: why would he do kotlin now if scala was _so_ awesome?
15:51RedNifreIn that case I guess having a Clojure jar that can access both Android's SQLite database and the network would be enough to make me happy. GUI on Android is very special, there exist a ton of Java libraries to make GUI easier, so having the GUI stuff in Java but handle everything else in Clojure sounds appealing to me.
15:51RedNifreoddcully what, Kotlin is done by the same guy as Groovy? Didn't know that.
15:52RedNifreI just heard that rumor from somewhere, not sure if it's true.
15:52oddcullyRedNifre: he now works for jet"brains"
15:52justin_smithRedNifre: yeah, I think the big hurdles are the extra RAM usage and startup time - but if you can accomodate those, bob's your uncle (and there are some long term plans for those... we'll see)
15:52RedNifreAnd I didn't say that Scala is great, only that it might be better than Groovy according to rumors, not sure ;)
15:52oddcullyRedNifre: also this quote is taken out of context
15:52RedNifreBut yeah, the point of Kotlin is to have a better Java without having the complexity of Scala.
15:53RedNifreFor example, in Scala implicits change everything everywhere while in Kotlin you have to explicitely import them one by one to make them available in your source file.
15:53oddcullyRedNifre: also this quote has been produces way back. groovy nowadays is static typed if you like etc
15:53RedNifreDo you have the quote handy? I only heard rumors.
15:57oddcullyRedNifre: ask the duck for strachan kotlin
15:57oddcullyor google if you have to
16:14hlollijustin_smith where would you recommend me to look/read for making an functional/recursive state counter
16:15hlolliI changed the atom to agents and I have now even more problems, I know the way you suggested for indexes would not include these problems Im havinf
16:30RedNifreI think I heard something about variables only existing on the top level while all datastructures are truly immutable. Is that true or could I stick atoms / refs anywhere I want?
16:33RedNifreAre there annotations? Are there user defined annotations? Do they support unicode? Could I create a "deprecated" annotation that instead of using the word "deprecated" uses the unicode character for spiderweb instead? ;)
16:35RedNifre(defn old-function "🕸 don't use" [] ...)
16:36justin_smithRedNifre: the data structures are immutable, vars, refs, atoms, and agents are mutable containers that hold data-structures, vars are the mutable container that is created when you call def
16:37RedNifreso can data structures contain vars/refs/atoms/agents? Or only the other way around?
16:39justin_smiththey can, but in practice we don't
16:39RedNifreso the difference to java's mutable vs immutable is more cultural than technical then?
16:40justin_smithhlolli: the idea is to have a function that takes a single hash-map holding all the data you need, then returning a pair - a new version of that map, plus your result
16:40justin_smithRedNifre: all the objects we use are java objects
16:40justin_smiththough not always the default ones the lang provides
16:40justin_smithfor example we use regular strings, there's nothing wrong with them, they are already immutable, they work great
16:40justin_smithsame with the simple numeric types
16:41hlolliok
16:41hlollisometing like this http://stackoverflow.com/questions/26151509/maintaining-state-within-a-recursive-function-in-clojure ??
16:41RedNifreRight, but I meant that you can build objects that are as mutable as the ones in java, it's just considered bad style... unlike Haskell were things are really so immutable that you can't possibly have an atom/ref/var inside another data structure.
16:42xemdetiahlolli, I'm not sure if that is clear. I think justin_smith is saying something more the lines of you have f(world) -> world', and world' becomes the new 'true state'
16:43xemdetiathe tick logic comes from synchronizing state across the world, if tick(world) -> a you can schedule something to happen at a + 5
16:43justin_smithRedNifre: right
16:44oddcullyRedNifre: you saying justin_smith is sober?
16:44hlolliI have to hold the state somewhere. I can schedule, but the events have to be muteable and the state has to lookup again and again, that's the nature of what Im goind.
16:44hlollidoing
16:45hlolliwithin a recursive call would sound like plausable solution
16:45xemdetiahlolli, no, the idea is that you pass the world' to the next f, g, h or we and get world'' and time moves forawrd
16:45xemdetiasince it is recursive
16:45RedNifredon't you have a world and an event queue, both sitting in a "pair" which sits in a ref and when a new event comes in you create a new immutable pair with the event in the queue and put that in your ref?
16:46RedNifreand when you feel like it you perform a tick to create a new world and an empty queue in a new pair and put that one in the ref?
16:47xemdetiausually I just do it as more of a queue-styled design where you read from a queue
16:47xemdetiaand just block until you should tick or schedule ticks in the input queue
16:48hlolliok, I think I understand the theory but application Im not really getting my head around. And blocking, I would just if time = time etc..
16:48xemdetiathe 'tick' solution is inventing your own clock
16:48xemdetiaif that wasn't clear
16:49justin_smithhlolli: there's no such thing as time, the function takes "now" as an argument, and returns the future
16:49hlolliok, well, I'm fixed on a clock from csound, you know csound?
16:49justin_smithhlolli: right, sure, so follow that clock
16:49justin_smithand generate the future as needed
16:50justin_smithI'm just saying in the clojure world "time" isn't really a strong thing, you can mess with it as you like
16:50justin_smithso you could generate 5 next states, and keep the one you like best
16:50justin_smithsince the function doesn't have side effects, it just takes an input, generates the next state of things
16:51RedNifreThe trick is that your computation is faster than the time between two csound clock ticks. When this csound clock ticks, you compute the next step and you still have time to space until the next tick. (I don't know what csound is, I guess it calls one of your functions at regular intervals)
16:51hlolliyes, I have the number of samples coming from ksmps and that number ticks, then I send note events calculated from modulo, and then I want to store index from note-patters and rather not use much atoms/agents.
16:52justin_smithOK, so have a function that takes an "initial world" - the state of your orchestra - then have it call recur with the new state of the world, and also send a result to csound
16:53justin_smithrecur can take as many args as you like, but it may be easier to pack everything into one hash-map
16:53jbrhbrit seems the clojure+programmatic music contingent might be thriving? :)
16:53justin_smithjbrhbr: and diversifying!
16:53jbrhbrthat's how i plan on learning it myself
16:53jbrhbrfor now i'm just lurking here and picking up little things
16:54jbrhbrit's cool that after so many years, a lisp dialect is finally getting some true respect
16:55xemdetiaI guess I should ask
16:55xemdetiaanyone doing clojure work on arm soc?
16:56hlolliok, it's almost excacly like I've been doing things so far. I know I can experiment more with recursion, so I will do that first and I can have probably better questions later. But how is recursion on mutable variables, something to watch out for?
16:56justin_smithyou don't need to mutate when you use recursion properly
16:56justin_smiththat's kind of the point here
16:57RedNifrehow do I "mutate" something that is deeply nested? Like (mutate someMap :firstKey :secondKey :thirdKey "The new value I want")?
16:57justin_smithwhen you call recur, the values you provide are the new bindings
16:57justin_smithRedNifre: update-in / assoc-in
16:57RedNifrethanks, I'll look that up.
16:57justin_smith,(iterate #(update-in % [:a :b :c] (fnil inc 0)) nil)
16:57turbofailupdate-in is probably one of my favorite functions
16:57clojurebot(nil {:a {:b {:c 1}}} {:a {:b {:c 2}}} {:a {:b {:c 3}}} {:a {:b {:c 4}}} ...)
16:57justin_smithRedNifre: see above
16:58justin_smithRedNifre: update-in gets nil, and turns it into {:a {:b {:c 1}}} then turns that into {:a {:b {:c 2}}} etc.
16:58hlolliyes yes. ok, I have my red bull next to me, now I program
16:58hlolli(hack)
16:58justin_smithheh :)
16:59RedNifreokay, I guess I understood half of that.
16:59RedNifreHow exactly does nil turn into that deeply nested map?
16:59justin_smithRedNifre: between that example, the docs, and your own repl, you'll pick it up fast
17:00justin_smithRedNifre: I told it to look up the trail of keys [:a :b :c] and at each level it makes a new hash-map if one was not there yet
17:00RedNifreor is it just a feature of update-in that it treats nils as nested maps of which it can change the value?
17:00justin_smithotherwise it just looks in the one that was there already
17:00RedNifreah
17:00justin_smithRedNifre: it's a feature of assoc that it turns nil into a map - update-in just leverages that
17:01justin_smith,(assoc nil :a 0)
17:01clojurebot{:a 0}
17:01RedNifreokay, and (fnil inc 0) creates an inc that treats nil as 0, huh?
17:01justin_smithRedNifre: you can probably guess how assoc-in and get-in behavir :)
17:02justin_smithRedNifre: exactly!
17:02RedNifrethat's just fantastic :)
17:02justin_smithRedNifre: these features turn clojure into a very nice language for one-liners.
17:02aneif i want my app to read configuration files, is it wiser to just keep it in plain json or invent some kind of clojure-based thing that's eval'd on the fly?
17:02justin_smithyou should see the amazing things we make the bots do around here sometimes.
17:03anejust a plain json object vs. what leiningen does
17:03justin_smithane: option c: use slurp / clojure.edn/read-string
17:03RedNifreI love how you can carve a nested path out of a nil to retrieve a blob that looks like 0 to the function you feed it into :)
17:03justin_smithRedNifre: it's addictive
17:04justin_smithyou might find some other languages a bit disappointing after you get used to clojure
17:04jbrhbrjustin_smith: are you using it professionally now?
17:04RedNifreTHough I wonder if you'd miss nil-pointer-exceptions when you end up with an incomplete map because at some point in the middle a nil popped up but got silently turned into some incomplete map.
17:04aneracket is cool with rackjure
17:04justin_smithjbrhbr: have been for a few years, yes
17:05jbrhbrjustin_smith: nice, do you mind roughly answering where you work and what you're doing with it?
17:05justin_smithRedNifre: or because some co-worker changed the shape of the data and your codebase didn't have unit tests or assertions
17:05justin_smithRedNifre: yes, playing so fast and loose with data does have costs sometimes
17:05justin_smithbut unit tests, prismatic/schema, hammock time (gained because coding is faster...) - these all help
17:06anehuh. turns out i designed my dependencies so that component was super easy to plug into it
17:06justin_smithjbrhbr: previously it was brochure websites using clojure as a RAD platform to push out servers, now it's a SAAS doing social graph metrics and reports for enterprise customers
17:06jbrhbrinteresting
17:07jbrhbrthanks
17:07justin_smithnp
17:08anejustin_smith: edn looks like just the thing
17:09justin_smithane: it's easy to output edn with pr-str
17:09justin_smiththough hand editing is nice if it's a hand edited config
17:09ane,(doc clojure.edn/read-string)
17:09clojurebotexcusez-moi
17:09justin_smith,(require 'clojure.edn)
17:09clojurebotnil
17:09justin_smithnow try
17:09ane,(doc clojure.edn/read-string)
17:09clojurebot"([s] [opts s]); Reads one object from the string s. Returns nil when s is nil or empty. Reads data in the edn format (subset of Clojure data): http://edn-format.org opts is a map as per clojure.edn/read"
17:10aneoh right it returns whatever it reads
17:11justin_smith,(get-in (clojure.edn/read-string "{:a {:b 42}}") [:a :b])
17:11clojurebot42
17:15RedNifreI'm not sure I understand ->>... if I have a keyword in a ref, why can't I do (->> someMap @someKeyword), why do I need (->> someMap (@someKeyword)) instead? I mean, (->> someMap :someKeyword) works so I don't understand why the deref needs extra parens?
17:15justin_smith,(macroexpand '(-> foo @bar))
17:15clojurebot(clojure.core/deref foo bar)
17:15justin_smithRedNifre: does that answer your question?
17:16RedNifre...n-no ..?
17:16justin_smithRedNifre: macroexpand shows you how a macro expands - in other words what code actually gets compiled
17:16RedNifre,(macroexpand '(->> foo @bar))
17:16clojurebot(clojure.core/deref bar foo)
17:16justin_smith,(macroexpand '@foo)
17:16clojurebot(clojure.core/deref foo)
17:17justin_smithany closer to getting it?
17:17RedNifredoes it execute the macros in the wrong order?
17:17justin_smithRedNifre: @foo is a reader macro, it expands before your code does
17:17bja@ is a reader macro for deref. The code actually reads (->> someMap (deref someKeyword))
17:17justin_smithit because (deref foo)
17:17RedNifre,(macroexpand '(->> map (@keyword)))
17:17clojurebot((clojure.core/deref keyword) map)
17:18anetry without the parens
17:18RedNifre,(macroexpand '(->> map @keyword))
17:18clojurebot(clojure.core/deref keyword map)
17:19justin_smithRedNifre: do you understand how ->> and -> operate by rewriting forms?
17:19justin_smith,(macroexpand '(-> a b c))
17:19clojurebot(c (b a))
17:20RedNifreI don't understand what "rewriting forms" means exactly, I just stumbled across ->> and thought it turns (->> e d c b a) into (a (b (c (d e))))
17:20justin_smithright, and that's a list transformation, right?
17:20RedNifre...oookay.
17:20justin_smithit's rewriting the input form into a different shape before it gets compiled
17:20RedNifreok
17:20justin_smitha form being a list you intend to get compiled
17:21RedNifredoes "form" mean "part of the AST"?
17:21justin_smithpretty much - technically there's a real AST that isn't just your clojure intput file
17:21justin_smithbut close enough for this convo I think :)
17:21RedNifreok :)
17:22bjaas an aside, there are actually two asts right? one that the official clojure compiler uses, one that tools.analyzer and friends use. or are those the same?
17:22bjaI haven't delved into those waters yet
17:22justin_smithbja: they each create their own representation, and they don't use the same data structures no, so yeah, they have their own thing they call an AST
17:23RedNifreNow hang on... how can (->> map @keyword) possibly expand into (deref keyword map)? That looks like @keyword expanded into two things and I thought that was impossible?
17:23aneprecedence
17:23aneit's not @keyword getting expanded twice
17:23justin_smithRedNifre: (->> map @keyword) => (->> map (deref keyword)) => (deref keyword map)
17:23aneit's first ->> and then @keyword
17:23RedNifreNo, I think earlier it was said that one macro can't expand into two things... when I tried to create a macro that expands both into the key and value of a map?
17:23aneor i don't know what the order is
17:24justin_smithRedNifre: it's still one thing, one list
17:24justin_smithRedNifre: but there's no rule that says ->> can't turn one item in a list into two items in a list
17:24justin_smithit's just that you can only return one value to a caller
17:24RedNifre...
17:24justin_smithso the compiler calls ->> to get the form to compile (or further macro expand) and gets a single list, that's all
17:25RedNifrewhat does "return one value to a caller" mean in the context of macro expansion? I thought the macro sort of returns source code to the source code file?
17:25justin_smithRedNifre: the restriction would apply to @ - @keyword can't return two separate items
17:25bjathe value returned from ->> is '(e (d (c (b a)))), which replaces (->> a b c d e)
17:25justin_smithRedNifre: the compiler calls a macro, and the macro returns the form for the compiler to expand or compile
17:25RedNifreCan you show me the first step of macro expansion for (->> map @keyword)?
17:25justin_smithRedNifre: I did above
17:26justin_smithRedNifre: (->> map @keyword) => (->> map (deref keyword)) => (deref keyword map)
17:26justin_smiththat's the step by step
17:26RedNifrehm, I feel like I nearly get it... one sec.
17:27bja,(read-string "(->> map @keyword)")
17:27clojurebot(->> map (clojure.core/deref keyword))
17:27BronsaRedNifre: @ is a reader macro, reader macros get expanded at read-time, before macroexpansion time
17:27Bronsathe macro never sees @
17:27justin_smithyay, more details (and of course more accuracy)
17:28RedNifreWhat? So there are (at least) two categories of macros, one type goes first, then the other ones?
17:28justin_smithRedNifre: and macros are recursive
17:28justin_smiththe forms that are returned by one macro can contain other macros to expand
17:28BronsaRedNifre: technically, yes. you can't write reader-macros though, those are reserved by the clojure implementation
17:29RedNifre...but I guess a regular macro can't expand to a reader macro because that would be too late since the reader macro expansion phase is already over?
17:29justin_smithright, unless the macro ended up invoking the reader
17:29bjawell, there are places where macros are expanded. the first as when your source code is "read" (things like @ are expanded here). the other common place is as a precursor to execution, macros are evaluated recursively until there are no more macros, then the code is executed.
17:29RedNifre...
17:29justin_smithwhich means you are probably doing something very stupid and or something very clever if not both
17:30bjaactually, more precisely, the current form is macroexpanded recursively until there are no more macros.
17:30RedNifrewell, I understand the first step of the expansion but I don't understand why (->> map (deref keyword)) doesn't turn into ((deref keyword) map)... where do the parens go?
17:30RedNifre...ooooh
17:30justin_smithRedNifre: thats just what ->> does
17:30RedNifreThat's a feature of ->> right, to allow for curried functions?
17:31justin_smithit takes each result and inserts it into the next form
17:31justin_smithnot really currying
17:31RedNifreSo that I can do (->> 1 (+ 1)) and it turns that into (+ 1 1)?
17:31justin_smithbut to allow for functions of more than one arg, yes
17:31justin_smithright
17:31RedNifreOkay, then it all makes sense. :)
17:31justin_smithRedNifre: I hope you see what I mean about rewriting forms now
17:32RedNifreOh, there was so much content in the last few minutes, I'm not sure I remember which point about rewriting forms you refer to now :)
17:32justin_smithhehe
17:32RedNifreenlighten me :)
17:32justin_smithit was my initial answer, about what ->> was doing
17:32justin_smithand why it was doing something so dumb - because it's just a dumb list-recombining rule
17:34RedNifre,(let [needs-two (fn [a b] (+ a b))] (->> 1 (needs-two 1)))
17:34clojurebot2
17:34RedNifrelooks a lot like currying to me
17:35RedNifreand what's "->" then?
17:40justin_smithRedNifre: but currying creates functions, and ->> creates forms for the compiler to compile
17:40justin_smithRedNifre: it just puts the previous form in the second place rather htan last
17:40RedNifre,(macroexpand '(-> a b c))
17:40clojurebot(c (b a))
17:40RedNifre,(macroexpand '(->> a b c))
17:40clojurebot(c (b a))
17:40justin_smith,(macroexpand '[(-> (a b) (c d) (e f)) (->> (a b) (c d) (e f))])
17:40clojurebot[(-> (a b) (c d) (e f)) (->> (a b) (c d) (e f))]
17:40justin_smitherr
17:40justin_smith,(macroexpand '(-> (a b) (c d) (e f)))
17:40clojurebot(e (c (a b) d) f)
17:40justin_smith,(macroexpand '(->> (a b) (c d) (e f)))
17:40clojurebot(e f (c d (a b)))
17:40justin_smithsee how different?
17:40justin_smith->> puts the form into the last place of the next form
17:40justin_smith-> puts the form into the second place in the next form
17:40RedNifreOkay, I see how ->> is useful for chained functions, but -> looks like a very special case... when do you need that one?
17:40HoneyBadgerSup peopel
17:40HoneyBadger*people
17:40justin_smithRedNifre: -> is good for map operations where each step returns a new hash-map
17:40justin_smithRedNifre: ->> is good for seq operations where each step returns a new sequence
17:40RedNifreHoneyBadger second day...
17:40HoneyBadgeryup
17:40justin_smithbecause the hash-map is usually the second arg, for hash-map functions
17:40justin_smithget, assoc, dissoc, get-in, update-in, etc.
17:40justin_smithand a sequence is usually the last arg - map, reduce, filter, apply, etc.
17:40TimMcI use both quite a bit.
17:40HoneyBadgeri thought it was first?
17:40TimMcHoneyBadger: First arg is second element in the list.
17:40HoneyBadgeroh i see
17:40TimMc(foo 1 2 3) <- the first element is the fn you are calling :-)
17:41RedNifreOh, you mean when you don't do (get 3 someMap), it's when you do (someMap 3)?
17:41RedNifrehm...
17:41RedNifrecan you give me a real world example for ->?
17:42turbofail(-> {:foo 0} (update :foo inc) (assoc :bar 10))
17:42justin_smithturbofail: clojurebot is here you know
17:42turbofail,(-> {:foo 0} (update :foo inc) (assoc :bar 10))
17:42clojurebot{:foo 1, :bar 10}
17:43RedNifre,(macroexpand '(-> {:foo 0} (update :foo inc) (assoc :bar 10)))
17:43clojurebot(assoc (update {:foo 0} :foo inc) :bar 10)
17:44RedNifreokay, that makes sense, but I thought functional style would put the data last anyway, so why are update and assoc "backwards" like that?
17:45justin_smithRedNifre: (assoc m :a x :b y :c z ...) works better if the thing to assoc onto goes first
17:45justin_smithor I like it that way, at least
17:49RedNifreIs there something like Hoogle where you can type a function signature and it shows you all functions in the standard library that match it?
17:51RedNifreFor example, now I'm looking for a function that cuts an element out of a list that is nested in some maps, how do I find such a function?
17:52thearthurRedNifre: I don't think such a search exists, the type signature of a whole lot of functions is sequence to sequence for instance
17:52RedNifrehm, I guess I can just use a higher order function in an update-in...
17:52thearthurwould update-in do it for you
17:53RedNifreI think it would if I use (fn [innerList] (removeTheElementAndReturnTheRemainingList elem innerList) as the update function...
17:53justin_smithRedNifre: Raynes made findfn
17:54justin_smithRedNifre: it takes some args and a result, and finds all the functions that give that result for your args
17:54RedNifresounds good.
17:54RedNifredo we have a bot for that? :)
17:55justin_smithRedNifre: lazybot, which is currently on vacation
17:56RedNifreI'll just go with the cheat sheet, that one will probably have most functions I need on it, at least for the next couple of days I guess.
17:57RedNifreI mean "remove an element from a list and return what's left of the list" is probably on the sheet...
17:59RedNifreSo, vectors are like fixed size arrays and lists are linked lists, right? I guess this means that since I want to create rooms that contain things that can move from room to room, the content of a room should be a list, not a vector, huh?
18:00turbofailvectors in clojure aren't fixed-size arrays
18:00turbofailthey're much cooler than that
18:00RedNifrehow do they differ from lists?
18:01turbofailyou can efficiently append to the end of a vector
18:01turbofailyou also get log32(n) random access
18:01RedNifre...but can I also efficiently cut something out in the middle?
18:01turbofailno, not with normal vectors. but you can if you use core.rrb-vector
18:01amalloyRedNifre: the middle is a bad place. stay away from the middle
18:02RedNifreRight, but since lists are linked, removing something from the middle is no problem there, huh? Should be O(listSize/2) = O(n), no?
18:02turbofailwell if you're counting O(n) as good enough, then you can do that with vectors too
18:02RedNifrewell, it should also be O(n) for the vector, but a much worse O(n)... hm...
18:03RedNifreokay, so if I have a list and I want a list that is like the first except that an element in the middle is missing, what should I do?
18:03turbofailnot really. it's just about the same, you take a subvector and then re-append the parts you want after it
18:03RedNifrenow that I think of it, sets could also work.
18:03turbofailactually the vector might still be faster
18:04RedNifreI guess the set would still be faster, right?
18:04turbofailwell yeah
18:04justin_smithRedNifre: some combination of take / drop / concat is the usual answer, there is no nice way to do it, so nothing built in
18:04luma,(remove #{:bar} '(:foo :bar :baz))
18:04clojurebot(:foo :baz)
18:04justin_smith(regarding dropping out of the middle of a list)
18:05lumaremove drops all items that match the predicate, so be sure that only the one you want to drop matches
18:05amalloyRedNifre: O(n) for a single operation is pretty bad
18:06amalloyso like, yes, inserting or removing into the middle of a list is O(n), but so is copying the entire list
18:06turbofailif what you want is a set then you should use a set
18:06RedNifreDepends. If I use a set but want to display it in a deterministic fashon I would have to display it sorted in O(n*log(n))...
18:07turbofailthere's sorted sets if you're into that
18:08turbofailanyway n*log(n) isn't much overhead over the linear time it takes to display it regardless
18:14RedNifreWow (remove #{:bar} #{:foo :bar}) is pretty weird...
18:14RedNifreIs there a more normal remov like (remove :bar #{:foo :bar}) ?
18:15turbofail,(disj #{:foo :bar} :bar)
18:15clojurebot#{:foo}
18:15turbofailremove is for sequences
18:17marshzor,(print "hello, world!")
18:17clojurebothello, world!
18:19RedNifre,(print "/me thinks")
18:19clojurebot/me thinks
18:19RedNifremeh
18:30RedNifreAre there tuples or do I just use vectors?
18:31amalloygenerally just vectors
18:34RedNifreso #{} is a set and #() is a lambda, but what is #[] ?
18:35justin_smithRedNifre: nothing!
18:35RedNifrethe repl says something about a reader tag which must be a symbol...
18:35RedNifre...but I guess that's just the REPL's way of saying "nothing!".
18:36justin_smith,(java.util.Date.)
18:36clojurebot#inst "2015-09-24T22:37:03.302-00:00"
18:36averagehat,http://symbolhound.com/ is good for these kind of questions usually
18:36clojurebot#<RuntimeException java.lang.RuntimeException: Invalid token: http://symbolhound.com/&gt;
18:37justin_smithRedNifre: note how dates print
18:37justin_smith#inst
18:37justin_smithso it's looking for a symbol, and finds [] instead
18:37RedNifreok
18:38justin_smith,(defrecord R [])
18:38clojurebotsandbox.R
18:38justin_smith,(R.)
18:38clojurebot#sandbox.R{}
18:38justin_smithsee how that one gets sandbox.R as its symbol
18:38marshzorif I have a function that returns a boolean, is there a function that returns the negation of a function?
18:39justin_smithmarshzor: complement
18:39marshzorty justin_smith
18:39justin_smith,(def not-a-list? (complement list?))
18:39clojurebot#'sandbox/not-a-list?
18:40justin_smith,(not-a-list? (range))
18:40clojurebottrue
18:40justin_smith,(not-a-list? (list))
18:40clojurebotfalse
19:42jsabeaudryjust watched the talk about om next, I'm excited but I can't find the code, is it not on github.com/omcljs/ ?
19:46justin_smithany suggestions for a simple repl function that would test how much heap my clojure process is willing to take from the OS?
19:46justin_smithespecially if I could incrementally jump up heap usage and see it go up in htop
19:47RedNifreWhen I print some data structures, is there a way to format the output? Currently it's all in one line which makes it hard to read large data structures.
19:48justin_smithRedNifre: your repl should map clojure.pprint/pprint to pprint
19:49justin_smithand it does that nicely
20:21futurojsabeaudry: I don't have a definitive answer, but it searching through the codebase brings up references to 'next', so my guess is that it's been merged in
20:21futurobut that's only a guess
20:44jsabeaudryfuturo, oh, that would explain why the om next demo I found include om and nothing named om next, thanks for your help!
21:18zakwilsonIs http://clojure-android.info/ a reasonable place to get started for using Clojure on Android, or is there somewhere else I should be looking?
21:51TEttingerzakwilson: also try joining their IRC, #clojure-android here on freenode
21:51TEttingerthere's slack too. I should probably get a slack account.
21:57zakwilsonTEttinger: I joined the IRC.
22:03gganleyI'm looking into writing a SAT solver. I started in haskell using Lens' but I also use clojure in my spare time and I remembered about core.logic. Is that something that would be useful for doing things like unit propogation and other search space optimizations or is it used for other things. I have no prior knowlage about the library and I'm currently looking it up
22:17TEttingergganley: IIRC core.logic is an implementation one of the MiniKanren or MuKanren or something prolog-like logic solvers
23:42flaingbonjure