#clojure log - Sep 24 2015

The Joy of Clojure
Main Clojure site
Google Group
List of all logged dates

3:36 craftybones: Hello

3:37 kungi: craftybones: good morning

3:39 craftybones: I'm busy solving a few project euler problems and I want to write a thing where I can something like "lein euler run 1"

4:14 neurostorm: My small webapp eats about 650mb memory, can i reduce this?

4:18 Zeta700: When using the threading operators `->` or `->>` I regularily notice how I want to construct expressions that need mixed positioning of arguments.

4:18 Not always the insertion point of a previous expression should be either the first or last arg.

4:18 Is there an idiomatic solution to this? Am I the only one experiencing it? Demo here: https://www.refheap.com/4be8fdf7f7796e0ae1f763b4a

4:26 luma: Zeta700, there's as->

4:36 Zeta700: luma: ok, I will look into that, thx!

4:38 luma: yes good, that’s it, wasn’t aware of that operator.

4:45 rritoch: If 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.

7:20 itruslove: are there any clojure events or meetups in London or to the west of london in the next few weeks?

7:25 ane: itruslove: http://www.londonclojurians.org/dojos.html

7:30 itruslove: thanks ane

8:16 justin_smith: luma: Zeta700 already left, but there is also the fact that ->> can be used inside ->

8:28 craftybones 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.

8:30 also for project euler you can get away with not using lein for deps "java -jar clojure.jar '(load-file "euler1.clj")'"

8:51 djang0nub: I'm trying to decipher some clojure code - can anyone translate this to java?

8:51 (defmethod generator SetMetaData [^SetMetaData desc overrides] 53 (gen/fmap set (gen/list (generator (.elemMetaData desc) 54 overrides))))

8:51 (defmethod generator SetMetaData [^SetMetaData desc overrides] (gen/fmap set (gen/list (generator (.elemMetaData desc) overrides))))\

8:52 justin_smith: djang0nub: defmethod defines a multimethod dispatch (which is very similar to extending an existing class to an interface with only one method)

8:53 it's saying that if the dispatch for generator returns SetMetaData, run this code

8:53 djang0nub: justin_smith: ok.. so defmethod generator SetMetaData means "generator" method is now being defined for SetMetaData input right?

8:53 justin_smith: djang0nub: right, and usually the dispatch is based on class

8:53 djang0nub: ok

8:54 polymorphism

8:54 wowee

8:54 so what is this funny carat

8:54 justin_smith: (but it doesn't have to be :))

8:54 djang0nub: and desc overrides

8:54 justin_smith: that's a hint to the clojure compiler

8:54 ^SetMetaData desc means "desc is probably of concrete type SetMetaData"

8:55 but it doesn't force, coerce, or guarantee this fact

8:55 gilliard: What does it actually do, then?

8:55 justin_smith: tell the compiler what type to expect as the most likely case

8:55 djang0nub: so desc is the first parameter of the function?

8:56 and what is the overrides for

8:56 justin_smith: yes

8:56 djang0nub: overrides is the other argument

8:56 it will break if it gets any number of args other than 2

8:56 djang0nub: ah ok..

8:56 and the .elemMetaData

8:56 what is it dotting

8:56 justin_smith: then it calls gen/fmap with the args set, and (gen/list ... overrides)

8:57 djang0nub: (.foo bar) means call method foo on bar

8:57 as in java methods

8:57 djang0nub: ohh right

8:57 this crazy RPS

8:57 justin_smith: literally (.foo bar) is foo.bar()

8:57 djang0nub: RPN

8:57 ok :)

8:57 justin_smith: and (.foo bar baz) is foo.bar(baz)

8:57 djang0nub: wait is (.foo bar) foo.bar

8:58 or bar.foo()

8:58 bar.foo()?

8:58 justin_smith: djang0nub: argh, just woke up, you are right

8:58 bar.foo()

8:58 or bar.foo - it could be field access

8:58 djang0nub: right.

8:58 makes sense now :D

8:58 ty

8:59 justin_smith: so yeah, (.foo bar) is foo.bar() or foo.bar, and (.foo bar baz) is bar.foo(baz)

8:59 fuck I got it wrong again

8:59 djang0nub: lol

8:59 close

8:59 justin_smith: (.foo bar) is bar.foo()

8:59 djang0nub: this is why lisp scares me

8:59 so many lists

8:59 justin_smith: haha

9:00 djang0nub: in the examples above, the number of parens used is identical between the clojure and java versions

9:00 djang0nub: I got the conversion wrong because I never do the java version

9:01 djang0nub: nice

9:01 I'm trying to decipher this thing to rewrite it in scala

9:01 maybe next i can move to clojure

9:02 justin_smith: my 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

9:03 djang0nub: i was almost asking :)

9:03 and its true

9:03 thankfully we are gradually moving towards defining a "sane subset" of the language

9:03 less implicts and magic

9:03 but its still pretty crazy out there

9:04 must be nice on the lisp side

9:04 justin_smith: I think scala has a niche advantage in that it has a version that looks a lot like normal java

9:04 djang0nub: the language is just done

9:04 yeah.. not just looks, you can code in java

9:04 its a great way to ramp up to more FP type stuff

9:04 justin_smith: with 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

9:05 djang0nub: that sounds great actually

9:05 justin_smith: djang0nub: with clojure you can write half your code in java as well, interop between java and clojure is very easy in both directions actually

9:06 I'm sure you've learned that's not always the case with other jvm languages

9:06 djang0nub: i know, thats why its the only lisp ill consider learning right now :)

9:06 scala interop is definitely impossible from java side

9:07 justin_smith: it'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...)

9:08 djang0nub: hey so does clojure have the capability of implementing crazy functional stuff like type classes or semigroups or dependent types

9:08 more i read scala more i get exposed to these fp ideas, and then peoples horrific hacks to get them to work with scala

9:08 makes me think haskell might be the way to go for pure functional glory

9:08 justin_smith: djang0nub: it's possible but in practice we don't do it, because we are not strongly typed

9:09 djang0nub: omg

9:09 travesty

9:09 justin_smith: though their are monads and lens libraries for those who want to go that route

9:09 *there

9:09 djang0nub: oh now i see why we need the compiler hints

9:09 so its kind of duck typed

9:09 ?

9:10 Hacker-Pro: sg Who is here a good Programmer c++ ?

9:10 justin_smith: kind 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)

9:10 pbx: djang0nub, you might be interested in http://typedclojure.org/

9:11 dstockton: and then http://blog.circleci.com/why-were-no-longer-using-core-typed/

9:11 pbx: and djang0nub, haskell is the path of purity yes

9:12 djang0nub: ty i will read both of those

9:12 yeah haskell is definitely pure.. i think i may need to be a monk in a monastery for a few months before I'm ready

9:13 gtg, thank you for your help @justin_smith

9:14 justin_smith: np

9:47 phaseNi: Is there a shorthand way tocreate a map from a bunch of variables? Like the reverse of the {:keys [a b c d]} destructuring?

9:48 noncom: phaseNi: how would you like it to look like?

9:49 phaseNi: {:var1 var1 :var2 var2 ...}

9:55 jeremyheiler: phaseNi: maybe you just need this?

9:56 ,(let [m1 {:a 1 :b 2 :c 3} [:keys [a b c] :as m2]] m2)

9:56 clojurebot: #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...

9:57 jeremyheiler: ,(let [[:keys [a b c] :as m] {:a 1 :b 2 :c 3}] m)

