2015-09-24
| 03:36 | craftybones | Hello |
| 03:37 | kungi | craftybones: good morning |
| 03: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" |
| 04:14 | neurostorm | My small webapp eats about 650mb memory, can i reduce this? |
| 04:18 | Zeta700 | When using the threading operators `->` or `->>` I regularily notice how I want to construct expressions that need mixed positioning of arguments. |
| 04:18 | Zeta700 | Not always the insertion point of a previous expression should be either the first or last arg. |
| 04:18 | Zeta700 | Is there an idiomatic solution to this? Am I the only one experiencing it? Demo here: https://www.refheap.com/4be8fdf7f7796e0ae1f763b4a |
| 04:26 | luma | Zeta700, there's as-> |
| 04:36 | Zeta700 | luma: ok, I will look into that, thx! |
| 04:38 | Zeta700 | luma: yes good, that’s it, wasn’t aware of that operator. |
| 04: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. |
| 07:20 | itruslove | are there any clojure events or meetups in London or to the west of london in the next few weeks? |
| 07:25 | ane | itruslove: http://www.londonclojurians.org/dojos.html |
| 07:30 | itruslove | thanks ane |
| 08:16 | justin_smith | luma: Zeta700 already left, but there is also the fact that ->> can be used inside -> |
| 08:28 | justin_smith | 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. |
| 08:30 | justin_smith | also for project euler you can get away with not using lein for deps "java -jar clojure.jar '(load-file "euler1.clj")'" |
| 08:51 | djang0nub | I'm trying to decipher some clojure code - can anyone translate this to java? |
| 08:51 | djang0nub | (defmethod generator SetMetaData [^SetMetaData desc overrides] 53 (gen/fmap set (gen/list (generator (.elemMetaData desc) 54 overrides)))) |
| 08:51 | djang0nub | (defmethod generator SetMetaData [^SetMetaData desc overrides] (gen/fmap set (gen/list (generator (.elemMetaData desc) overrides))))\ |
| 08: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) |
| 08:53 | justin_smith | it's saying that if the dispatch for generator returns SetMetaData, run this code |
| 08:53 | djang0nub | justin_smith: ok.. so defmethod generator SetMetaData means "generator" method is now being defined for SetMetaData input right? |
| 08:53 | justin_smith | djang0nub: right, and usually the dispatch is based on class |
| 08:53 | djang0nub | ok |
| 08:54 | djang0nub | polymorphism |
| 08:54 | djang0nub | wowee |
| 08:54 | djang0nub | so what is this funny carat |
| 08:54 | justin_smith | (but it doesn't have to be :)) |
| 08:54 | djang0nub | and desc overrides |
| 08:54 | justin_smith | that's a hint to the clojure compiler |
| 08:54 | justin_smith | ^SetMetaData desc means "desc is probably of concrete type SetMetaData" |
| 08:55 | justin_smith | but it doesn't force, coerce, or guarantee this fact |
| 08:55 | gilliard | What does it actually do, then? |
| 08:55 | justin_smith | tell the compiler what type to expect as the most likely case |
| 08:55 | djang0nub | so desc is the first parameter of the function? |
| 08:56 | djang0nub | and what is the overrides for |
| 08:56 | justin_smith | yes |
| 08:56 | justin_smith | djang0nub: overrides is the other argument |
| 08:56 | justin_smith | it will break if it gets any number of args other than 2 |
| 08:56 | djang0nub | ah ok.. |
| 08:56 | djang0nub | and the .elemMetaData |
| 08:56 | djang0nub | what is it dotting |
| 08:56 | justin_smith | then it calls gen/fmap with the args set, and (gen/list ... overrides) |
| 08:57 | justin_smith | djang0nub: (.foo bar) means call method foo on bar |
| 08:57 | justin_smith | as in java methods |
| 08:57 | djang0nub | ohh right |
| 08:57 | djang0nub | this crazy RPS |
| 08:57 | justin_smith | literally (.foo bar) is foo.bar() |
| 08:57 | djang0nub | RPN |
| 08:57 | djang0nub | ok :) |
| 08:57 | justin_smith | and (.foo bar baz) is foo.bar(baz) |
| 08:57 | djang0nub | wait is (.foo bar) foo.bar |
| 08:58 | djang0nub | or bar.foo() |
| 08:58 | djang0nub | bar.foo()? |
| 08:58 | justin_smith | djang0nub: argh, just woke up, you are right |
| 08:58 | justin_smith | bar.foo() |
| 08:58 | justin_smith | or bar.foo - it could be field access |
| 08:58 | djang0nub | right. |
| 08:58 | djang0nub | makes sense now :D |
| 08:58 | djang0nub | ty |
| 08:59 | justin_smith | so yeah, (.foo bar) is foo.bar() or foo.bar, and (.foo bar baz) is bar.foo(baz) |
| 08:59 | justin_smith | fuck I got it wrong again |
| 08:59 | djang0nub | lol |
| 08:59 | djang0nub | close |
| 08:59 | justin_smith | (.foo bar) is bar.foo() |
| 08:59 | djang0nub | this is why lisp scares me |
| 08:59 | djang0nub | so many lists |
| 08:59 | justin_smith | haha |
| 09:00 | justin_smith | djang0nub: in the examples above, the number of parens used is identical between the clojure and java versions |
| 09:00 | justin_smith | djang0nub: I got the conversion wrong because I never do the java version |
| 09:01 | djang0nub | nice |
| 09:01 | djang0nub | I'm trying to decipher this thing to rewrite it in scala |
| 09:01 | djang0nub | maybe next i can move to clojure |
| 09: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 |
| 09:03 | djang0nub | i was almost asking :) |
| 09:03 | djang0nub | and its true |
| 09:03 | djang0nub | thankfully we are gradually moving towards defining a "sane subset" of the language |
| 09:03 | djang0nub | less implicts and magic |
| 09:03 | djang0nub | but its still pretty crazy out there |
| 09:04 | djang0nub | must be nice on the lisp side |
| 09:04 | justin_smith | I think scala has a niche advantage in that it has a version that looks a lot like normal java |
| 09:04 | djang0nub | the language is just done |
| 09:04 | djang0nub | yeah.. not just looks, you can code in java |
| 09:04 | djang0nub | its a great way to ramp up to more FP type stuff |
| 09: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 |
| 09:05 | djang0nub | that sounds great actually |
| 09: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 |
| 09:06 | justin_smith | I'm sure you've learned that's not always the case with other jvm languages |
| 09:06 | djang0nub | i know, thats why its the only lisp ill consider learning right now :) |
| 09:06 | djang0nub | scala interop is definitely impossible from java side |
| 09: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...) |
| 09:08 | djang0nub | hey so does clojure have the capability of implementing crazy functional stuff like type classes or semigroups or dependent types |
| 09:08 | djang0nub | more i read scala more i get exposed to these fp ideas, and then peoples horrific hacks to get them to work with scala |
| 09:08 | djang0nub | makes me think haskell might be the way to go for pure functional glory |
| 09:08 | justin_smith | djang0nub: it's possible but in practice we don't do it, because we are not strongly typed |
| 09:09 | djang0nub | omg |
| 09:09 | djang0nub | travesty |
| 09:09 | justin_smith | though their are monads and lens libraries for those who want to go that route |
| 09:09 | justin_smith | *there |
| 09:09 | djang0nub | oh now i see why we need the compiler hints |
| 09:09 | djang0nub | so its kind of duck typed |
| 09:09 | djang0nub | ? |
| 09:10 | Hacker-Pro | sg Who is here a good Programmer c++ ? |
| 09: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) |
| 09:10 | pbx | djang0nub, you might be interested in http://typedclojure.org/ |
| 09:11 | dstockton | and then http://blog.circleci.com/why-were-no-longer-using-core-typed/ |
| 09:11 | pbx | and djang0nub, haskell is the path of purity yes |
| 09:12 | djang0nub | ty i will read both of those |
| 09:12 | djang0nub | 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 |
| 09:13 | djang0nub | gtg, thank you for your help @justin_smith |
| 09:14 | justin_smith | np |
| 09: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? |
| 09:48 | noncom | phaseNi: how would you like it to look like? |
| 09:49 | phaseNi | {:var1 var1 :var2 var2 ...} |
| 09:55 | jeremyheiler | phaseNi: maybe you just need this? |
| 09:56 | jeremyheiler | ,(let [m1 {:a 1 :b 2 :c 3} [:keys [a b c] :as m2]] m2) |
| 09: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... |
| 09:57 | jeremyheiler | ,(let [[:keys [a b c] :as m] {:a 1 :b 2 :c 3}] m) |
| 09: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... |
| 09:57 | jeremyheiler | gah |
| 09:57 | jeremyheiler | ,(let [{:keys [a b c] :as m} {:a 1 :b 2 :c 3}] m) |
| 09:57 | clojurebot | {:a 1, :b 2, :c 3} |
| 09:59 | jeremyheiler | ,(do (def a 1) (meta #'a)) |
| 09: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 | jeremyheiler | for example, let bindings are not vars, so don't have a meta |
| 10:01 | snowell | Clojurebot! |
| 10:01 | snowell | You're back! |
| 10:05 | luma | how about something like this: https://www.refheap.com/109923 |
| 10:07 | noncom | (inc luma) |
| 10:08 | noncom | 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 | phaseNi | :P |
| 10:26 | phaseNi | 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 | justin_smith | 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 | rvsv | Hmm, that I'm not sure about. I'm trying to send an email using postal. |
| 10:35 | rvsv | 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 | justin_smith | 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 | rvsv | 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 | justin_smith | that arity of the function calls itself |
| 10:43 | justin_smith | and of course that would stack overflow on that line (the line in your error), line 68 |
| 10:44 | justin_smith | 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 | justin_smith | IMHO |
| 10:45 | justin_smith | (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 | justin_smith | 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 | RedNifre | 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 | RedNifre | 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 | justin_smith | but yeah, sure, go for it |
| 12:03 | RedNifre | oh? what do you mean? |
| 12:03 | RedNifre | 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 | justin_smith | 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 | justin_smith | yes, the alternative is just using a hash-map |
| 12:04 | justin_smith | 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 | justin_smith | 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 | justin_smith | you don't need to implement methods |
| 12:07 | justin_smith | in fact we quite like dumb classes that don't have methods other than accessors |
| 12:07 | justin_smith | 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 | justin_smith | RedNifre: no, it makes perfect sense to have a protocol only implemented by one record |
| 12:09 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | RedNifre: not without some splicing or concatenation |
| 12:12 | justin_smith | RedNifre: clojure expressions can only return one value, there is no clojure semantics for returning two values |
| 12:13 | justin_smith | 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 | craftybones | {(m "ha"} -> {{:ha "ha"}} |
| 12:15 | craftybones | sorry {(m "ha")} -> {{:ha "ha"}} |
| 12:15 | justin_smith | `{~@[ha "ha"] ~@nil} |
| 12:15 | justin_smith | ,`{~@[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 | justin_smith | oh never mind, that goes back to the previous misunderstanding |
| 12:18 | justin_smith | ,(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 | RedNifre | which approach of the three looks reasonable? |
| 12:24 | RedNifre | 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 | justin_smith | if I understand what the three options are correctly... |
| 12:25 | justin_smith | 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 | RedNifre | 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 | xemdetia | I don't know of any third party ones |
| 12:33 | xemdetia | err |
| 12:33 | xemdetia | 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 | hlolli | 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 | RedNifre | 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 | RedNifre | 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 | justin_smith | but if you are directly running the interactive code, with no repl, it will work just fine |
| 13:31 | justin_smith | 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 | justin_smith | or via unit tests even |
| 13:32 | RedNifre | I see. |
| 13:33 | RedNifre | 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 | justin_smith | 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 | xemdetia | is it good? |
| 13:36 | wasamasa | xemdetia: yup |
| 13:36 | wasamasa | xemdetia: got a copy here for a colleague |
| 13:37 | wasamasa | xemdetia: it's nice for beginners *and* is using idiomatic code |
| 13:37 | xemdetia | cool, looks like amazon gets more money today |
| 13:37 | xemdetia | (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 | wasamasa | since it's the same author |
| 13:40 | wasamasa | the overall style is comparable |
| 13:42 | xemdetia | yeah I found a sample on oreilly's site |
| 13:42 | xemdetia | the chapter layout looks good |
| 13:43 | wasamasa | if you want something more advanced, hm |
| 13:44 | wasamasa | the joy of clojure |
| 13:44 | wasamasa | 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 | wasamasa | I found it too much for someone new to the language |
| 13:47 | wasamasa | 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 | xemdetia | no I just like technical books among clojure |
| 13:47 | xemdetia | so if there is a better replacement for the 'beginner clojure' book that's interesting |
| 13:48 | xemdetia | replacement is the wrong word |
| 13:48 | xemdetia | 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 | justin_smith | RedNifre: lein repl does supply readline, you can also use rlwrap to provide readline facilities to any program (including a clojure repl) |
| 13:53 | justin_smith | RedNifre: to reload a namespace use (require 'some.ns :reload) or (require 'some.ns :reload-all) |
| 13:53 | justin_smith | the latter will recursively load the namespaces that ns loads as well |
| 13:53 | justin_smith | (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 | justin_smith | 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 | justin_smith | :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 | justin_smith | one of them has been compiled, the other cannot run unless you compile it |
| 14:02 | justin_smith | RedNifre: clojure has no interpreter, you can only run things that have been turned into byte code |
| 14:02 | justin_smith | 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 | justin_smith | I' |
| 14:04 | justin_smith | 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 | justin_smith | ,'(+ 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 | RedNifre | 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 | justin_smith | 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 | justin_smith | so yeah, hash map where values are functions sounds perfect |
| 14:09 | justin_smith | 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 | justin_smith | RedNifre: there is potentially reflection |
| 14:10 | justin_smith | it's bytecode that reflects (sometimes) on its args |
| 14:11 | justin_smith | 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 | justin_smith | it's very useful for finding slow code |
| 14:16 | RedNifre | Is there a short form for (fn [] ...) ? |
| 14:16 | justin_smith | #() |
| 14:17 | justin_smith | the first arg is % or %1, second arg is %2, all remaining args after last accessed are %& (one list) |
| 14:17 | justin_smith | ,(#(+ % %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 | justin_smith | just wrap it in parens |
| 14:22 | justin_smith | ,(#(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 | justin_smith | (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 | justin_smith | (if true (1) (2)) |
| 14:25 | justin_smith | it would work in another language, it errors in clojure, for obvious reasons |
| 14:25 | justin_smith | 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 | RedNifre | 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 | justin_smith | 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 | justin_smith | 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 | RedNifre | 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 | justin_smith | 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 | justin_smith | ,(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 | justin_smith | 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 | justin_smith | ,(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 | justin_smith | ,(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 | hlolli` | 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 | justin_smith | 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 | justin_smith | so atoms get slower faster when you have frequent update |
| 14:58 | justin_smith | and are optimal for rare updates (thanks to their optimism) |
| 14:59 | justin_smith | "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 | justin_smith | hlolli`: makes sense, yeah |
| 15:00 | justin_smith | 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 | justin_smith | 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 | hlolli` | 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 | justin_smith | 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 | justin_smith | and just make a new one each time |
| 15:03 | justin_smith | 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 | justin_smith | (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 | justin_smith | 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 | justin_smith | 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 | hlolli` | 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 | RedNifre | 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 | hlolli` | 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 | justin_smith | heh |
| 15:17 | RedNifre | Yeah, that's planned for next time. |
| 15:18 | RedNifre | 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 | justin_smith | 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 | 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 | justin_smith | 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 | RedNifre | ...but I guess it depends on the skill set of your team... or are you the only one working on it? |
| 15:24 | RedNifre | 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 | xemdetia | 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 | xemdetia | 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 | RedNifre | 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 | RedNifre | 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 | augustl | 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 | 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 | xemdetia | 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 | augustl | 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 | RedNifre | 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 | RedNifre | ...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 | justin_smith | 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 | justin_smith | RedNifre: calling clojure from java, and java from clojure, are both very very easy |
| 15:49 | justin_smith | 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 | RedNifre | oddcully what, Kotlin is done by the same guy as Groovy? Didn't know that. |
| 15:52 | RedNifre | 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 | RedNifre | 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 | oddcully | 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 | hlolli | 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 | RedNifre | 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 | RedNifre | (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 | justin_smith | RedNifre: all the objects we use are java objects |
| 16:40 | justin_smith | though not always the default ones the lang provides |
| 16:40 | justin_smith | for example we use regular strings, there's nothing wrong with them, they are already immutable, they work great |
| 16:40 | justin_smith | same with the simple numeric types |
| 16:41 | hlolli | ok |
| 16:41 | hlolli | 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 | xemdetia | 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 | hlolli | doing |
| 16:45 | hlolli | 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 | xemdetia | 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 | RedNifre | 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 | xemdetia | 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 | xemdetia | 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 | justin_smith | and generate the future as needed |
| 16:50 | justin_smith | 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 | justin_smith | so you could generate 5 next states, and keep the one you like best |
| 16:50 | justin_smith | 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 | justin_smith | 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 | jbrhbr | for now i'm just lurking here and picking up little things |
| 16:54 | jbrhbr | 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 | xemdetia | 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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | hlolli | (hack) |
| 16:58 | justin_smith | heh :) |
| 16:59 | RedNifre | okay, I guess I understood half of that. |
| 16:59 | RedNifre | 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 | justin_smith | 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 | justin_smith | ,(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 | justin_smith | RedNifre: exactly! |
| 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 | justin_smith | 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 | justin_smith | RedNifre: yes, playing so fast and loose with data does have costs sometimes |
| 17:05 | justin_smith | 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 | jbrhbr | 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 | justin_smith | 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 | 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:19 | justin_smith | RedNifre: do you understand how ->> and -> operate by rewriting forms? |
| 17:19 | justin_smith | ,(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 | justin_smith | 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 | bja | 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 | ane | 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 | justin_smith | RedNifre: but there's no rule that says ->> can't turn one item in a list into two items in a list |
| 17:24 | justin_smith | 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 | justin_smith | RedNifre: (->> map @keyword) => (->> map (deref keyword)) => (deref keyword map) |
| 17:26 | justin_smith | 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 | Bronsa | 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 | justin_smith | 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 | RedNifre | ...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 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | RedNifre | and what's "->" then? |
| 17:40 | justin_smith | RedNifre: but currying creates functions, and ->> creates forms for the compiler to compile |
| 17:40 | justin_smith | 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 | justin_smith | ,(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 | justin_smith | ->> puts the form into the last place of the next form |
| 17:40 | justin_smith | -> 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 | HoneyBadger | *people |
| 17:40 | justin_smith | RedNifre: -> is good for map operations where each step returns a new hash-map |
| 17:40 | justin_smith | 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 | justin_smith | get, assoc, dissoc, get-in, update-in, etc. |
| 17:40 | justin_smith | 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 | RedNifre | hm... |
| 17:41 | RedNifre | 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 | justin_smith | 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 | RedNifre | 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 | justin_smith | 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 | RedNifre | 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 | RedNifre | I mean "remove an element from a list and return what's left of the list" is probably on the sheet... |
| 17:59 | RedNifre | 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 | turbofail | 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 | turbofail | 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 | RedNifre | 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 | amalloy | 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 | turbofail | 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 | RedNifre | 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 | RedNifre | 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 | RedNifre | ...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/> |
| 18:37 | justin_smith | RedNifre: note how dates print |
| 18:37 | justin_smith | #inst |
| 18:37 | justin_smith | 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 | justin_smith | 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 | justin_smith | 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 | futuro | 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 | TEttinger | 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 |