#clojure log - Mar 05 2016

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

0:51 will_sm: How should I make "structs" in clojure?

0:51 should I just use a hashmap?

0:52 skelternet: will_sm: think so

1:01 could use a record but it will include most funcs of a map.

1:02 sorry not staying longer. good night

4:55 Confusionist: Hi, is there a way to get 'lein ancient' to also report what plugins and dependencies in my .lein/profiles.clj can be upgraded?

5:43 renl: Hi what libraries should I use for interprocess comma as well as distributed systems?

6:28 spuz_: Is there a better way to find the largest value in a sequence that is less than some value?

6:28 , (apply max (filter #(< % 10) (range 1 19)))

6:28 clojurebot: 9

6:53 Confusionist: spuz_, in general there isn't, but if you know the sequence is ordered you can exploit that. E.g. in the example case you can replace 'apply max' by 'last'

6:53 spuz_: Confusionist, yeah thanks

6:53 or drop-while and first I guess

6:54 Confusionist: or reverse and find the first entry matching a predicate: that could also be significantly faster

8:04 OscarZ: i cant get lein repl to open.. i probably messed up something in my project.clj.. just getting message "Logging initialized..."

8:05 any ideas what are common causes for repl not to start ? after a timeout it fails with error "REPL server launch timeout"

8:07 hmm.. it loads the namespaces i suppose, maybe im doing something foolish somewhere in my .clj files

8:38 hmm i suppose i need to find out what lein repl actually does...

8:58 justin_smith: renl: manifold from ztellman ties those things together, and has nice things like flow control and back pressure too

8:59 OscarZ: it starts an nREPL server then loads your :main namespace

8:59 renl: thanks

9:00 justin_smith: OscarZ: I think your best bet is to run lein clean, then run 'lein run -m clojure.main' then that launches a vanilla clojure.main repl

9:00 OscarZ: in that repl (require 'my-main.ns) and watch for errors

9:00 I bet that will be informative

9:01 renl: is manifold like a message queue?

9:01 OscarZ: justin_smith, thanks ill try that.. forgot to mention that i have a dev profile with user.clj.. but it has nothing in it except requires

9:02 justin_smith: renl: no, it's a utility library with many facilities relating to message queues

9:03 OscarZ: a clojure.main repl should also end up loading your user.clj - but in my experience running clojure.main is more likely to expose errors that are silent in lein repl in some cases

9:05 OscarZ: ok.. i think this is an issue with some of my code that runs when the namespaces are loaded

9:10 renl: manifold doesnt handle the actual socket programming right? is aleph the goto library for all things socket?

9:10 justin_smith: renl: aleph is good (and it already pulls in manifold by default)

9:12 I think I misinterpreted your question because of the "as well as" - manifold can help do coordination inside one vm as well as coordination between vms, whether on the same host or different hosts, and can be used to tie socket comms together with distributed stuff

9:12 though in java IPC tends to fall back to the same stuff you would use for distributed, because there's not much for portable IPC and java aims to be portable

9:13 renl: nod thanks

9:13 justin_smith: I should say "on the jvm" and not "in java" but you probably get that's what I meant

9:15 renl: yup

12:02 arkh: is there a way to limit/truncate stacktrace length when using tools.namespace and (refresh)?

12:04 the interesting parts are usually the first 30 lines or so but some stack traces get in the ballpark of 120 lines

12:25 cortexman: what's a stateful transducer?

12:29 arkh: cortexman: http://stackoverflow.com/questions/26506689/what-is-a-stateful-transducer

12:55 cortexman: arkh, is it the same as a finite state tranducer?

12:55 *transducer

13:04 arkh: cortexman: an FST is a bit more than a stateful transducer though to be honest I've never used one. Looks interesting though! I'm reading up on it now

13:04 e.g. https://github.com/structureddynamics/clj-fst

13:46 spuz_: , (-> (range 10) (partial map even?))

13:46 clojurebot: #object[clojure.core$partial$fn__4761 0x4e1b30c4 "clojure.core$partial$fn__4761@4e1b30c4"]

13:46 spuz_: why does that not return [0 2 4 6 8] ?

13:48 TMA: ,(macroexpand '(-> (range 10) (partial map even?)))

13:48 clojurebot: (partial (range 10) map even?)

13:49 TMA: spuz_: that's why

13:49 spuz_: ah right

13:50 if I want to use -> and a map, how can I do it?

13:50 TMA: spuz_: what do you want to accomplish?

13:50 spuz_: I want that first example to return a list of even numbers

13:51 and to use the threading macro

13:51 TMA: why?

13:51 clojurebot: TMA: because you can't handle the truth!

13:53 TMA: spuz_: if you want the even numbers, you might be better of with (filter even? (range 10)) or some such

13:53 spuz_: TMA, ok maybe it would be more accurate to say, I would like to learn how the -> macro works

13:54 TMA: now you are talking :)

13:54 ,(macroexpand '(-> a (b) (c)))

13:54 clojurebot: (c (b a))

13:55 TMA: spuz_: it is quite easy: (a) (-> form) is just form

13:56 spuz_: (-> form (head rest...)) is (head form rest...)

13:56 spuz_: TMA is it possible to use -> with (map f) ?

13:56 TMA: spuz_: and finally (-> form (head rest...) others...) is (-> (-> form (head rest...)) others...)

13:57 spuz_: indeed it is. but the result might be very different from what you might expect

13:58 spuz_: is it possible to use it in a way that does what I expect?

13:58 or do I have to use ->> ?

13:58 TMA: spuz_: (-> X (map f)) is the same as (map X f)

14:00 burhanloey: (->> (range 10) (filter even?)) I guess?

14:02 TMA: spuz_: also, map yields a sequence of the same length as its second argument, which is very unwieldy if you like to filter out some elements

14:03 spuz_: however, with proper obfuscation it can be done

14:03 spuz_: I meant to use filter, not map

14:03 poor exampe

14:03 example*

14:04 bacon198`: I found it hard to understand threading at first

14:04 you need to think in terms of (-> <context> <expressions...>)

14:04 <context> gets placed as the first argument of each expression

14:05 errr

14:05 the result of that previous expresssion gets passed on to the next one

14:05 as the first argument of the expression

14:06 will_sm: So I can't put a docstring in a defonce?

14:06 TMA: ,(apply concat (-> (range 10) (((fn [f] (fn [a b] (f b a))) map) (fn [x] (if (even? x) [x] [])))))

14:07 clojurebot: (0 2 4 6 8)

14:07 TMA: spuz_: see? it can be done with -> and map

14:07 spuz_: with a bit of magic

14:08 spuz_: um

14:08 that is going a bit too far for me

14:12 TMA: spuz_: please do not do that ever in production code

14:13 spuz_: from the inside ((fn [f] (fn [a b] (f b a))) map) is a function that is exactly like map, except when you call (map A B) you need to call (abomination B A) for the same result

14:14 spuz_: hah ok i see

14:14 TMA: spuz_: (fn [x] (if (even? x) [x] [])) is a function that returns [x] for even x and [] otherwise

14:14 spuz_: isn't there a function that does that for you in clojure?

14:14 i.e reverses the order of arguments

14:15 * TMA does not know any clojure...

14:15 TMA: it might be there

14:17 spuz_: and finally the (apply concat ...) merges the [[0] [] [2] [] [4] [] [6] [] [8] []] into (0 2 4 6 8)

14:17 justin_smith: TMA: that could be mapcat btw

14:17 spuz_: in haskell there is flip

14:18 justin_smith: in clojure I would do (fn flippy [f] (fn [& args] (apply f (reverse args))))

14:19 but that isn't a thing defined in clojure.core

14:19 TMA: justin_smith: I have already said that I do not know clojure ;) therefore I was ignorant of mapcat

14:19 justin_smith: aha

14:20 TMA: I am using it now and then, but I do not know it. It is so much better than java, I can be more productive even without knowing it

14:23 spuz_: personally I do not use -> or ->> or any of the ilk. they make code too cumbersome to figure out for me. However, that means there is a lot more deply nested parentheses in my code, which I personally do not abhor as much as the average programmer

14:23 spuz_: heh

14:23 I have not used them so much so far

14:24 but for this particular case they make a lot of sense

14:24 Somelauw: I see a lot of clojure programmers use unicode for variable names. What is your favourite method of doing unicode input?

14:25 I mean, how do you enter weird chars like → or ǽ or Greek λ or is that still a bad idea?

14:26 justin_smith: TMA: wait, I just realized your silly thing above was just (filter even? (range 10))

14:26 Somelauw: probably mostly people are using emacs

14:27 Somelauw: either something that automatically translates certain names, or gives shortcuts for inserting favorite chars (though in emacs you can insert any char by name with M-x insert-char)

14:27 TMA: justin_smith: yes. the goal was to implement it using -> and map; in particular -> had to be used

14:27 justin_smith: oh

14:28 ,(->> (range 10) (mapcat #(when (even %) %)))

14:28 clojurebot: #error {\n :cause "Unable to resolve symbol: even in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: even in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6688]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: even in this co...

14:28 justin_smith: ergh

14:28 ,(->> (range 10) (mapcat #(when (even? %) %)))

14:28 clojurebot: #<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long>

14:28 justin_smith: oh right

14:29 ,(->> (range 10) (mapcat #(when (even? %) [%])))

14:29 clojurebot: (0 2 4 6 8)

14:29 TMA: justin_smith: ->> was expressly forbidden, hence the argument flipping abomination

14:30 justin_smith: oh, man, I should have just read the scrollback

14:30 Somelauw: justin_smith: ah, in vim there is ctrl-k. I just think I want this system-wide or something

14:30 TMA: it was fun to design it though. :)

14:31 Somelauw: If emacs also uses RFC 1345, vim and emacs might actually be using the same mappings

14:31 justin_smith: Somelauw: emacs uses the full written name of the char (with tab completion) for insert-char at least

14:31 spuz_: Is there a better way to write this function? https://www.refheap.com/a3fa8bcb058f49e845a7dc0e5

14:32 I am not entirely sure why I have to use map, apply and str to convert the partitioned values back into a string

14:32 justin_smith: Somelauw: thanks for the info, I just switched to vim a week ago (I had been using evil mode for wrist reasons, and I figured I might as well try the real thing)

14:33 Somelauw: oh cool, C-kl*

14:50 OscarZ: how does clojure namespaces work.. say i have some core namespace that has some namespaces that are :required at the top of the file.. does it load and run them recursively in the order then ?

14:54 hiredman: because a string becaomes a seq of characters, and partition partitions that seq of characters in to a seq of seqs of characters, and join takes a collection of strings

14:55 justin_smith: spuz_: also, unlike in haskell, in the jvm a list of chars is not a string

14:59 OscarZ: im using the ns :require in dev/user.clj to require some namespaces, one of them is db.clj where im doing some db initialization work .. for some reason when running lein repl this init gets stuck

14:59 justin_smith: OscarZ: when you require a namespace, all top level code is run

15:00 so if there's something at the top level of the namespace that would block or error, require will make that blocking or error happen

15:00 OscarZ: but if i comment the particular namespace out from ns, lein repl starts.. and if i then run (require 'db) it works fine

15:00 justin_smith: hmm

15:00 OscarZ: seems like some kind of race condition or something..

15:01 justin_smith: require doesn't do anything concurrent, (but if you have async stuff at the top level of your namespace that has side effects I'd expect all kinds of problems)

15:04 OscarZ: no async stuff as far as i know :)

15:08 somehow related to repl and its loading .. it works when repl is loaded and then i require the namespace..

15:15 jumar: willharrison, you should be able to download it from http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey

15:16 just look at the source code - you should find the link like http://videoh.infoq.com/presentations/09-sep-clojure-keynote.flv

16:00 Somelauw: justin_smith: I admit that emacs insert-char is easier to use than vim digraphs. Maybe not faster, but definitely easier.

16:42 justin_smith: I ported the emacs insert-char function to Python: https://pastee.org/3pwmc

16:43 justin_smith: Somelauw: so is that a plugin for dmenu? I think that's what I use as a launcher with i3 on my linux box

16:43 honestly I forget what the program is called

16:44 Somelauw: yes, i3 uses dmenu in its standard config

16:45 not completely finished, because it only prints the character, I need to insert it as well

16:48 unfortunately, xdotool key "!" doesn't work for me

16:56 justin_smith: An ugly workaround, but it works if you call it like this: python insert-char.py | xclip -i && xdotool key "shift+Insert"

16:57 rhg135: Beats learning vimlang

16:58 Somelauw: and instead of print, you need sys.stdout.write in the last line of my paste

17:11 blegh: it seems that in Python printing unicode works fine when printing to a console, but that an encoding is needed when piping to another process

17:12 so here is yet another version: https://pastee.org/t3w4c

17:14 rhg135: I could maybe rewrite it as a plugin for some fuzzy-completion plugin in vim, but now I have it system-wide

17:15 Unix says: DOTADIW but sometimes it's unclear which resposibility should be given to whom

17:15 rhg135: Somelauw: I think you can spawn subprocesses, they must be synchronous though

17:16 r!stuff is useful

17:16 Somelauw: rhg135: i'm already doing that, I now have a small shell wrapper around python that can also be put in the code itself using subprocess

17:16 what is r!stuff?

17:17 rhg135: Insert the output of a command

17:18 Somelauw: in shell?

17:18 justin_smith: Somelauw: clearly what you need is to turn vim into a window manager

17:19 rhg135: Any executable

17:19 Somelauw: rhg135: can you give an example?

17:19 justin_smith: that's what Emacs said

17:19 rhg135: :r!echo 1 would insert 1 in vim

17:20 Somelauw: oh, in vim

17:20 and I thought that needed a space between r and !

17:20 rhg135: Maybe it does

17:21 justin_smith: no, it doesn't

17:21 (just checked)

17:22 rhg135: justin_smith: actually vim already does a bit of window management

17:22 Somelauw: rhg135: :r !python insert-char.py prints the char after a newline for me

17:23 justin_smith: rhg135: right, my point was he was trying to get vim-style char insertion everywhere (thus the dwm thing...) and a vim solution only helps if all his other apps are inside vim

17:23 but maybe I misunderstood the task

17:25 rhg135: Isn't this why input managers exist?

17:26 Somelauw: if an input-manager can do this or has a plugin system to do this or something that would be great

17:26 rhg135: Well, I wasted time getting uim working

17:27 Somelauw: For some reason, gtk and qt use a different input than the rest of x

17:27 rhg135: Let's me bug people with infinite amounts of Greek letters

17:27 Somelauw: rhg135: How have you set uim up?

17:28 rhg135: I think I just launched it and set some variables from my profile

17:28 Somelauw: how do you enter a greek char from uim?

17:29 rhg135: The arch wiki was useful here

17:29 Somelauw: at least in xim, you need to write your own .XCompose

17:29 rhg135: Compose key is how I do it

17:29 C-S-u

17:29 Somelauw: because the standard composing have a very limited set

17:30 rhg135: Yup

17:30 Somelauw: I already made xim use vim digraphs using some script

17:34 the arch wiki page about uim only talks about Japanese

17:37 rhg135: I looked at all the input manager pages

17:39 Somelauw: it might be that uim has more features than xim I don't know

17:39 rhg135: Is there anything xim doesn't do that you need?

17:39 rhg135: I couldn't get xim to work in some apps

17:41 Somelauw: yeah, gtk and kde use their own input methods by default for some reason, you could force them to use xim or uim

17:42 rhg135: I'll try that when I'm at my computer later

17:50 Somelauw:


17:51 that's supposed to be snowman

17:51 okay, played enough

18:09 rhg135: I see

18:23 justin_smith: yes, that was a snowman

18:26 RedNifre: Hey there. First time using leiningen, I try to use Compojure by following this guide: https://adambard.com/blog/sinatra-docs-in-clojure/ but I get this error, what is the problem? http://pastebin.com/Xn54awdU

18:27 justin_smith: RedNifre: how did you add compojure to your dependencies?

18:27 that error means that the clojure compiler could not find the compojure files

18:27 RedNifre: good question, the tutorial wasn't that clear so I improvised. One second...

18:28 Ah, I'm pasting too much. Anyway, here is my project.clj: http://pastebin.com/mNyyRNaV

18:30 I did not follow the tutorial exactly, it said to run "lein new myapp" but I thought since that would create a library project where I surely want an executable I went with "lein new app myapp" instead. Was that wrong?

18:30 justin_smith: RedNifre: if the project.clj is what you shared, and it is in the directory with the project, lein should have downloaded compojure (or used the copy already on your cache)

18:31 RedNifre: I won't have a copy in my cache since this is the first time I use compojure. I should do "lein run" in the project directory i.e. the directory that contains the project.clj, right?

18:31 justin_smith: RedNifre: can you share the output of 'lein cp' - or at least check if compojure is actually in that output?

18:32 RedNifre: right

18:33 RedNifre: Ah, never mind. It seems that the project.clj wasn't properly saved m/

18:33 Seems to work properly now, sorry.

18:34 justin_smith: that would explain it

18:36 RedNifre: Regarding tutorials, I did not like "Clojure for the brave and true" at all. I heard "the joy of clojure" might be good, can you recommend it? Or what might be a good next step for me?

18:36 justin_smith: joy of clojure is good, but it's not a slow intro

18:37 RedNifre: Sounds good. I'm looking for something that is as in-your-face overwhelming as the Haskell tutorials.

18:41 ridcully: if you are battle hardened in other languages i found clojure applied very nice

18:42 justin_smith: yes, that's a good one too

18:45 RedNifre: So what's the theme of "joy of clojure" compared to "clojure applied"?

18:45 justin_smith: joc gets more into the design philosophy, clojure applied has more about typical incidental things you should know if you are going to develop and deploy clojure

18:50 RedNifre: Well, the situation I find myself in is this: I write large things in Haskell (solid, difficult) and small scripts in Ruby (messy, easy). Clojure looks interesting to me because it's functional. I'm skeptical about the dynamic typing but I think I should give it a chance. Macros seem amazing, though the only use case I can think of so far would be Haskell's do-notation. What I find very fascinating is this "code is data" thing; edn is muc

18:50 h more elegant than json so the idea of writing config files in the same data format that I use to write the small tools that use them is appealing.

18:52 So basically I wonder if Clojure can replace Ruby for me (I only use Ruby for small tools) and if that works out I might check how far it scales i.e. whether I can write code as robust as Haskell code but quicker (because of dynamic typing).

18:53 I'm not sure if I can judge those two books yet, but maybe I should read joc first to understand the philosophy and "clojure applied" after that?

18:55 TEttinger: RedNifre: the only annoyance I have with clojure for small tools is the somewhat long startup time, though there are good ways around that now. I do spend much less time writing the tools though, relative to OOP langs like Java, so that's a benefit :)

18:55 https://github.com/technomancy/leiningen/wiki/Faster

18:56 RedNifre: TEttinger do you always create jars or do you have very tiny scripts that you run interpreted?

18:56 Regarding lein, is it a build tool / dependency resolver like all the others (cabal, npm etc.) or does it differ?

18:56 TEttinger: I almost never create jars unless I have to

18:57 I run with 'lein run' usually, sometimes 'lein repl' and paste in some snippet

18:57 for tiny stuff I just paste it into a lazybot instance I have running

18:58 lein is a built tool and dep resolver, it doesn't do much too different. it does have good creation of uberjars

18:58 RedNifre: I like Ruby's shell interop, you can run shell commands just by using backticks: `curl -bla`. How shell friendly is Clojure?

18:58 TEttinger: it emphasizes cross-platform stuff, and shell commands are not that at all, so...

18:59 there is shell support in the standard lib

18:59 RedNifre: Okay, so it might not be the best choice for glorified bash scripts, huh?

18:59 TEttinger: http://clojuredocs.org/clojure.java.shell/sh

18:59 maybe. that's largely what I use it for, I suppose

19:00 justin_smith: ProcessBuilder is super powerful, but it is also stupidly limited by portability

19:00 eg. you can't find out the PID of a process you launched

19:02 RedNifre: sh looks good. But what for do you people use Clojure? Do you use it for the tiniest tools or do you write those in bash or a traditional scripting language? Or do you use Clojure for large projects? Something in between?

19:04 TEttinger: I've been meaning to use it for larger projects, but mostly I currently use Clojure for small things that I want to get working fast

19:04 clojure is definitely good as projects start to get bigger

19:06 an example is this spreadsheet data turned into TSV: https://dl.dropboxusercontent.com/u/11914692/input.txt I run a small, messy script on it to convert it to much more usable text https://dl.dropboxusercontent.com/u/11914692/class-formatter.clj and the output is like https://dl.dropboxusercontent.com/u/11914692/classes200-final.txt

19:09 RedNifre: The problem sounds like the kind of thing that I solve with Ruby, but the Clojure code looks surprising to me. I'll have to stare at it for a moment...

19:10 Ah, so inp is the file stream and the -> macro funnels it through several processing steps?

19:11 TEttinger: it was never meant to be read!

19:11 this was meant to be written once, clumsily, using regexes to make regexes lol

19:11 I probably have cleaner scripts

19:11 RedNifre: heh

19:12 Indentation would be nice :)

19:12 TEttinger: that's just one I've used a bunch and produced very very quickly

19:12 that one I just paste into the repl

19:12 RedNifre: I see.

19:12 TEttinger: so the indentation doesn't affect it

19:12 (it wouldn't anyway I guess)

19:13 RedNifre: Personally I'm still not used to Lisp's ((()()))()()()))))) but I solved it with this thing called "parinfer": I just use indentation and parinfer just places the parens for me.

19:13 TEttinger: what I pasted is a terrible example of what makes clojure powerful, there'

19:13 what I pasted is a terrible example of what makes clojure powerful, there's a lot better examples I can probably dredge up

19:14 RedNifre: Sure, I only understand it up to "(def fin" so far.

19:14 Does "fin" stand for "finishing touches"?

19:15 TEttinger: hehe yep. I would probably recommend this instead, in retrospect http://ideone.com/L0eHc6

19:15 h.p.lovecraft-style rambling generator

19:15 RedNifre: weird, the syntax highlighting is almost completely pink.

19:16 TEttinger: that can technically be compressed down to one IRC message, about 400 chars. I won't paste it since it's super illegible that compressed

19:16 yeah ideone is broken with '

19:16 it thinks ' is a paired quote somehow

19:19 output is below

19:19 RedNifre: What is "#(do["? Wasn't "#(" the beginning of a lambda and doesn't do usually look like (do (bla) (blaa) (bleh))?

19:20 justin_smith: RedNifre: both of these things are correct

19:20 TEttinger: that might have been to produce a vector in a lambda with a little less code

19:20 justin_smith: RedNifre: #([...]) will error when called, because it calls a vector with no args

19:20 TEttinger: ,(map #(do [% %]) [1 2 3])

19:20 clojurebot: ([1 1] [2 2] [3 3])

19:21 justin_smith: ,([:a :b :c] 1)

19:21 clojurebot: :b

19:21 * RedNifre is thinking

19:22 RedNifre: I don't understand why #(do [%]) should be fine but #([%]) isn't. To me, both look like the lambda would just return the vector with the parameter inside.

19:22 TEttinger: another repacement would be

19:22 ,(map #(vector % %) [1 2 3])

19:22 justin_smith: RedNifre: try expanding

19:22 clojurebot: ([1 1] [2 2] [3 3])

19:22 justin_smith: ,'#([:a])

19:22 clojurebot: (fn* [] ([:a]))

19:23 justin_smith: RedNifre: see the problem?

19:23 ,'#(do [:a])

19:23 clojurebot: (fn* [] (do [:a]))

19:24 TEttinger: unless there's a quote preventing it from being run, parentheses in clojure go before fn or macro calls. vectors and maps actually can be used as fns, but they need an index or key as an argument to that fn

19:24 justin_smith: [] is callable, if #([]) was not an error, and returned [], then #(println) would return the println function, instead of calling it

19:25 RedNifre: Oh, that makes sense.

19:25 TEttinger: I think the do[ was a relic from when I ported it from the one-liner

19:25 RedNifre: That also means that if I actually want a lambda that returns the println function I have to write (fn [] println) instead? Or maybe #('println) ?

19:26 justin_smith: RedNifre: 'println is a symbol

19:26 not a function

19:26 ,('println "hello" "world")

19:26 clojurebot: "world"

19:26 justin_smith: sorry, that was evil

19:26 RedNifre: My bad, I confused the quote with "don't execute it yet".

19:26 justin_smith: RedNifre: with symbols it means "don't resolve it yet"

19:27 TEttinger: ,(constantly println) ; this is a nice way to return a fn that when called will return the value you want, every time

19:27 clojurebot: #object[clojure.core$constantly$fn__4614 0x7e9f909b "clojure.core$constantly$fn__4614@7e9f909b"]

19:27 justin_smith: ,((resolve 'println) "hello" "world")

19:27 clojurebot: hello world\n

19:27 TEttinger: ,(map (constantly 11) [1 2 3])

19:27 clojurebot: (11 11 11)

19:27 RedNifre: Why did clojurebot just say "world"?

19:28 justin_smith: ,('a 'b 'c)

19:28 clojurebot: c

19:28 TEttinger: I don't know either, haha

19:28 justin_smith: ,(get 'a 'b 'c)

19:28 clojurebot: c

19:28 justin_smith: err

19:28 ,(get 'b 'a 'c) ; this is actually equivalent

19:28 clojurebot: c

19:28 RedNifre: well, I don't understand that one either.

19:28 justin_smith: RedNifre: when a symbol is called, it acts as get of itself

19:28 ,('a '{a 0})

19:28 clojurebot: 0

19:28 TEttinger: justin_smith: is that the default get thing?

19:28 justin_smith: ,('a '{b 0} 'c)

19:29 clojurebot: c

19:29 justin_smith: TEttinger: yup

19:29 RedNifre: here's an amazing bug

19:29 ,('or nil false)

19:29 clojurebot: false

19:29 justin_smith: ,('or true false)

19:29 clojurebot: false

19:29 justin_smith: haha

19:29 (bug being using 'or instead of or of course)

19:29 TEttinger: ,(get {:a 1} :a)

19:29 clojurebot: 1

19:30 TEttinger: ,(get {:b 1} :a)

19:30 clojurebot: nil

19:30 TEttinger: ,(get {:b 1} :a :not-found)

19:30 clojurebot: :not-found

19:30 TEttinger: does that make it a bit clearer?

19:30 RedNifre: I only know why (:a {a 0}) would be 0, I don't understand what happens in ('a '{a 0}). How does clojurebot execute quoted forms?

19:30 justin_smith: RedNifre: you don't get quote

19:31 TEttinger: to be honest I don't either :)

19:31 justin_smith: '{a 0} is the same as {'a 0}

19:31 since 0 is self-evaluating

19:31 for that matter, '{:a 0} is the same as {:a 0} because all parts of the form are self-evaluating

19:32 RedNifre: Okay, I only understood quote as "Don't run this code" and that I can run it later with eval which I shouldn't.

19:32 justin_smith: quoting prevents lists from evaluating, and it prevents symbols from being resolved

19:32 RedNifre: no, it is not "don't run this code" it's "don't execute lists (just leave them as lists) and don't resolve symbols (just leave them as symbols)"

19:32 TEttinger: where something like (a b c) is a list of symbols, and unless quoted, would try to resolve them

19:33 ,(a b c) ; will fail

19:33 clojurebot: #error {\n :cause "Unable to resolve symbol: a in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6688]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: a in this context"\n ...

19:33 TEttinger: ,'(a b c) ; will not fail

19:33 clojurebot: (a b c)

19:33 TEttinger: ,'(a b) ; will not fail

19:33 clojurebot: (a b)

19:33 justin_smith: RedNifre: now it does turn out that eval uses lists and symbols, but we have reader macros, which means that certain things still happen, even inside quoted data - eg '{} still constructs a hash-map because that's part of reading not eval

19:34 RedNifre: So '(+ 1 2) is a list containing +,1,2, right? What exactly is '+? I guess '+ is not the function yet, it's just ... the symbol... but what does that mean? Does it sort of mean it's something like a String containing the character "+" and resolving the symbol will find the + function? Or is the symbol a more technical thing like the memory location of the function or something?

19:34 TEttinger: ,('a 'b) ; but this doesn't quote the list, so it's trying to call the quoted symbol 'a now

19:34 clojurebot: nil

19:34 justin_smith: RedNifre: symbols are like keywords, but they also resolve to vars in a namespace

19:35 TEttinger: ,`+

19:35 clojurebot: clojure.core/+

19:35 justin_smith: and also to locals inside a bound body

19:35 TEttinger: + actually knows what its namespace is, I think, which is how ` (backtick, aka syntax-quote) can get the namespaced fn name

19:36 justin_smith: TEttinger: '+ has no namespace, eval decides what the namespace is based on the mappings in the current context

19:36 TEttinger: ohh

19:36 justin_smith: TEttinger: for example what if it's bound to a local? then it has no namespace

19:36 TEttinger: mm

19:36 justin_smith: what if you used :refer-clojure [:exclusions [+]] or however that works

19:36 RedNifre: So if I define my own + in my own namespace and then do '+ I'll get a symbol for my own + and even if I throw this '+ over to another namespace it will there not mean the standard clojure +, it will still mean my own + from the other namespace?

19:37 TEttinger: so '+ doesn't know, the environment produced from things like require is what knows

19:37 RedNifre: ah, so the opposite of what I said.

19:37 justin_smith: RedNifre: '+ isn't even "for" anything yet - you prevented the resolving step

19:37 if you used + it would resolve to the one in the same namespace, sure

19:37 RedNifre: for the last part, no, absolutely not

19:37 symbols know nothing

19:38 RedNifre: So '+ is more like the String containing the character plus?

19:38 justin_smith: namespaces decide what symbols mean

19:38 kind of, yeah, with some special behaviors that strings dont' have, of course

19:38 TEttinger: I think the last statement is closest yea

19:39 it does seem like you're picking this up pretty quickly, RedNifre

19:39 the quote thing can get kinda complex at first

19:39 justin_smith: RedNifre: maybe it would help to mention that a namespace owns a hash-map, and the keys are symbols, the values are vars, and vars are dereffed to get values created with def

19:39 RedNifre: Right, so when the symbol gets resolved, Clojure checks the current namespace for a function or variable that kinda has the same String name like what's in the symbol.

19:39 So quoted symbols can lead to disasters when using code obfuscation?

19:39 justin_smith: RedNifre: and when you call def, you are mutating the hash-map to add a new symbol as a key

19:39 RedNifre: Thanks, TEttinger.

19:42 Okay, now I'll ponder about "'{a 0} is the same as {'a 0}" for a moment...

19:42 justin_smith: ,(= '0 0)

19:42 clojurebot: true

19:43 justin_smith: ,(= '{} {})

19:43 clojurebot: true

19:43 justin_smith: ,(= ':a :a)

19:43 clojurebot: true

19:43 RedNifre: See, to me it looks like '{a 0} is a symbol and {'a 0} is a map that maps the symbol 'a to 0. I also don't understand what the hint "0 evaluates to self" means.

19:43 justin_smith: it's what I was saying about the reading step

19:43 the reading step happens before resolve, and resolve happens as the first step of eval

19:43 ' does not prevent reading - "" prevents reading

19:44 RedNifre: Are '0 and 0 really the same or does = turn the '0 into 0 before comparing?

19:44 justin_smith: RedNifre: '0 is 0

19:44 the reader creates 0

19:44 it has nothing to do with eval

19:44 reading has to happen before ' can be applied

19:44 actually, reading turns 'x into (quote x)

19:44 ,''x

19:45 clojurebot: (quote x)

19:45 RedNifre: And (quote 0) is 0?

19:45 justin_smith: will, the one above is the list '(quote 0)

19:45 but yes

19:45 ,(quote 0)

19:45 clojurebot: 0

19:45 justin_smith: ,(quote (quote 0))

19:45 clojurebot: (quote 0)

19:46 justin_smith: (quote just quoted another quote)

19:46 but 0 above is still 0!

19:46 RedNifre: Which 0 above?

19:46 justin_smith: but of course "0" contains no 0 - that actually prevented reading

19:47 RedNifre: You mean the String "0" containing the character with the unicode value 32?

19:47 justin_smith: RedNifre: the one generated by (quote (quote 0)) is a regular 0 - it is created before the quote is applied

19:47 RedNifre: yes, "" is what prevents reading

19:47 once reading happens, all literals have been created

19:47 this includes data structures and numbers and symbols and keywords - all self-evaluating things

19:48 RedNifre: remember it's a REPL

19:48 R happens first

19:49 RedNifre: Just to be sure, are we talking about something that only behaves like this in the REPL or is it the same in regular compiled clojure?

19:49 justin_smith: RedNifre: most languages have something like this (in the compiler), but lisps have the annoying / cool property that you end up needing to care, because forms are first class in the language.

19:49 RedNifre: there is no such distinction

19:49 the repl is regular compiled clojure in every aspect

19:49 there is no compiler / interpreter split

19:50 RedNifre: Is '0 just syntactic sugar for (quote 0) or is this a change or does it make no difference because resolving syntactic sugar is kinda what other things like macros are doing?

19:50 justin_smith: RedNifre: yes, ' is synctactic sugar for (quote)

19:50 just like @ is syntactic sugar for (deref)

19:51 RedNifre: Does Clojure even have a distinction between syntactic sugar and macros/reader stuff?

19:51 justin_smith: RedNifre: we have the reader itself (for things like ' and data structures and @), reader macros (all things that start with # are reader-macros), then macros, and then finally functions to evaluate

19:52 I might have missed something there, but I think that's most of it

19:52 RedNifre: Okay, so there is a hierarchy and '0 meaning (quote 0) is more language featurey than the macros I can define myself...

19:53 justin_smith: yes

19:54 RedNifre: So I take it that when I write (= '0 0) what happens is that it first turns into (= (quote 0) 0). I assume quote is also a macro which resolves it to (= 0 0). That is the final code that gets compiled and when it gets run it evaluates to true?

19:58 justin_smith: RedNifre: well, it resolves to true at compilation time

19:58 so no byte code is generated - it just returns true

19:58 RedNifre: But that's just an arbitrary optimization, right?

19:58 justin_smith: so yeah, you have the idea

19:58 RedNifre: sure, yeah, but it would be a silly one to leave out

19:58 RedNifre: Fair point.

19:59 justin_smith: RedNifre: and it's a gotcha - people expect the clojure compiler to emit byte code for things that have top level side effects, but instead the side effects are invoked while compiling

19:59 common newcomer pitfall

19:59 RedNifre: huh?

19:59 THat sounds surprising.

19:59 justin_smith: RedNifre: (def war (launch-the missiles))

19:59 that launches the missiles in order to compile the def form

20:00 RedNifre: the lesson here is don't ever do top level side effects, unless you are trying to do things at compilation time explicitly

20:00 RedNifre: The code looks wrong, it should be (defn war [] (launch-the-missiles)), right?

20:01 justin_smith: right

20:01 RedNifre: but it's usually (def connection (connect-to db))

20:01 works great in a repl!

20:01 works at dev time if you just run the project

20:01 RedNifre: I have to think about what I would expect (def war (launch)) to do actually.

20:01 justin_smith: works poorly when compiling ahead of time to make a jar that java can invoke

20:02 RedNifre: Okay, well, Haskell would launch the missiles lazily when you try to print the value of the war variable... hm...

20:02 justin_smith: heh, yeah

20:03 RedNifre: Clojure uses eager evaluation so I would actually expect the missiles to launch when the jar gets loaded into the JVM, not at compile time. Hm...

20:03 justin_smith: it launches at the time you are creating the jar

20:03 unless you are doing a non-aot packaging

20:03 RedNifre: So compiling evil source code can format my hard drive?

20:06 It seems like this makes compilation quite complicated. I'm surprised that parts of the program can run before other parts are even compiled. I guess it compiles the easy parts first and invokes the tricky parts last?

20:06 Hang on, does that mean that compiling a throw away script will run at compile time and the jar will only contain one large print statement with the result?

20:07 justin_smith: RedNifre: the compiler can be made to execute arbitrary code, this is the premise of a lisp

20:07 def is just a trivial example of this, but it goes much deeper

20:07 RedNifre: :)

20:07 It's getting more and more interesting.

20:08 That's exactly the stuff I was looking for: strange concepts I'm not familiar with yet.

20:08 justin_smith: RedNifre: you could easily test what it does in a project with a top level println in the same ns as -main with a freshly created app

20:09 RedNifre: Does the joc book cover all this?

20:09 justin_smith: yes, and probably makes a more coherent explanation of how these things all fit together and why

20:10 RedNifre: I'm sold, I'll order the book and come back when I have less basic questions to ask :)

20:11 Thank you two for taking the time to clear up my questions. It changed my view.

20:12 Have a good night!

20:12 justin_smith: you too

20:25 TEttinger: https://twitter.com/voxel/status/586452796125265921

20:28 rhg135: Heh, any you say?

20:30 justin_smith: "

20:30 err

20:31 "this is , but we call him bom"

20:33 rhg135: I think I knew a guy called bom

20:34 The school's IT system must have had fun

20:35 blockzombie: lol

20:38 TEttinger: @voxel @buzzert I noticed "a baby" is not on that list.

21:08 rhg135: Whoa

21:16 gilch: Can anybody explain where "#=" is documented? It's ungoogleable.

21:16 blockzombie: lol

21:16 it's on the cheat sheet

21:16 ... getting

21:17 http://clojure.org/api/cheatsheet

21:17 gilch: I don't see it on grimore

21:17 justin_smith: gilch: it's a reader macro, and is covered in the part of the clojure.org docs about reader macros. Everything that starts with # is a reader macro.

21:17 blockzombie: ah it's not there sorry

21:19 justin_smith: gilch: oh, wow, it's missing here too http://clojure.org/reference/reader

21:19 gilch: justin_smith: you mean http://clojure.org/reference/reader ? The string "#+" not found on page :(

21:20 *"#="

21:20 justin_smith: ,(read-string "#=(+ 1 1)")

21:20 clojurebot: #error {\n :cause "EvalReader not allowed when *read-eval* is false."\n :via\n [{:type java.lang.RuntimeException\n :message "EvalReader not allowed when *read-eval* is false."\n :at [clojure.lang.Util runtimeException "Util.java" 221]}]\n :trace\n [[clojure.lang.Util runtimeException "Util.java" 221]\n [clojure.lang.LispReader$EvalReader invoke "LispReader.java" 1100]\n [clojure.lang.LispRe...

21:20 justin_smith: anyway, that's what that is - in your repl the above returns 2

21:20 it's for evaluating arbitrary code at read time

21:20 it's kind of a terrible feature actually

21:21 gilch: Oh, like common lisp

21:21 #.

21:22 It seriously isn't documented anywhere?

21:22 justin_smith: sorry to mislead, I was sure it would be described with the other reader dispatch in the section on the reader

21:23 it has to be documented, I'm just wondering where...

21:23 gilch: this isn't searchable for obvious reasons, but it's described here https://clojuredocs.org/clojure.core/*read-eval*

21:24 gilch: aha!

21:24 gilch: in your repl, try (find-doc "#=")

21:25 that's clojure.repl/find-doc, but most repls auto-use clojure.repl for obvious reasons

21:27 amalloy: i think the reason it's not mentione with the reader macros is it's one you're not supposed to use as a feature; rather it's really just for the runtime's internal use

21:29 gilch: How am I supposed to tell what's API and what's implementation detail?

21:29 *read-eval* is documented

21:30 I wonder if I'm missing any other dispatches not on the cheatsheet?

21:31 Is there a list of the builtin edn tags somewhere?

21:32 justin_smith: #= is not an edn tag - the main difference between core/read and edn/read is that it does not respect that flag

21:32 gilch: So edn will work on all the other #x

21:32 justin_smith: that I know of, yeah

21:33 gilch: #@? / #?

21:33 justin_smith: one moment, testing

21:33 gilch: I can't think of a use edn would have for that

21:34 justin_smith: oh, just now I tested it, and #? failed

21:34 so yeah, good instinct on that one

21:35 amalloy: ,(require 'clojure.edn)

21:35 clojurebot: nil

21:35 amalloy: ,(clojure.edn/read-string "#_1 2")

21:35 clojurebot: 2

21:36 gilch: I saw #inst and #uuid on the github spec. I thought I saw a #js in code somewhere, but... Maybe find-doc

21:36 nope

21:38 https://github.com/edn-format/edn explicitly says it does support "#_"

21:38 justin_smith: gilch: also the e stands for extensible, people can add their own readers for # things

21:39 gilch: but they have to use namespace/

21:39 but #inst doesn't use namespace/

21:39 what else doesn't?

21:39 just #uuid so far?

22:21 rhg135: #= can be useful for derived constants, like if you need a char code but are too lazy

22:23 You can either write a macro, or insert seemingly random numbers

22:33 gilch: does weird things when nested (quote #=(symbol (str foo . bar)))

22:34 =>unmatched delimiter?

22:35 (quote #= (symbol #=(str foo . bar)))

22:36 foo.bar

22:59 rhg135: ,(str . " what?")

22:59 clojurebot: #error {\n :cause "Unable to resolve symbol: . in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: . in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6688]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: . in this context"\n ...

22:59 rhg135: I thought so

23:00 justin_smith: ,(str 'foo '. 'bar)

23:00 clojurebot: "foo.bar"

23:03 rhg135: Ah

23:05 justin_smith: hmm

23:05 ,(intern 'sanbox '. "dot")

23:05 clojurebot: #error {\n :cause "No namespace: sanbox found"\n :via\n [{:type java.lang.Exception\n :message "No namespace: sanbox found"\n :at [clojure.core$the_ns invokeStatic "core.clj" 4032]}]\n :trace\n [[clojure.core$the_ns invokeStatic "core.clj" 4032]\n [clojure.core$intern invokeStatic "core.clj" 6071]\n [clojure.core$intern invoke "core.clj" 6071]\n [sandbox$eval72 invokeStatic "NO_SOURCE_FILE"...

23:05 justin_smith: ,(intern 'sandbox '. "dot")

23:05 clojurebot: #'sandbox/.

23:05 justin_smith: ,.

23:05 clojurebot: "dot"

23:05 rhg135: Box-san!

23:07 Those are a few reasons why I no longer write code after dark

23:07 justin_smith: oh, wait

23:07 ,(def . "dotty")

23:07 clojurebot: #'sandbox/.

23:08 rhg135: ,(def . '.)

23:08 clojurebot: #'sandbox/.

23:08 justin_smith: ,(defn . [] .)

23:08 clojurebot: #'sandbox/.

23:08 justin_smith: ,(apply . ())

23:08 clojurebot: #object[sandbox$_DOT_ 0x50807f2e "sandbox$_DOT_@50807f2e"]

23:08 justin_smith: that is so weird

23:09 rhg135: That'd totally mess with elispers

23:09 justin_smith: seriously

23:09 I mean it scans really weird even to me as a clojurist

23:12 rhg135: .s are symbols too!

Logging service provided by n01se.net