9:57 clojurebot: #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...

9:57 jeremyheiler: gah

9:57 ,(let [{:keys [a b c] :as m} {:a 1 :b 2 :c 3}] m)

9:57 clojurebot: {:a 1, :b 2, :c 3}

9:59 jeremyheiler: ,(do (def a 1) (meta #'a))

9:59 clojurebot: {:line 0, :column 0, :file "NO_SOURCE_PATH", :name a, :ns #object[clojure.lang.Namespace 0x488e2d88 "sandbox"]}

10:00 jeremyheiler: i don't know how reliable it is to look into the meta for names

10:00 for example, let bindings are not vars, so don't have a meta

10:01 snowell: Clojurebot!

10:01 You're back!

10:05 luma: how about something like this: https://www.refheap.com/109923

10:07 noncom: (inc luma)

10:08 my version is 5 lines, using reduce, yours is only 3 lines and much simpler

10:12 jeremyheiler: nice

10:25 phaseNi: jeremyheiler: that doesn't save me any typing at all

10:25 :P

10:26 jeremyheiler: thanks though

10:33 rvsv: Does 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:33 justin_smith: rvsv: are you providing the same arguments?

10:34 could the side effect of printing in the repl be doing something that prevents a stack overflow?

10:34 rvsv: Yeah, exactly the same steps. After it broke in the application I went to the repl and stepped through with the same steps.

10:34 Hmm, that I'm not sure about. I'm trying to send an email using postal.

10:35 I get a success code in the repl, but in the live application I get the stackoverflow, something for java.io.UnixFileSystem.getBooleanAttributes0

10:36 justin_smith: wait, do you mean stack overflow or stack trace?

10:37 because these are not the same thing at all

10:37 rvsv: It's a stack trace showing a StackOverflowError

10:37 justin_smith: can you paste your stack trace into refheap.com?

10:37 rvsv: sure, just a moment

10:39 https://www.refheap.com/109925

10:40 justin_smith: what's your version of postal?

10:40 CookedGryphon: Hey all. Does anyone know if there's a way to query clojure's keyword pool?

10:41 rvsv: 1.11.4

10:41 CookedGryphon: I 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:43 justin_smith: rvsv: 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:43 that arity of the function calls itself

10:43 and of course that would stack overflow on that line (the line in your error), line 68

10:44 rvsv: if you update to a newer version, that error is fixed

10:44 rvsv: Right, thanks!

10:44 justin_smith: also, that is a hilariour bug

10:44 IMHO

10:45 (not that I haven't done worse myself)

10:45 reiddraper: CookedGryphon: neat idea

10:45 justin_smith: rvsv: 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:46 rvsv: Which of course will blow up.

10:46 justin_smith: right

10:46 rvsv: Ah, you're a lifesaver, I was pulling my hair out

10:46 CookedGryphon: reiddraper: 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:46 rvsv: I'll be more careful about just updating to whatever lein ancient says is new, and test each one.

10:46 justin_smith: rvsv: well, how many times does it have to say "line 68" before you look at what's on line 68 :D

10:47 rvsv: I 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:07 justin_smith: my 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:09 TEttinger: my coworkers would possibly object to my lack of shoes, if I had any coworkers

11:09 justin_smith: they also go around barefoot all the time

11:10 do you use cider while being afraid to update it? maybe you guys would get along great

11:56 RedNifre: Good day everyone.

11:57 Started 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:58 Is it possible to use records without protocols?

12:03 justin_smith: RedNifre: if you don't need protocols, there's not much point to using records

12:03 but yeah, sure, go for it

12:03 RedNifre: oh? what do you mean?

12:03 I thought a protocol was like an interface/abstract class and a record is like an object?

12:03 justin_smith: RedNifre: they have only slim advantages if you are not using protocols

12:04 and the overhead of defining them is usually not going to be worth that advantage

12:04 RedNifre: But what's the alternative? raw maps?

12:04 justin_smith: A record is not like an Object, it's like a Class

12:04 yes, the alternative is just using a hash-map

12:04 RedNifre: and that's what we use instead of records 99% of the time

12:05 RedNifre: ah, right, I meant class. So that I can do (Room. "kitchen" "food" []) like in my example...

12:05 justin_smith: unless we need a feature like protocols

12:05 RedNifre: okay, I'll see if I can rewrite it. Still, what's wrong with my code?

12:05 justin_smith: RedNifre: you can't implement methods that are not part of some protocol

12:06 RedNifre: in clojure a method is always from some protocol or interface, you can't just invent them from scratch

12:06 RedNifre: So... every "class" needs to implement an "interface"?

12:06 justin_smith: no!

12:06 you don't need to implement methods

12:07 in fact we quite like dumb classes that don't have methods other than accessors

12:07 and you can implement as many protocols or interfaces as you like, 0 or more

12:08 RedNifre: So 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:08 justin_smith: RedNifre: also, on line 7, "keyword" is a noop, it might as well not be there because it does nothing

12:09 RedNifre: no, it makes perfect sense to have a protocol only implemented by one record

12:09 but really it might be easier to use a hash-map plus a function

12:09 RedNifre: In 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:09 justin_smith: RedNifre: what does "keyword" supposedly do?

12:10 because right now it might as well be a comment

12:10 RedNifre: It turns line 10 into what you see in line 15... at least that's the idea.

12:10 justin_smith: it's not capable of doing that

12:10 RedNifre: I guess the problem is that one macro can not turn into two separate things?

12:11 justin_smith: RedNifre: a macro must return the thing that gets compiled

12:11 RedNifre: if it's not the last thing in the body of hte macro, it's not returned, so it's a noop

12:11 RedNifre: we have the do form, so you can emit (list 'do keyword ...)

12:12 RedNifre: So 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:12 justin_smith: and that way "keyword" gets compiled in the output

12:12 RedNifre: not without some splicing or concatenation

12:12 RedNifre: clojure expressions can only return one value, there is no clojure semantics for returning two values

12:13 except as separate items in some collection

12:14 craftybones: You won't be able to compile it as {:ha "ha"}. Maybe as {{:ha "ha"}}

12:14 justin_smith: craftybones: he can with concatenation or splicing, eg. ~@

12:14 craftybones: (m "ha") -> {:ha "ha"}

12:14 {(m "ha"} -> {{:ha "ha"}}

12:15 sorry {(m "ha")} -> {{:ha "ha"}}

12:15 justin_smith: `{~@[ha "ha"] ~@nil}

12:15 ,`{~@[ha "ha"] ~@nil}

12:15 clojurebot: #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:15 justin_smith: ,`{~@['ha "ha"] ~@nil}

12:15 clojurebot: {ha "ha"}

12:15 RedNifre: Well, conflating the hash key with the human readable name is a bad idea anyways.

12:16 justin_smith: the ~@nil is there for sad reader-related reasons

12:16 RedNifre: I don't understand anything in that line.

12:16 craftybones: @justin_smith this will evaluate over the containing set?

12:17 justin_smith: RedNifre: on line 9, you create a hash map where the key is a kitchen and the value is a corridor?

12:18 oh never mind, that goes back to the previous misunderstanding

12:18 ,(conj {} [:ha "ha"]) ; RedNifre this is simpler and accomplishes the same thing

12:18 clojurebot: {:ha "ha"}

12:19 justin_smith: RedNifre: so your macro can return a vector of two items, and stuffs can call conj

12:24 RedNifre: I 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:24 justin_smith: RedNifre: 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:24 RedNifre: oh, right.

12:24 which approach of the three looks reasonable?

12:24 Is one of them most idiomatic or is it just personal taste?

12:24 justin_smith: the second is what you would usually see with clojure code

12:25 if I understand what the three options are correctly...

12:25 second one being the thing starting on line 14, anding on line 24

12:26 RedNifre: The 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:31 Yeah, 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:33 xemdetia: RedNifre, I usually use emacs formatting, you should see if there is a good editing mode for whatever you are using

12:33 I don't know of any third party ones

12:33 err

12:33 external tool ones

12:35 RedNifre: So 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:42 wasamasa: living clojure

12:55 hlolli: Can you test for a persitent vector if it has subvectors (subvec? [[ ] [ ] ] ) ;=> true (subvec? [ ]) ;=> false ???

12:59 Well I'll just do (if (vector? (get nth 0)) (blabla))....

13:00 luma: ,(some vector? [[] []])

13:00 clojurebot: true

13:00 luma: ,(some vector? [])

13:00 clojurebot: nil

13:00 hlolli: nice! thanks!!

13:03 RedNifre: why is it println and read-line instead of print-line or readln?

13:05 If 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:28 justin_smith: RedNifre: that works as long as you are not running a repl

13:29 RedNifre: What are you implying? That I should do it differently so that I can debug my program in a REPL?

13:29 Or should I just put a debug command in the loop that stops it so I can continue in the REPL?

13:29 justin_smith: RedNifre: I'm saying that the REPL also wants to read-lines and print and it gets messy

13:30 but if you are directly running the interactive code, with no repl, it will work just fine

13:31 you 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:31 or via unit tests even

13:32 RedNifre: I see.

13:33 I 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:33 justin_smith: that 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:33 RedNifre: you need to call (flush) unless the output ends with a newline

13:33 RedNifre: ah

13:36 xemdetia: wasamasa, that's the first reference I saw to living clojure

13:36 is it good?

13:36 wasamasa: xemdetia: yup

13:36 xemdetia: got a copy here for a colleague

13:37 xemdetia: it's nice for beginners *and* is using idiomatic code

13:37 xemdetia: cool, looks like amazon gets more money today

13:37 (or whoever I end up buying it from)

13:39 wasamasa: this is sort of a preview: https://www.howistart.org/posts/clojure/1

13:39 since it's the same author

13:40 the overall style is comparable

13:42 xemdetia: yeah I found a sample on oreilly's site

13:42 the chapter layout looks good

13:43 wasamasa: if you want something more advanced, hm

13:44 the joy of clojure

13:44 but the second edition

13:46 xemdetia: wasamasa, yes I had heard of that one and that's been on my to-buy list

13:46 wasamasa: the book describes itself as "Drinking from the fire hose"

13:46 I found it too much for someone new to the language

13:47 but maybe you are into that kind of thing :D

13:47 xemdetia: that's what I have heard described here which is fine

13:47 no I just like technical books among clojure

13:47 so if there is a better replacement for the 'beginner clojure' book that's interesting

13:48 replacement is the wrong word

13:48 alternative is what I should have said

13:51 RedNifre: regarding 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:51 justin_smith: RedNifre: clojure itself does not supply readline support

13:52 RedNifre: lein repl does supply readline, you can also use rlwrap to provide readline facilities to any program (including a clojure repl)

13:53 RedNifre: to reload a namespace use (require 'some.ns :reload) or (require 'some.ns :reload-all)

13:53 the latter will recursively load the namespaces that ns loads as well

13:53 (recursively reload that is)

13:53 RedNifre: not 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:53 justin_smith: RedNifre: no, I am saying the proper way to load a script is with require

13:54 RedNifre: so in order to load my.clj I need to read about namespaces first, huh? :)

13:54 wasamasa: that would be a good idea

13:54 justin_smith: RedNifre: or you can use load-file like a neanderthal, that simply unconditionally loads a file

13:54 but you won't get far in clojure without dealing with namespaces

13:55 wasamasa: neanderthals use clojure?

13:55 justin_smith: wasamasa: yes, and they use load-file instead of require, try to keep up

13:55 :P

13:55 RedNifre: sounds good, since I started clojure yesterday I'll do the neanderthal way today and read about namespaces tomorrow.

13:57 justin_smith: RedNifre: 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:00 RedNifre: Is there a difference between a fn that takes no parameters and a quoted piece of code?

14:00 justin_smith: yes

14:01 one of them has been compiled, the other cannot run unless you compile it

14:02 RedNifre: clojure has no interpreter, you can only run things that have been turned into byte code

14:02 this is done by the E (eval) in the REPL

14:03 RedNifre: I'm surprised, I thought "data as code" was a big thing in the Lisp world. So that doesn't work in Clojure?

14:04 justin_smith: RedNifre: it does, data becomes code when you compile it with eval

14:04 I'

14:04 m 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:05 ,'(+ 1 1)

14:05 clojurebot: (+ 1 1)

14:05 justin_smith: ,(eval '(+ 1 1))

14:05 clojurebot: 2

14:05 justin_smith: in normal code, eval is a sign you are a) doing something exceedingly clever or b) making a huge mistake. Often both at once!

14:06 RedNifre: Right... 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:07 So the rule of thumb is "only use quoted code if there is no other way"?

14:07 justin_smith: RedNifre: yes, if possible have functions you can run on the user input / look up in a hash map based on user input

14:07 if you want the full power of clojure to compile code at runtime, you need to use eval on user input

14:08 RedNifre: no, I would never eval user input.

14:08 justin_smith: (well, you need read / read-string first to make compilable tokens, followed by eval, but that's the basic idea)

14:08 RedNifre: What I have now is { "help" '(println "Type 'quit' to quit.")} but {"help" (fn [] (println "type..."))} would work just as well.

14:08 justin_smith: yeah, 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:08 RedNifre: no no no, I don't want the users to enter code.

14:09 justin_smith: right

14:09 so yeah, hash map where values are functions sounds perfect

14:09 they could even optionally take args for more flexibility

14:09 RedNifre: so it's fully compiled to bytecode, right? No data of the code is left at runtime at all? No reflection?

14:10 justin_smith: RedNifre: 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:10 RedNifre: there is potentially reflection

14:10 it's bytecode that reflects (sometimes) on its args

14:11 RedNifre: we have a flag you can turn on for "warn on reflection", which gets triggered when the code for runtime reflection gets compiled

14:11 it's very useful for finding slow code

14:16 RedNifre: Is there a short form for (fn [] ...) ?

14:16 justin_smith: #()

14:17 the first arg is % or %1, second arg is %2, all remaining args after last accessed are %& (one list)

14:17 ,(#(+ % %3) 1 'a 2)

14:17 clojurebot: 3

14:22 RedNifre: Ah, great. But how do I call it? (apply bla) doesn't work and (apply bla []) is clumsy.

14:22 justin_smith: RedNifre: you don't need apply

14:22 just wrap it in parens

14:22 ,(#(print 'OK))

14:22 clojurebot: OK

14:23 RedNifre: oh, right! How could I forget that...

14:23 justin_smith: RedNifre: which leads us to another gotcha: only wrap things in parens when they are functions you want to call

14:24 (or macros or special forms etc. of course)

14:24 RedNifre: hm, in what situations might I accidentally put something into parens that I would not want to call?

14:24 justin_smith: RedNifre: I see it from newcomers all the time

14:24 (if true (1) (2))

14:25 it would work in another language, it errors in clojure, for obvious reasons

14:25 or (if (true) 1 2) - just as bad

14:26 ystael: justin_smith: obviously all non-function values should automatically convert to constant functions when invoked

14:26 justin_smith: ystael: I've often thought that 2 should be the function that calls its arg twice

14:26 ystael: ooh, that's way better

14:27 celwell: Hi, what is the best way to assoc key-value pair (kv) into map (m) iff v is non-nil?

14:28 justin_smith: ,(let [m {} kv [:a 0]] (or (and (val kv) (conj m kv)) m))

14:28 clojurebot: {:a 0}

14:28 justin_smith: that's how I'd do it at least

14:28 celwell: is a let really necessary, that seems wy too messy

14:28 justin_smith: celwell: let was in order to have the names you proposed

14:29 celwell: oh i see, sorry jumped th gun

14:29 justin_smith: ,(or (and (val [:a 0]) (conj {} [:a 0])) {})

14:29 clojurebot: {:a 0}

14:29 justin_smith: there's repetition of the literals when you don't have bindings, of course

14:30 RedNifre: Hm, 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:30 lowed?

14:30 ystael: justin_smith: note that celwell asked for "iff v is non-nil", not "iff v is truthy"

14:30 justin_smith: ,'#("hi"0) ; see the expansion, this is why it doesn't work

14:30 clojurebot: (fn* [] ("hi" 0))

14:30 justin_smith: ystael: true

14:31 ystael: (some (val kv)) of course

14:31 RedNifre: So is there something like #("hi") that expands to (fn [] "hi")

14:31 justin_smith: err

14:31 celwell: I wonder if this would be a good candidate for a macro, like: assoc-if-non-nil

14:31 justin_smith: (constantly "hi")

14:31 ystael: celwell: function, no need for a macro here

14:32 RedNifre: Ah, great.

14:32 oddcully: ,(let [m {:a 1} k :b v nil] (into m (when-not (nil? v) [[k v]])))

14:32 clojurebot: {:a 1}

14:32 oddcully: ey! da bot is back

14:33 justin_smith: oddcully: if you used conj instead of into you could avoid creating a useless vector

14:34 oddcully: that's totally true

14:35 justin_smith: oddcully: but you avoided my silly or / and, which is great

14:35 I forgot that (conj m nil) is just m :)

14:35 celwell: justin_smith: yeah given that it just ignores nils I'm going to go with conj here, thanks

14:35 oddcully: but i bet there is something better than (when-not (nil?))

14:36 gfredericks: ,(doc some?)

14:36 clojurebot: "([x]); Returns true if x is not nil, false otherwise."

14:36 celwell: well I'm just using (when x [:a 1])

14:36 oddcully: (when (some?))

14:36 celwell: truthy is fine for me here

14:36 oddcully: gfredericks: yeah just tried that

14:45 hlolli: Is there any alternative to reset! function for agents? Or other function that updates all keys that match?

14:49 RedNifre: I'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:50 I'm converting to vector in the end because accessing an element in a sequence with (someSequence 3) seems to not work.

14:50 justin_smith: RedNifre: (key (rand-nth (seq m)))

14:50 RedNifre: (v 3) only works with vectors, not other sequence types

14:50 RedNifre: I see, thanks.

14:51 justin_smith: or (rand-nth (keys m)) actually

14:51 ,(rand-nth (keys {:a 0 :b 1 :c 2}))

14:51 clojurebot: :b

14:52 hlolli`: justin, did you see if you or anyone replied my question above, because my internet broke appearantly and cant see last 10 minutes

14:52 justin_smith: hlolli`: no, but reset! doesn't work on agents of course

14:52 and I don't know what you mean by "updates keys that match"

14:54 hlolli`: 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:55 justin_smith: hlolli`: so you want something like (send map merge agent) ?

14:55 ,(def a (agent {}))

14:55 clojurebot: #'sandbox/a

14:55 justin_smith: ,(send a merge {:a 0 :b 1 :c 2 :d 3})

14:55 clojurebot: #object[clojure.lang.Agent 0x796e5ca2 {:status :ready, :val {}}]

14:55 justin_smith: ,@a

14:55 clojurebot: {}

14:55 justin_smith: ,@a

14:55 clojurebot: {}

14:55 justin_smith: I seem to have done this wrong!

14:56 ,(send a #(merge % {:a 0 :b 1 :c 2 :d 3}))

14:56 clojurebot: #object[clojure.lang.Agent 0x796e5ca2 {:status :ready, :val {}}]

14:56 justin_smith: ,@a

14:56 clojurebot: {}

14:56 hlolli`: hmm maybe.. let me check

14:56 this worked for me, seemingly. I think this is excacly what I needed.

14:56 justin_smith: yeah, I think agents may be funky in clojurebot

14:57 so yeah, send a merge, no need for the #() funkiness

14:57 hlolli`: 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:57 justin_smith: hlolli`: makes sense - iirc you change the data frequently, and agents queue up while atoms retry

14:58 so atoms get slower faster when you have frequent update

14:58 and are optimal for rare updates (thanks to their optimism)

14:59 "get slower faster" - me english good!

14:59 hlolli`: 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:59 justin_smith: atoms perform well for rare updates, and slow down with more heavy usage

14:59 hlolli`: makes sense, yeah

15:00 hlolli`: 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:00 but imperative is an easy fit for something like this too

15:01 hlolli`: hmmm... yes I'd like to be more functional, my code is a mix of functional style and imperative style.

15:02 how are you thinking about measure status? Where are you going to store the "status" if not in a atom/agent/ref?

15:02 justin_smith: hlolli`: you could write a function that takes a "state" and returns a "new state" and a measure of music data

15:03 hlolli`: 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:03 and just make a new one each time

15:03 your "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:03 sameerynho: hey folks, is there any websocket lib around for clojure ?

15:04 justin_smith: sameerynho: I have had great luck with sente

15:04 (if you have cljs on the client side)

15:04 sameerynho: justin_smith: i don't have cljs

15:04 hlolli`: ok, yes I see now, was not really getting my head around this first.

15:04 justin_smith: sameerynho: then in that case standard websocket usage as provided by aleph or http-kit should be fine

15:05 hlolli`: 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:06 sameerynho: justin_smith: thanks man

15:06 justin_smith: and 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:07 hlolli`: and since this is csound you could do this all the way down to the granular texture level :)

15:08 hlolli`: 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:09 State "quote on quote". I need to think this more functional for sure.

15:11 justin_smith: hlolli`: 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:14 RedNifre: Well, 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:14 It's most likely not a viable base for a "real" game, the point of this is just to understand some Clojure concepts...

15:15 hlolli`: yes, this is on my todo-list for my programming.

15:15 Well, where's the line of hacking and programming? Don't want to sound too snobby :)

15:16 justin_smith: RedNifre: 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:16 heh

15:17 RedNifre: Yeah, that's planned for next time.

15:18 I 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:18 justin_smith: RedNifre: 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:18 RedNifre: allowing interactive building while exploring in-game

15:18 RedNifre: That's what I also thought about :)

15:18 justin_smith: RedNifre: not tricky at all, I just told you how to do it - make all rooms and objects be arguments to recur

15:19 RedNifre: And it could be linked to an IRC channel or be accessible via XMPP.

15:19 justin_smith: cool, yeah

15:20 RedNifre: yes, 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:20 * RedNifre thinks...

15:20 RedNifre: Well, if Clojure is good at basing new copies on old ones it doesn't matter, right?

15:21 justin_smith: RedNifre: you want ticks in that kind of game. Things change when ticks happen. The recur is the tick.

15:21 right, all you need to do is let the data you don't need fall out of scope

15:21 sameerynho: guys, 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:22 xemdetia: sameerynho, if you have 30M people problems what is your existing infrastructure based on

15:22 RedNifre: I'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:23 sameerynho: xemdetia: it's a new feature, we already have a rails application

15:23 RedNifre: You might like Elixir, it's Erlang with Ruby syntax.

15:24 ...but I guess it depends on the skill set of your team... or are you the only one working on it?

15:24 Which languages do you know already?

15:25 sameerynho: RedNifre: 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:25 RedNifre: Who will maintain it?

15:25 sameerynho: RedNifre: I'm alone, Ruby, python, php, C/C++, lisp perl .....

15:26 RedNifre: so what's your opinion on lisp VS ruby/python/C++?

15:26 xemdetia: sameerynho, 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:26 sameerynho: RedNifre: I like lisp, and also i love Ruby

15:27 xemdetia: I feel go and clojure (or java interop) could probably give you the same power

15:27 RedNifre: Well, Go has nearly no freedom, so it might feel very different from Ruby.

15:27 sameerynho: xemdetia: that's a good point

15:27 augustl: is the problem shardable?

15:27 sameerynho: RedNifre: what do you mean ?

15:27 xemdetia: I think he's just saying go isn't dynamically typed

15:27 and compiled

15:28 RedNifre: In Ruby, you are allowed to do basically everything, including rewriting the standard library at runtime.

15:28 xemdetia: but it also has a more strict project/build structure

15:28 but that's not awful if you are also considering java

15:28 RedNifre: In 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:28 augustl: "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:28 RedNifre: :)

15:29 I'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:30 augustl: I have a very visual relationship with that quote. I can see it before my eyes!

15:30 RedNifre: On 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:30 But if you like Lisp, Clojure is probably a good idea? Not sure.

15:30 augustl: exceptions is a killer feature for me

15:31 xemdetia: I 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:31 oddcully: augustl: in that metaphor perl is a doggy bag. you use it once for something and then throw it away

15:31 augustl: I like to just fail miserably with a loud boom, instead of trying to handle every failure case

15:31 oddcully: haha, nice

15:32 RedNifre: What's your view on using Clojure for small command line scripts?

15:32 sameerynho: Generally I need to know technical advantages of Clojure over Go

15:32 augustl: if I were to do that I'd probably see if I could use nodejs instead of the jvm

15:33 sameerynho: RedNifre: I rather to use ruby with Thor instead of node or jvm

15:33 oddcully: RedNifre: use /bin/sh

15:33 RedNifre: I'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:33 justin_smith: sameerynho: 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:33 RedNifre: oddcully 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:34 justin_smith: people complain about the startup time, but when you take nrepl and lein out of the equation it's not so terrible

15:34 augustl: sameerynho: 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:34 oddcully: RedNifre: the point here is, that even java -cp clojure.jar might not be worth the overhead

15:34 augustl: sameerynho: I have no experience with high scalability myself though..

15:34 sameerynho: justin_smith: Go uses the immutable data structures too , so they both share the "too much ram" problem

15:35 RedNifre: The 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:35 justin_smith: sameerynho: it's a difference of degree - go offers data structures that are mutable as defaults too

15:35 xemdetia: For 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:36 justin_smith: RedNifre: don't rule out trying OCaml as a less demanding Haskell too

15:36 sameerynho: thanks guys

15:36 RedNifre: ...or Idris as a more demanding Haskell ;)

15:36 justin_smith: haha

15:37 oddcully: well tbh ruby (which i never did) or python are quite good for this stuff

15:37 RedNifre: Yeah, I haven't checked out OCaml yet, it's on my list...

15:37 justin_smith: well, you were talking about something dynamically typed, and though OCaml is still strongly typed, it's more friendly to our web-footed friends

15:38 augustl: I need to learn some type theory so I can shake the feeling "those folks know something I don't"

15:38 RedNifre: I 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:38 justin_smith: RedNifre: as long as you don't need high performance, yeah

15:39 RedNifre: Really? I thought Erlang scales... easy to spread across many machines etc... not sure though.

15:39 * oddcully uses groovy if the overhead justifies it

15:39 oddcully: now it's out

15:39 justin_smith: RedNifre: oh, now we have to define performance, "as long as you don't need low latency and high throughput, it is great"

15:39 oddcully: may the bots kick me out of this chanel

15:39 justin_smith: haha

15:40 RedNifre: I 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:40 snowell: I've loved what I've used of Groovy/Grails

15:40 justin_smith: RedNifre: scala comprises 5 languages better than groovy, 5 that are worse, and 5 that nobody has discovered yet

15:40 snowell: If that scala quote is true…that's pretty hilarious

15:40 oddcully: groovy is the java minus ide-generated-bs

15:41 snowell: And plus some nice syntactic sugar

15:41 xemdetia: well 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:41 RedNifre: I use Kotlin for java minus ide-generated-bs :)

15:41 augustl: I 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:41 snowell: I mean, how many languages do you know with an operator called the spaceship operator? :D

15:42 ystael: snowell: originates with Perl, I believe?

15:42 RedNifre: Oh, that one exists outside of Ruby? :D

15:42 oddcully: ystael: perl... we had earlier ;p

15:42 augustl: snowell: and s/map/collect as well as s/reduce/inject

15:42 snowell: Okay, fine, I never said groovy was the ONLY one

15:42 augustl: that's a spaceship thing apparently

15:42 snowell: grumble grumble

15:43 xemdetia: I spend most of my time writing perl right now actually

15:43 works on anything

15:43 oddcully: augustl: once you get the juniors out of the state of using each for _everything_ its even ok with the java folks

15:43 augustl: :D

15:44 current day job is spent writing a groovy project built with gradle that depends on clojure 1.7 for the data structures ;)

15:45 RedNifre: I 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:46 That 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:46 ...not that I could possible introduce Clojure at the office, but at least I could use Clojure for private projects.

15:47 xemdetia: wasn't there a talk about someone who pulled that off like 2 or 3 years ago

15:47 justin_smith: RedNifre: 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:47 I mean we still have a vestigal clojure-CLR too

15:47 RedNifre: Yeah, 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:48 justin_smith: RedNifre: well both versions I know of (using the android vm and using cljs) will tie into the core android functionality just fine...

15:48 RedNifre: How 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:48 justin_smith: clojure has and will always prioritize direct access to host functionality

15:49 RedNifre: calling clojure from java, and java from clojure, are both very very easy

15:49 as 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:50 oddcully: RedNifre: 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:51 RedNifre: In 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:51 oddcully what, Kotlin is done by the same guy as Groovy? Didn't know that.

15:52 I just heard that rumor from somewhere, not sure if it's true.

15:52 oddcully: RedNifre: he now works for jet"brains"

15:52 justin_smith: RedNifre: 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:52 RedNifre: And I didn't say that Scala is great, only that it might be better than Groovy according to rumors, not sure ;)

15:52 oddcully: RedNifre: also this quote is taken out of context

15:52 RedNifre: But yeah, the point of Kotlin is to have a better Java without having the complexity of Scala.

15:53 For 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:53 oddcully: RedNifre: also this quote has been produces way back. groovy nowadays is static typed if you like etc

15:53 RedNifre: Do you have the quote handy? I only heard rumors.

15:57 oddcully: RedNifre: ask the duck for strachan kotlin

15:57 or google if you have to

16:14 hlolli: justin_smith where would you recommend me to look/read for making an functional/recursive state counter

16:15 I 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:30 RedNifre: I 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:33 Are 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:35 (defn old-function "🕸 don't use" [] ...)

16:36 justin_smith: RedNifre: 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:37 RedNifre: so can data structures contain vars/refs/atoms/agents? Or only the other way around?

16:39 justin_smith: they can, but in practice we don't

16:39 RedNifre: so the difference to java's mutable vs immutable is more cultural than technical then?

16:40 justin_smith: hlolli: 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:40 RedNifre: all the objects we use are java objects

16:40 though not always the default ones the lang provides

16:40 for example we use regular strings, there's nothing wrong with them, they are already immutable, they work great

16:40 same with the simple numeric types

16:41 hlolli: ok

16:41 someting like this http://stackoverflow.com/questions/26151509/maintaining-state-within-a-recursive-function-in-clojure ??

16:41 RedNifre: Right, 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:42 xemdetia: hlolli, 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:43 the tick logic comes from synchronizing state across the world, if tick(world) -> a you can schedule something to happen at a + 5

16:43 justin_smith: RedNifre: right

16:44 oddcully: RedNifre: you saying justin_smith is sober?

16:44 hlolli: I 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:44 doing

16:45 within a recursive call would sound like plausable solution

16:45 xemdetia: hlolli, 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:45 since it is recursive

16:45 RedNifre: don'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:46 and 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:47 xemdetia: usually I just do it as more of a queue-styled design where you read from a queue

16:47 and just block until you should tick or schedule ticks in the input queue

16:48 hlolli: ok, 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:48 xemdetia: the 'tick' solution is inventing your own clock

16:48 if that wasn't clear

16:49 justin_smith: hlolli: there's no such thing as time, the function takes "now" as an argument, and returns the future

16:49 hlolli: ok, well, I'm fixed on a clock from csound, you know csound?

16:49 justin_smith: hlolli: right, sure, so follow that clock

16:49 and generate the future as needed

16:50 I'm just saying in the clojure world "time" isn't really a strong thing, you can mess with it as you like

16:50 so you could generate 5 next states, and keep the one you like best

16:50 since the function doesn't have side effects, it just takes an input, generates the next state of things

16:51 RedNifre: The 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:51 hlolli: yes, 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:52 justin_smith: OK, 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:53 recur can take as many args as you like, but it may be easier to pack everything into one hash-map

16:53 jbrhbr: it seems the clojure+programmatic music contingent might be thriving? :)

16:53 justin_smith: jbrhbr: and diversifying!

16:53 jbrhbr: that's how i plan on learning it myself

16:53 for now i'm just lurking here and picking up little things

16:54 it's cool that after so many years, a lisp dialect is finally getting some true respect

16:55 xemdetia: I guess I should ask

16:55 anyone doing clojure work on arm soc?

16:56 hlolli: ok, 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:56 justin_smith: you don't need to mutate when you use recursion properly

16:56 that's kind of the point here

16:57 RedNifre: how do I "mutate" something that is deeply nested? Like (mutate someMap :firstKey :secondKey :thirdKey "The new value I want")?

16:57 justin_smith: when you call recur, the values you provide are the new bindings

16:57 RedNifre: update-in / assoc-in

16:57 RedNifre: thanks, I'll look that up.

16:57 justin_smith: ,(iterate #(update-in % [:a :b :c] (fnil inc 0)) nil)

16:57 turbofail: update-in is probably one of my favorite functions

16:57 clojurebot: (nil {:a {:b {:c 1}}} {:a {:b {:c 2}}} {:a {:b {:c 3}}} {:a {:b {:c 4}}} ...)

16:57 justin_smith: RedNifre: see above

16:58 RedNifre: update-in gets nil, and turns it into {:a {:b {:c 1}}} then turns that into {:a {:b {:c 2}}} etc.

16:58 hlolli: yes yes. ok, I have my red bull next to me, now I program

16:58 (hack)

16:58 justin_smith: heh :)

16:59 RedNifre: okay, I guess I understood half of that.

16:59 How exactly does nil turn into that deeply nested map?

16:59 justin_smith: RedNifre: between that example, the docs, and your own repl, you'll pick it up fast

17:00 RedNifre: 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:00 RedNifre: or is it just a feature of update-in that it treats nils as nested maps of which it can change the value?

17:00 justin_smith: otherwise it just looks in the one that was there already

17:00 RedNifre: ah

17:00 justin_smith: RedNifre: it's a feature of assoc that it turns nil into a map - update-in just leverages that

17:01 ,(assoc nil :a 0)

17:01 clojurebot: {:a 0}

17:01 RedNifre: okay, and (fnil inc 0) creates an inc that treats nil as 0, huh?

17:01 justin_smith: RedNifre: you can probably guess how assoc-in and get-in behavir :)

17:02 RedNifre: exactly!

17:02 * justin_smith loves fnil.

17:02 RedNifre: that's just fantastic :)

17:02 justin_smith: RedNifre: these features turn clojure into a very nice language for one-liners.

17:02 ane: if 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:02 justin_smith: you should see the amazing things we make the bots do around here sometimes.

17:03 ane: just a plain json object vs. what leiningen does

17:03 justin_smith: ane: option c: use slurp / clojure.edn/read-string

17:03 RedNifre: I 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:03 justin_smith: RedNifre: it's addictive

17:04 you might find some other languages a bit disappointing after you get used to clojure

17:04 jbrhbr: justin_smith: are you using it professionally now?

17:04 RedNifre: THough 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:04 ane: racket is cool with rackjure

17:04 justin_smith: jbrhbr: have been for a few years, yes

17:05 jbrhbr: justin_smith: nice, do you mind roughly answering where you work and what you're doing with it?

17:05 justin_smith: RedNifre: or because some co-worker changed the shape of the data and your codebase didn't have unit tests or assertions

17:05 RedNifre: yes, playing so fast and loose with data does have costs sometimes

17:05 but unit tests, prismatic/schema, hammock time (gained because coding is faster...) - these all help

17:06 ane: huh. turns out i designed my dependencies so that component was super easy to plug into it

17:06 justin_smith: jbrhbr: 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:06 jbrhbr: interesting

17:07 thanks

17:07 justin_smith: np

17:08 ane: justin_smith: edn looks like just the thing

17:09 justin_smith: ane: it's easy to output edn with pr-str

17:09 though hand editing is nice if it's a hand edited config

17:09 ane: ,(doc clojure.edn/read-string)

17:09 clojurebot: excusez-moi

17:09 * ane grunts

17:09 justin_smith: ,(require 'clojure.edn)

17:09 clojurebot: nil

17:09 justin_smith: now try

17:09 ane: ,(doc clojure.edn/read-string)

17:09 clojurebot: "([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:10 ane: oh right it returns whatever it reads

17:11 justin_smith: ,(get-in (clojure.edn/read-string "{:a {:b 42}}") [:a :b])

17:11 clojurebot: 42

17:15 RedNifre: I'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:15 justin_smith: ,(macroexpand '(-> foo @bar))

17:15 clojurebot: (clojure.core/deref foo bar)

17:15 justin_smith: RedNifre: does that answer your question?

17:16 RedNifre: ...n-no ..?

17:16 justin_smith: RedNifre: macroexpand shows you how a macro expands - in other words what code actually gets compiled

17:16 RedNifre: ,(macroexpand '(->> foo @bar))

17:16 clojurebot: (clojure.core/deref bar foo)

17:16 justin_smith: ,(macroexpand '@foo)

17:16 clojurebot: (clojure.core/deref foo)

17:17 justin_smith: any closer to getting it?

17:17 RedNifre: does it execute the macros in the wrong order?

17:17 justin_smith: RedNifre: @foo is a reader macro, it expands before your code does

17:17 bja: @ is a reader macro for deref. The code actually reads (->> someMap (deref someKeyword))

17:17 justin_smith: it because (deref foo)

17:17 RedNifre: ,(macroexpand '(->> map (@keyword)))

17:17 clojurebot: ((clojure.core/deref keyword) map)

17:18 ane: try without the parens

17:18 RedNifre: ,(macroexpand '(->> map @keyword))

17:18 clojurebot: (clojure.core/deref keyword map)

17:18 * RedNifre thinks.

17:19 justin_smith: RedNifre: do you understand how ->> and -> operate by rewriting forms?

17:19 ,(macroexpand '(-> a b c))

17:19 clojurebot: (c (b a))

17:20 RedNifre: I 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:20 justin_smith: right, and that's a list transformation, right?

17:20 RedNifre: ...oookay.

17:20 justin_smith: it's rewriting the input form into a different shape before it gets compiled

17:20 RedNifre: ok

17:20 justin_smith: a form being a list you intend to get compiled

17:21 RedNifre: does "form" mean "part of the AST"?

17:21 justin_smith: pretty much - technically there's a real AST that isn't just your clojure intput file

17:21 but close enough for this convo I think :)

17:21 RedNifre: ok :)

17:22 bja: as 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:22 I haven't delved into those waters yet

17:22 justin_smith: bja: 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:23 RedNifre: Now 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:23 ane: precedence

17:23 it's not @keyword getting expanded twice

17:23 justin_smith: RedNifre: (->> map @keyword) => (->> map (deref keyword)) => (deref keyword map)

17:23 ane: it's first ->> and then @keyword

17:23 RedNifre: No, 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:23 ane: or i don't know what the order is

17:24 justin_smith: RedNifre: it's still one thing, one list

17:24 RedNifre: but there's no rule that says ->> can't turn one item in a list into two items in a list

17:24 it's just that you can only return one value to a caller

17:24 RedNifre: ...

17:24 justin_smith: so the compiler calls ->> to get the form to compile (or further macro expand) and gets a single list, that's all

17:25 RedNifre: what 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:25 justin_smith: RedNifre: the restriction would apply to @ - @keyword can't return two separate items

17:25 bja: the value returned from ->> is '(e (d (c (b a)))), which replaces (->> a b c d e)

17:25 justin_smith: RedNifre: the compiler calls a macro, and the macro returns the form for the compiler to expand or compile

17:25 RedNifre: Can you show me the first step of macro expansion for (->> map @keyword)?

17:25 justin_smith: RedNifre: I did above

17:26 RedNifre: (->> map @keyword) => (->> map (deref keyword)) => (deref keyword map)

17:26 that's the step by step

17:26 RedNifre: hm, I feel like I nearly get it... one sec.

17:27 bja: ,(read-string "(->> map @keyword)")

17:27 clojurebot: (->> map (clojure.core/deref keyword))

17:27 Bronsa: RedNifre: @ is a reader macro, reader macros get expanded at read-time, before macroexpansion time

17:27 the macro never sees @

17:27 justin_smith: yay, more details (and of course more accuracy)

17:28 RedNifre: What? So there are (at least) two categories of macros, one type goes first, then the other ones?

17:28 justin_smith: RedNifre: and macros are recursive

17:28 the forms that are returned by one macro can contain other macros to expand

17:28 Bronsa: RedNifre: technically, yes. you can't write reader-macros though, those are reserved by the clojure implementation

17:29 RedNifre: ...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:29 justin_smith: right, unless the macro ended up invoking the reader

17:29 bja: well, 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:29 RedNifre: ...

17:29 justin_smith: which means you are probably doing something very stupid and or something very clever if not both

17:30 bja: actually, more precisely, the current form is macroexpanded recursively until there are no more macros.

17:30 RedNifre: well, 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:30 ...ooooh

17:30 justin_smith: RedNifre: thats just what ->> does

17:30 RedNifre: That's a feature of ->> right, to allow for curried functions?

17:31 justin_smith: it takes each result and inserts it into the next form

17:31 not really currying

17:31 RedNifre: So that I can do (->> 1 (+ 1)) and it turns that into (+ 1 1)?

17:31 justin_smith: but to allow for functions of more than one arg, yes

17:31 right

17:31 RedNifre: Okay, then it all makes sense. :)

17:31 justin_smith: RedNifre: I hope you see what I mean about rewriting forms now

17:32 RedNifre: Oh, 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:32 justin_smith: hehe

17:32 RedNifre: enlighten me :)

17:32 justin_smith: it was my initial answer, about what ->> was doing

17:32 and why it was doing something so dumb - because it's just a dumb list-recombining rule

17:34 RedNifre: ,(let [needs-two (fn [a b] (+ a b))] (->> 1 (needs-two 1)))

17:34 clojurebot: 2

17:34 RedNifre: looks a lot like currying to me

17:35 and what's "->" then?

17:40 justin_smith: RedNifre: but currying creates functions, and ->> creates forms for the compiler to compile

17:40 RedNifre: it just puts the previous form in the second place rather htan last

17:40 RedNifre: ,(macroexpand '(-> a b c))

17:40 clojurebot: (c (b a))

17:40 RedNifre: ,(macroexpand '(->> a b c))

17:40 clojurebot: (c (b a))

17:40 justin_smith: ,(macroexpand '[(-> (a b) (c d) (e f)) (->> (a b) (c d) (e f))])

17:40 clojurebot: [(-> (a b) (c d) (e f)) (->> (a b) (c d) (e f))]

17:40 justin_smith: err

17:40 ,(macroexpand '(-> (a b) (c d) (e f)))

17:40 clojurebot: (e (c (a b) d) f)

17:40 justin_smith: ,(macroexpand '(->> (a b) (c d) (e f)))

17:40 clojurebot: (e f (c d (a b)))

17:40 justin_smith: see how different?

17:40 ->> puts the form into the last place of the next form

17:40 -> puts the form into the second place in the next form

17:40 RedNifre: Okay, I see how ->> is useful for chained functions, but -> looks like a very special case... when do you need that one?

17:40 HoneyBadger: Sup peopel

17:40 *people

17:40 justin_smith: RedNifre: -> is good for map operations where each step returns a new hash-map

17:40 RedNifre: ->> is good for seq operations where each step returns a new sequence

17:40 RedNifre: HoneyBadger second day...

17:40 HoneyBadger: yup

17:40 justin_smith: because the hash-map is usually the second arg, for hash-map functions

17:40 get, assoc, dissoc, get-in, update-in, etc.

17:40 and a sequence is usually the last arg - map, reduce, filter, apply, etc.

17:40 TimMc: I use both quite a bit.

17:40 HoneyBadger: i thought it was first?

17:40 TimMc: HoneyBadger: First arg is second element in the list.

17:40 HoneyBadger: oh i see

17:40 TimMc: (foo 1 2 3) <- the first element is the fn you are calling :-)

17:41 RedNifre: Oh, you mean when you don't do (get 3 someMap), it's when you do (someMap 3)?

17:41 hm...

17:41 can you give me a real world example for ->?

17:42 turbofail: (-> {:foo 0} (update :foo inc) (assoc :bar 10))

17:42 justin_smith: turbofail: clojurebot is here you know

17:42 turbofail: ,(-> {:foo 0} (update :foo inc) (assoc :bar 10))

17:42 clojurebot: {:foo 1, :bar 10}

17:43 RedNifre: ,(macroexpand '(-> {:foo 0} (update :foo inc) (assoc :bar 10)))

17:43 clojurebot: (assoc (update {:foo 0} :foo inc) :bar 10)

17:44 RedNifre: okay, that makes sense, but I thought functional style would put the data last anyway, so why are update and assoc "backwards" like that?

17:45 justin_smith: RedNifre: (assoc m :a x :b y :c z ...) works better if the thing to assoc onto goes first

17:45 or I like it that way, at least

17:49 RedNifre: Is 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:51 For 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:52 thearthur: RedNifre: I don't think such a search exists, the type signature of a whole lot of functions is sequence to sequence for instance

17:52 RedNifre: hm, I guess I can just use a higher order function in an update-in...

17:52 thearthur: would update-in do it for you

17:53 RedNifre: I think it would if I use (fn [innerList] (removeTheElementAndReturnTheRemainingList elem innerList) as the update function...

17:53 justin_smith: RedNifre: Raynes made findfn

17:54 RedNifre: it takes some args and a result, and finds all the functions that give that result for your args

17:54 RedNifre: sounds good.

17:54 do we have a bot for that? :)

17:55 justin_smith: RedNifre: lazybot, which is currently on vacation

17:56 RedNifre: I'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:57 I mean "remove an element from a list and return what's left of the list" is probably on the sheet...

17:59 So, 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:00 turbofail: vectors in clojure aren't fixed-size arrays

18:00 they're much cooler than that

18:00 RedNifre: how do they differ from lists?

18:01 turbofail: you can efficiently append to the end of a vector

18:01 you also get log32(n) random access

18:01 RedNifre: ...but can I also efficiently cut something out in the middle?

18:01 turbofail: no, not with normal vectors. but you can if you use core.rrb-vector

18:01 amalloy: RedNifre: the middle is a bad place. stay away from the middle

18:02 RedNifre: Right, but since lists are linked, removing something from the middle is no problem there, huh? Should be O(listSize/2) = O(n), no?

18:02 turbofail: well if you're counting O(n) as good enough, then you can do that with vectors too

18:02 RedNifre: well, it should also be O(n) for the vector, but a much worse O(n)... hm...

18:03 okay, 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:03 turbofail: not really. it's just about the same, you take a subvector and then re-append the parts you want after it

18:03 RedNifre: now that I think of it, sets could also work.

18:03 turbofail: actually the vector might still be faster

18:04 RedNifre: I guess the set would still be faster, right?

18:04 turbofail: well yeah

18:04 justin_smith: RedNifre: some combination of take / drop / concat is the usual answer, there is no nice way to do it, so nothing built in

18:04 luma: ,(remove #{:bar} '(:foo :bar :baz))

18:04 clojurebot: (:foo :baz)

18:04 justin_smith: (regarding dropping out of the middle of a list)

18:05 luma: remove drops all items that match the predicate, so be sure that only the one you want to drop matches

18:05 amalloy: RedNifre: O(n) for a single operation is pretty bad

18:06 so like, yes, inserting or removing into the middle of a list is O(n), but so is copying the entire list

18:06 turbofail: if what you want is a set then you should use a set

18:06 RedNifre: Depends. 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:07 turbofail: there's sorted sets if you're into that

18:08 anyway n*log(n) isn't much overhead over the linear time it takes to display it regardless

18:14 RedNifre: Wow (remove #{:bar} #{:foo :bar}) is pretty weird...

18:14 Is there a more normal remov like (remove :bar #{:foo :bar}) ?

18:15 turbofail: ,(disj #{:foo :bar} :bar)

18:15 clojurebot: #{:foo}

18:15 turbofail: remove is for sequences

18:17 marshzor: ,(print "hello, world!")

18:17 clojurebot: hello, world!

18:19 RedNifre: ,(print "/me thinks")

18:19 clojurebot: /me thinks

18:19 RedNifre: meh

18:30 Are there tuples or do I just use vectors?

18:31 amalloy: generally just vectors

18:34 RedNifre: so #{} is a set and #() is a lambda, but what is #[] ?

18:35 justin_smith: RedNifre: nothing!

18:35 RedNifre: the repl says something about a reader tag which must be a symbol...

18:35 ...but I guess that's just the REPL's way of saying "nothing!".

18:36 justin_smith: ,(java.util.Date.)

18:36 clojurebot: #inst "2015-09-24T22:37:03.302-00:00"

18:36 averagehat: ,http://symbolhound.com/ is good for these kind of questions usually

18:36 clojurebot: #<RuntimeException java.lang.RuntimeException: Invalid token: http://symbolhound.com/&gt;

18:37 justin_smith: RedNifre: note how dates print

18:37 #inst

18:37 so it's looking for a symbol, and finds [] instead

18:37 RedNifre: ok

18:38 justin_smith: ,(defrecord R [])

18:38 clojurebot: sandbox.R

18:38 justin_smith: ,(R.)

18:38 clojurebot: #sandbox.R{}

18:38 justin_smith: see how that one gets sandbox.R as its symbol

18:38 marshzor: if I have a function that returns a boolean, is there a function that returns the negation of a function?

18:39 justin_smith: marshzor: complement

18:39 marshzor: ty justin_smith

18:39 justin_smith: ,(def not-a-list? (complement list?))

18:39 clojurebot: #'sandbox/not-a-list?

18:40 justin_smith: ,(not-a-list? (range))

18:40 clojurebot: true

18:40 justin_smith: ,(not-a-list? (list))

18:40 clojurebot: false

19:42 jsabeaudry: just watched the talk about om next, I'm excited but I can't find the code, is it not on github.com/omcljs/ ?

19:46 justin_smith: any suggestions for a simple repl function that would test how much heap my clojure process is willing to take from the OS?

19:46 especially if I could incrementally jump up heap usage and see it go up in htop

19:47 RedNifre: When 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:48 justin_smith: RedNifre: your repl should map clojure.pprint/pprint to pprint

19:49 and it does that nicely

20:21 futuro: jsabeaudry: 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:21 but that's only a guess

20:44 jsabeaudry: futuro, oh, that would explain why the om next demo I found include om and nothing named om next, thanks for your help!

21:18 zakwilson: Is 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:51 TEttinger: zakwilson: also try joining their IRC, #clojure-android here on freenode

21:51 there's slack too. I should probably get a slack account.

21:57 zakwilson: TEttinger: I joined the IRC.

22:03 gganley: I'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:17 TEttinger: gganley: IIRC core.logic is an implementation one of the MiniKanren or MuKanren or something prolog-like logic solvers

23:42 flaing: bonjure

Logging service provided by n01se.net