#clojure log - May 20 2016

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

1:04 tolstoy: Hm. UUIDs for web-app routing: maybe a bit much.

2:15 ben_vulpes: tolstoy: i use 'em everywhere!

2:16 tolstoy: #/thing/<giant-uuid>/sub/<giant-uuid>/thing

2:16 Of course, giving up on REST purity helps: just need enough to recover state from a bookmark.

2:16 dysfun: no, just don't do that

2:17 tolstoy: do what?

2:17 dysfun: urls are for humans, not machines

2:17 at least urls for html documents

2:17 tolstoy: This is just hash stuff, after the #.

2:18 dysfun: oh i remember when that was the fashion before we all learned better

2:19 tolstoy: Heh. Well, soon as I figure out how to make it work in this SPA, I'll ditch it.

2:26 Those wankers over at gmail still use # routes. Twiiter, however, has seen the light.

2:27 dysfun: gmail still uses GWT, so, y'know... ;)

2:28 tolstoy: I log in to it one a quarter or so and delete all the spam from the spam folder.

3:58 Keksike: whats a function I can use instead of some, which iterates in the same way but doesnt brake at any point

3:59 so for example I have data {1 foo, 2 bar, 3 baz} and I wanna iterate through all of em and use the key-value pairs

3:59 luma: what do you want to do with the key-value pairs?

4:00 just get a sequence of them?

4:00 Keksike: send them to functions as parameters

4:00 luma: any example?

4:01 Keksike: so what I tried to do was like (some (fn [id value] (my-function id value)) myData)

4:01 where id would be for example 2 and value would be bar

4:01 luma: and you want to call that function for each pair and get out a sequence of the results?

4:02 ridcully: map?

4:02 clojurebot: map is *LAZY*

4:02 opqdonut: Keksike: map, or perhaps doseq

4:02 Keksike: I dont need to get any results, the my-function just saves stuff to db

4:02 opqdonut: then doseq

4:02 since map is lazy

4:02 (or can of course use (doall (map f xs)))

4:02 Keksike: hmm ok thanks

4:03 how would the syntax go in doseq

4:03 luma: (doseq [[id value] my-data] (my-function id value))

4:03 Keksike: right, thanks :)

6:43 jonathanj: ,(clojure.string/join "" ["one" "two" "three"])

6:43 clojurebot: "onetwothree"

6:43 jonathanj: is there a more concise way of joining a bunch of strings with no separator?

6:44 dysfun: (str "one" "two" "three")

6:46 MJB47: ,(apply str ["one" "two" "three"])

6:46 clojurebot: "onetwothree"

6:47 jonathanj: hrm, the order of the arguments to `clojure.string/replace` is kind of annoying.

6:47 dysfun: why?

6:47 clojurebot: http://clojure.org/rationale

6:47 dysfun: :)

6:48 jonathanj: at the moment i have some code that's of the order (s/replace (->> xs (map foo) (apply str)) #"..." ",,,")

6:48 it's annoying that replace can't be part of the threading

6:49 dysfun: not everything has to run through a single thread you know

6:49 jonathanj: the problem is you can't thread -> inside ->>

6:50 dysfun: oh that's a problem is it?

6:50 jonathanj: so as-> might be the only possibility, other than the original

6:50 dysfun: or there's -<> from swiss-arrows

6:51 jonathanj: anyway, it's annoying because if you think about partial application of something like `replace`, isn't the 99% case where you partially apply the replace/substitution stuff and have the string to apply those to be the param?

6:51 certainly that's my experience with string replacing

6:51 dysfun: on this, i'm inclined to agree

6:52 if i'm going to use partial, i'm going to want to change the string more often than the match

6:52 jonathanj: i guess i could write (->> xs a b c #(s/replace % #"..." ",,,")) and that would probably work because the #() only takes one param?

6:53 ridcully: that must be (->> ... (#(...))) right?

6:53 dysfun: actually you'll probably want to put that inside extra parens

6:53 what ridcully said :)

6:54 jonathanj: ridcully: oh right, yeah, that would definitely explain my unexpected result

6:58 ridcully: if your replace #"..." "..." is "fixed", i'd just turn that into it's own defn with a proper name and then you can thread along as you like

6:59 jonathanj: yeah, that was what i was thinking too

6:59 that's probably the choice with the best result

7:04 Rovanion: Hi all! I've got a bunch of database functions which I want to wrap in a try-clause. What would be the equivelent of a decorator in clojure?

7:04 dysfun: there are no decorators in clojure

7:05 what do you mean wrap them in a try clause? are they written in java or clojure?

7:05 Rovanion: So what would be the equivalent of a decorator in clojure?

7:05 dysfun: They end up in the java psql driver so they throw a ConnectionError if no db connection could be established.

7:06 dysfun: okay, so for each function, you want to wrap them in a new function that executes in a try?

7:07 Rovanion: I want to (try (function-i-want-to-wrap) (print "print some error message")) around a whole bunch of db-access functions.

7:07 If that makes it clear.

7:08 dysfun: realistically you're going to factor that behaviour out into a higher order function like this

7:08 (defn safely [f] (try (f) (catch Exception e nil)))

7:08 then dedicate a new namespace to these wrapper functions

7:09 and for each one define it like (defn my-fn [&args] (safely #(.method %)))

7:10 if you want to reduce it further, you will have to use macros

7:14 Bronsa: jonathan youn can always (-> xs (->> (map foo) (apply str)) (s/replace ..))

7:25 Rovanion: dysfun: Thank you, think I got the hang of it now!

7:30 jonathanj: Bronsa: yeah you can but i don't think that adds much value over (replace (->> ...)) personally

7:30 certainly not when you're threading a value through a single function

7:41 tikotus: I would like an indexed sorted data structure. I could use vec and sort it after insert, but only inserting the value at correct location would be more optimal. Any thoughts?

7:45 MJB47: you could use sorted map, but performance wise, is inserting it into the correct position actually faster than just sorting it after the fact?

7:45 tikotus: Inserting in sorted vec is O(n), sorting it is O(nlogn)

7:46 Naturally I could implement an insert function that keeps the vec sorted, but I'm slightly annoyed a ready solution for this doesn't seem to exist :(

7:46 opqdonut: why do you need sorting & indexing btw?

7:47 tikotus: I need a quick way to find element n and then I need it's neighbors

7:47 opqdonut: right

7:47 a sorted-set would work but you can't really get an "iterator" into there

7:47 tikotus: yeah :/

7:47 opqdonut: but yeah, there really aren't any data structure libraries for clojure

7:48 could you use the java SortedSet?

7:49 MJB47: correct me if im wrong, but i think inserting into a sorted array in O(n^2) ?

7:49 opqdonut: sorry I mean NavigableSet

7:49 MJB47: worst case anyway

7:49 opqdonut: see https://docs.oracle.com/javase/7/docs/api/java/util/NavigableSet.html

7:50 tikotus: Worst case would just be iterating through the whole array. I guess it could even be O(logn) with binary search

7:50 Thanks, I'll see what Java has to offer me

7:50 MJB47: interating through the whole array for each element to insert (so * n elements)

7:51 luma: worst case is O(n) because if you need to insert the item in the first place you need to move all items +1 place

7:51 MJB47: so to insert 1 element i guess its n (or logn)

7:51 tikotus: Oh yeah, I'm looking at the case of one element... maybe I should take the whole set into consideration

10:22 Jire: Hey guys, REALLY new to Clojure. I'm using IDEA with Cursive. Creating a project with the Leiningren creator, I can't seem to figure out how to add dependencies. I'm assuming to add for example Netty, I should just define it after the Clojure 1.8.0 like [io.netty/netty-all "4.1.0.CR7"]. Unfortunately it doesn't seem like Netty is in my classpath.

10:23 justin_smith: Jire: if you restart leiningen after adding that, it should see that dependency. Did you try running 'lein cp' from inside the project in the console?

10:24 also I assume your irc client added those smart quotes and you are using real "" in the file

10:24 wait those aren't smart quotes, just me using an unfamiliar font, sorry

10:25 ridcully: also hit the "reload" button in the lein tab in cursive

10:25 Jire: @justin_smith: I don't understand what you mean by "restart" leiningen. Should I restart IDEA?

10:26 justin_smith: Jire: I mean if a repl is running, restart that repl (lein starts the repl)

10:26 Jire: but as ridcully said, reload button

10:27 Jire: Not sure what you're referring to. This is my screen: http://i.imgur.com/VEyxfM3.png

10:28 justin_smith: Jire: I meant a command line

10:28 lein cp, not lein comp

10:28 as in a shell, not the repl

10:28 Jire: I'll install lein and add it to my cp on windows.

10:29 justin_smith: if you have a repl you have lein...

10:29 and that's still not what I meant!

10:29 I mean cp is an argument to lein

10:29 never mind, I guess cursive does things weird, and another cursive user would probably be more helpful

10:30 ridcully: Jire: click that open square in the lower left corner. there is something like leiningen in the list

10:30 Jire: this shows, what i meant with "lein tab". there is a reload button

10:31 Jire: Cool, lein cp did fix it when ran within the project directory. :)

10:32 @ridcully: This seems to be the way to do it without opening up a shell, thanks!

10:33 ridcully: in the past you always had to do it in IDEA, since Cursive would not reload changes to project.clj automatically. don't know, if this changed

10:34 justin_smith: I meant 'lein cp' as a diagnostic, not a fix - just to check if the classpath it shows includes the jars desired (and maybe you'd see some downloads too) - but regardless, sounds like it's sorted out

10:35 ridcully: yet the cp would load any missing deps, right? so restarting the repl would most likely pick up the change too. but autocomplete etc in idea/cursive would still fail

10:36 justin_smith: ridcully: right - I was trying to help as much as I could without knowing the cursive specific stuff

10:36 ridcully: i don't know them either ;)

10:41 Jire: Hmm, seems like all of a sudden the classpath got messed up. http://i.imgur.com/85tyWM3.png

10:41 justin_smith: Jire: that's not the right syntax

10:41 ServerBootstrap is the class, (ServerBootstrap.) creates one, but ServerBootstrap. without parens is invalid

10:42 also, def inside defn is always a bad idea - it doesn't work like def in scheme, it only creates top level bindings

10:43 Jire: So you recommend var defs to reside outside of functions?

10:43 dysfun: yes

10:43 justin_smith: Jire: defs always exist outside the function, regardless of where you create them

10:43 and yes, running def inside the function is a bad idea

10:44 Bronsa: especially since def will intern the Var at compile time, so it will exist even if you never run the function

10:47 Jire: OK, well I'll keep working on this server and leave a Gist if you're interested in reviewing my colossal failure of a first Lisp project. :P

10:47 justin_smith: hah, just trying to be helpful - theres some good docs out there and you might want to start by following a book or tutorial

10:48 best of luck

10:48 Jire: for the stuff you are doing now, I think this might be helpful http://clojure.org/reference/java_interop

10:49 Jire: Just referencing clojure in Y minutes and the clojure site ref. Thanks for the link

12:06 sdegutis: This is neat:

12:06 ,(-> :a #{:a :b})

12:06 Also:

12:06 ,(-> :a {:a :b})

12:06 clojurebot: :b

12:06 :a

12:06 sdegutis: Useful for checking whether a thing is in a thing without some or contains.

12:07 justin_smith: there is also find

12:07 ,(find {:a nil} :a)

12:07 clojurebot: [:a nil]

12:14 dimon_: does Luminus allow creating helper methods which I can use in html templates?

12:16 fijwg: /j emacs

12:16

12:16 dysfun: yes

12:16 it uses selmer

12:16 http://www.luminusweb.net/docs/html_templating.md

12:17 dimon_: dysfun: where?

12:17 dysfun: actually looks like it has options for selmer and hiccup

12:17 which one are you using?

12:18 dimon_: selmer

12:18 is it add-tag! ?

12:18 dysfun: well on that page at the top you will find a table of contents

12:18 do you want a custom filter or a custom tag?

12:19 (links 5 and 7)

12:19 dimon_: now I see, thx

12:20 dysfun: yw :)

12:33 dimon_: I need to "take while" a substring from a string. So that "aaa bbb ccc 999 eee ddd" => take while 999 => aaa bbb ccc

12:33 it's like indexof, then find a substring

12:34 but maybe clojure has a special function for this?

12:37 dysfun: if it has to be efficient, do it with the java string functions

12:38 dimon_: ok

13:16 justin_smith: dimon_: dimon_: that's just (subs s 0 (min 999 (count s))) (which uses the most efficient native method)

13:17 definitely not a take-while of any sort

13:17 oh, wait, you mean take until the first index of 999?

13:21 ,(defn cut-at [a b] (apply subs a 0 (replace {-1 (count a)} [(.indexOf a b)])))

13:21 tolstoy: string/split then take-while normally?

13:21 clojurebot: #'sandbox/cut-at

13:21 justin_smith: ,(cut-at "aaa bbb ccc 999 eee ddd" "999")

13:21 clojurebot: "aaa bbb ccc "

13:21 justin_smith: ,(cut-at "aaa bbb ccc 999 eee ddd" "hhh")

13:21 clojurebot: "aaa bbb ccc 999 eee ddd"

13:22 justin_smith: maybe that implementation is too clever though

13:26 sdegutis: justin_smith: find!?

13:26 I never knew find existed.

13:26 Wow. That's the most useless function ever.

13:26 (doc find)

13:26 clojurebot: "([map key]); Returns the map entry for key, or nil if key not present."

13:27 sdegutis: find should actually be filter + first

13:27 justin_smith: sdegutis: there are often situations where you need to know whether the key is really there, and also what the value is. find does this in a single call.

13:27 sdegutis: justin_smith: what's a good example situation?

13:28 justin_smith: sdegutis: any sitution where nil is a valid entry that doesn't mean the same thing as the absence of the key in the collection

13:29 sdegutis: justin_smith: why wouldn't (get get get get) work there?

13:29 justin_smith: because the nil from get is ambiguous

13:29 sdegutis: ,(get {} :a :404)

13:29 clojurebot: :404

13:29 justin_smith: sdegutis: but it could have :404 in there in the first place, why not just get the actual key/val pair?

13:29 ,(find {} :a)

13:29 clojurebot: nil

13:30 justin_smith: ,(find {:a 0} :a)

13:30 clojurebot: [:a 0]

13:30 justin_smith: ,(find {:a nil} :a)

13:30 clojurebot: [:a nil]

13:30 sdegutis: justin_smith: then why not contains? & get

13:31 (if (contains? m k) (get m k) :404)

13:31 justin_smith: sdegutis: like I said, one call instead of two

13:31 sdegutis: An because of that we now have to settle for two calls instead of one: (->> coll (filter f) (first))

13:32 rather than (find f coll)

13:32 justin_smith: sdegutis: so your complaint is that you think something else should have had dibs on the name?

13:32 sdegutis: Yep.

13:32 I think this is a much more common case.

13:32 To the point where it deserves a core fn more than the other case.

13:33 justin_smith: sdegutis: I assure you that nobody at cognitect said "we would make (comp first filter) a function in clojure.core, but the name we wanted was already taken so fuck it"

13:33 sdegutis: it's not as simple as (comp first filter) btw

13:34 justin_smith: how so?

13:34 sdegutis: ,((comp first filter) pos? [0 1 2])

13:34 clojurebot: 1

13:34 sdegutis: oh

13:35 Sure, they may not have said that. But Clojure's been getting /weird/ ever since 1.5-ish

13:35 dysfun: weird like adding cljc files and unrolled collections to improve performance?

13:35 justin_smith: ,(:added (meta #'find))

13:35 clojurebot: "1.0"

13:35 dysfun: those crazy kids!

13:35 sdegutis: haha

13:35 you guys, actin like i know what im talkin bout

13:36 dysfun: i can assure you we don't

13:36 it's just you show up in obvious need of being corrected ;)

13:36 luma: aww, he mad

13:36 justin_smith: sdegutis: well you sure punked me good, that will show me to go around acting like people have earnest opinions that deserve consideration!

13:37 what a jackass, huh

13:37 dysfun: justin_smith: yeah, he's clearly shown you to be an awful person

13:38 luma: well, now that we're on this topic, here's a similar function that always finds its way into util.cljc: (defn find-where [f v coll] (first (filter #(= v (f %)) coll)))

13:38 dysfun: can you give an example use of it?

13:39 * dysfun is struggling to figure out a good case for it

13:39 luma: (find-where :nick "dysfun" people)

13:39 dysfun: ah

13:41 map-keys and map-vals are common ones

13:42 hiredman: I hate that, like, are you really searching for the first hit so often that you write a function for it, but don't change your data representation so it is a faster operation?

13:42 dysfun: hiredman: that was my first thought too, however i can see it useful for taking apart whatever shitty data you've been given

13:43 hiredman: like, just sort the damn list and use a binary search

13:43 (or actually make it something like a map, or ...)

13:44 tolstoy: It's a process. Sometimes you struggle and come to realize there's just "too much code" or "this should be easier" and then you have a moment of clarity.

13:45 dysfun: sometimes you really don't care about maintaining it for faster future access because that will never come

13:46 tolstoy: 'It'll take me a day to refactor this whole thing and remove 1/2 the code." "No."

14:20 TimMc: Welp, I found out why our server was getting weird hangs periodically with tons of blocked threads implicating taoensso.tower: tower has `dev-mode?` on by default, which reloads the dictionary each time a translation is needed.

14:20 Much sad, wow.

14:21 justin_smith: ouch

14:22 TimMc: and I guess newer versions of tower don't have this problem because of some other caching, but ugh.

14:28 ystael: TimMc: :( :( :( sorry

14:52 sdegutis: Have more people moved to Boot from Lein? Is Boot becoming the new de facto build tool?

14:53 dysfun: no

14:53 people are using boot when they have more complicated things to do than lein caters for

14:53 jballanc: sdegutis: I use both. Lein is a better tool for "libraries", boot is better for "projects"

14:53 dysfun: i'm not convinced about that

14:54 i find boot useful for libraries that do clj+cljs

14:54 jballanc: ah yeah, the clj+cljs story for Lein is still not anywhere near boot

14:54 but how many libraries mix the two?

14:54 dysfun: several of mine...

14:54 sdegutis: jballanc: like, apps?

14:54 how so?

14:55 jballanc: so, complicated deployments, maintenance tasks, etc. are all easier to do with boot since tasks are just clojure code

14:55 sdegutis: Ah.

14:55 dysfun: it is people like me upgrading their libraries to support clojurescript that keeps you in libraries when you're using clojurescript

14:55 sdegutis: The only thing making me consider Boot is that I then get to use AWS Java SDK libraries directly in order to manage deployments from within Cider/Clojure.

14:56 jballanc: dysfun: yeah but are you using clj+cljs *simultaneously*? I might still use Lein for a cljc library

14:56 jonathanj: that seems like a good reason?

14:56 jballanc: sdegutis: that's exactly what we use it for

14:56 ben_vulpes: > deployments

14:56 > from emacs

14:56 you people are on crack

14:56 dysfun: jballanc: my most common workflow is (watch) (speak) (test) (test-cljs)

14:56 jballanc: I have boot tasks that update CloudFormation stacks, managed CodeDeploy deployments, build jars and copy to S3, etc.

14:56 sdegutis: That said, shutting down the app locally and running a small shell script isn't that bad.

14:57 ben_vulpes: do all of your devs use emacs?

14:57 sdegutis: ben_vulpes: mayhaps

14:57 ben_vulpes: who are you asking?

14:57 ben_vulpes: everyone deploying from Cider

14:57 sdegutis: (mayhaps was re: crack)

14:57 ben_vulpes: we only have one dev

14:57 jonathanj: why do you all think that boot hasn't totally replaced lein?

14:57 sdegutis: (me)

14:57 jonathanj: i use lein happily

14:57 ben_vulpes: oh well

14:57 dysfun: because it's got a confusing and horrible api

14:57 and because lein is really quite nice for the 90% case

14:58 tolstoy: lein + actual shell scripts = not too bad

14:58 sdegutis: dysfun: boot does?

14:58 yeah we're easily in the 90% case

14:58 tolstoy: Not cross platform, I guess.

14:58 dysfun: yes, boot's api is terrible and confusing

14:58 jonathanj: why do you say boot's api is horrible?

14:59 sdegutis: jonathanj: are you involved in boot?

14:59 dysfun: because i've used it?

14:59 jonathanj: *in*?

14:59 dysfun: well, what API are you talking about?

14:59 sdegutis: jonathanj: involved in the boot project

14:59 jonathanj: sdegutis: no

14:59 sdegutis: jonathanj: then im really confused why you're getting so defensive about boot criticism

14:59 jonathanj: defensive?

14:59 i'm just asking questions

14:59 dysfun: jonathanj: boot isn't configured with an edn file like clojure, you write clojure to do the build

14:59 sdegutis: well not very defensive

14:59 dysfun: thats the point of it tho

15:00 jonathanj: i have zero investment in lein or boot, so there's really nothing for me to be defensive about, i think you're misinterpreting me

15:00 dysfun: and the way it forces you to work leads to a lot of confusion even once you've gotten the basic principles

15:00 jballanc: boot isn't so bad once you realize it's just ring for filesets ;-)

15:00 dysfun: it's very powerful and shiny and everything, but it fails the "can i come back to it after not doing clojure for a few months?" test

15:02 jballanc: lein is definitely better at the "set-it-and-forget-it" approach

15:02 dysfun: boot is fine *once it is working*

15:03 getting to that point can be an exercise in frustration

15:04 all sorts of oddness too like the 'speak' task causes bizarre sound related errors on my release manager's machine

15:04 tolstoy: Does boot still have that thing where the files it generates are hidden away in a temp directory?

15:04 dysfun: no

15:04 ben_vulpes: > why do you all think that boot hasn't totally replaced lein << because toolchain migrations are expensive, people don't like using new software in production, because the clojure world does not jump willy nilly to the latest and greatest, a million billion reasons

15:05 dysfun: tolstoy: it does have a new and annoying feature, however

15:05 you have to use the (target) task at some point or nothing gets added to your build

15:06 except only once you've put something in boot.properties. when you start up in a new project, it will pester you on every run to tell you to set this property at which point your build will fail!

15:07 well i say fail, it will pass, it's just when you try and build a jar from it, you'll find it's mysteriously empty

15:07 sdegutis: welp

15:07 dysfun: the tl;dr is "use leiningen unless you have a good reason to use boot, then use boot"

15:07 sdegutis: dysfun, jballanc: also boot has a steep-ish learning curve

15:07 jonathanj: ben_vulpes: do you really have to use lein or boot in production?

15:07 sdegutis: i can get a lein project working in 2 minutes flat, but with boot itll be an hour or two

15:08 ben_vulpes: i consider the dev toolchain production jonathanj .

15:08 jonathanj: i mean all my clojure deployments consist of running something to build an uberjar which both boot and lein do equally as well

15:08 dysfun: sdegutis: yes, but once you're up and running, you just copy and paste old files and hope for the best

15:08 jonathanj: i also wasn't talking about jumping willy nilly

15:09 having used boot for dev, i think it's better than lein because you can compose tasks together instead of having every plugin reimplement stuff like file watchin

15:09 dysfun: not that lein couldn't do that itself, of course

15:09 hiredman: when I look at boot, what I see is not the latest and greatest, I see another tire on the fire. which is not to say that lein is great

15:09 jonathanj: couldn't do what?

15:09 ben_vulpes: i personally prefer toolchain stability over more or less anything.

15:10 the last thing i need is to waste a day fucking with the dev or build toolchain.

15:10 well

15:10 above a new hole in my head possibly

15:10 dysfun: i'm inclined to think something inspired by both boot and lein will take us by surprise in the next couple of years

15:10 sdegutis: dysfun: haha so grue

15:10 dysfun: but i think it will mostly cut into boot's market share, not lein's

15:10 sdegutis: /treu/

15:10 hiredman: people create terrible build processes, and instead of fixing the people creating terrible builds, the quick technical fix is to create a new build tool and use that, but eventually people are going to make terrible builds with that new tool

15:10 tolstoy: ben_vulpes Which is why I think "build" should just build, not deploy, create docker images, manage AWS deployments ....

15:10 sdegutis: hiredman: life is complicated

15:11 ben_vulpes: tolstoy: yeah, i lein uberjar and then my custom deployment stack handles it from there.

15:11 tolstoy: hiredman Amen.

15:11 ben_vulpes That means if I had to come in to maintain your stuff, I might actually be able to do it. ;)

15:11 ben_vulpes: tolstoy: that is the goal

15:11 dysfun: hiredman: i think the flexibility boot gives me serves an actual need

15:12 ben_vulpes: tolstoy: it's ALL BASH even.

15:12 dysfun: even if it's just having something workable in the meantime til we fix it in leiningen

15:12 tolstoy: ben_vulpes It's not even the implementation, it's the separation. ;)

15:13 ben_vulpes: right

15:13 hiredman: the js world is particular bad and requiring these heinous build processes

15:14 and somehow convicing people to do that

15:14 and because there is a js component to so many projects now, you see these terrible things getting introduced everywhere

15:14 dysfun: and the rise of ruby projects that require grunt to build...

15:14 ben_vulpes: i excised ruby from a django project recently

15:14 it shelled out

15:15 to compile scss

15:15 tolstoy: The ghost of Make.

15:15 ben_vulpes: so i walked the django compressor version back to be compatible with the fork of django that was in production

15:15 so that i could call python libsass

15:16 aaaaa

15:21 jballanc: boot does benefit from one very distinct advantage: code is more composable than config is

15:21 dysfun: lein benefits from one very distinct benefit: config is much more trustworthy than code

15:22 jballanc: very true

15:22 tolstoy: I think of them as enablers: what's the worst thing lein enables? or boot?

15:23 dysfun: or the best thing they enable

15:23 boot gives me something lein never did: even if my tests don't compile, it'll still watch and reload them until they do

15:23 jballanc: but with boot I've made projects where the developer types "boot dev" and it: downloads and installs datomic, starts a datomic transactor, starts middleman building, starts cljs building, builds and serves a clj server, and restarts the whole thing whenever files change

15:24 with lein you'd need at least 4 terminal windows open to do the same

15:24 tolstoy: I mean that boot allows you to write programs, so you can have an arbitrarily complex build. Lein doesn't encourage that.

15:24 jballanc: ...but then another developer on our team pointed out: that means I have 4 terminals worth of log in one terminal

15:25 tolstoy: jballanc That's what I'm scared of. If I have to maintain your project, and one of those things goes wrong....

15:25 hiredman: lein also limits what people learn about clojure and the jvm, so when things break, they cannot fix them

15:26 https://www.youtube.com/watch?v=ShEez0JkOFw

15:26 jballanc: tolstoy: we try to keep it well factored, so that the definition of the "dev" task is literally "(deftask dev (comp install-datomic run-transactor middleman..." etc.

15:27 so if some part breaks, you can literally just remove it from the task def

15:27 that's part of what I meant about code being more composable than config

15:27 dysfun: yes, but if the install-datomic step breaks, how will you run-transactor ?

15:27 jballanc: manually install datomic

15:27 tolstoy: jballanc You may have written a super clean, wonderful thing: but that has nothing to do with boot. So, my fear is other developers. ;)

15:28 jballanc: tolstoy: other developers are always the problem ;-)

15:28 dysfun: the truth is as a user the build tool is supposed to get out of your way. so your README should contain the commands people need to use to do things like generate docs or run tests

15:28 tolstoy: Indeed. ;) In both directions.

15:28 dysfun: and if you get a lot of reports of your build failing then stop being an idiot and sort your build out

15:34 hiredman: I've worked on large clojure projects, and we used lein, and had, if I say so my self, a very nice build, and we used lein

15:35 so, while boot may be great, the rhetoric about it being better in some way, and some how enabling you to generate value faster, or easier, or whatever, just doesn't scan for me

15:36 so in some ways, my response is reflexive, I seeboot being talked up, based on an stance that is, in my experience, incorrect

15:37 dysfun: and how much effort did you put into getting your nice build?

15:38 hiredman: some of that is also just different ways of doing things, booting a datomic transactor in a boot task, I've done stuff similar, but typically as a clojure.test fixture, not a task in a build system

15:39 there are basically a million ways to skin that cat, and to point at boot and say it is good because you can do one of them, is just laziness

15:41 which is not to say boot is not great, this is not a rant against boot

15:41 ridcully: isn't lazyness the trait of the developer?

15:42 hiredman: not laziness in thought at least, and maybe not other kinds of laziness

15:42 ridcully: as in shortest route

15:44 hiredman: when you work with tools, a tool can have two (hopefully) complementary kinds of beauty that attract you to it, and make you want to use it

15:44 \

15:45 ben_vulpes: README++

15:45 hiredman: type 1 is the beauty of the tool, the tool itself is well made, feels good in the hand, has good vibes

15:46 type 2 is the beauty of the results, the tool produces good results

15:47 type 1 is subjective, type 2 tends to be more objective (but really it depends on the subjectivity or objectivity of the judgement of the results)

15:51 lost my train of thought, but if discussions about tools center on type 1, then that is mostly noise

15:51 ridcully: in the end only type2 counts - unless it's from bizarro-type-1 world (incredible ugly and hard to use)

15:53 dysfun: type 1 does matter. tools that make you feel good make you want to use them more

15:53 and that can be the difference between getting a thing done and not when you're having a bad day

15:56 jballanc: another way to look at boot vs lein I've used is: lein is a build tool, boot is a task runner

15:56 building code is *a* task that boot can run, but it can run other stuff too

15:57 for example, you could use boot to stand-up an entire ETL workflow, and you'd only have to write the T

15:57 but since lein is more focused on *just* building, it does a better job at building

15:58 dysfun: does it? i think they're all much of a muchness as far as building is concerned

15:58 jballanc: doing the equivalent of `lein uberjar` in boot is still somewhat of an exercise in frustration

15:58 (at least the first time)

15:58 dysfun: 'boot uber jar' is hardly much different

15:58 hiredman: so why couldn't you do the etl thing (whatever your conception of it is) in lein?

15:59 I tend to not using lein for a lot of things either, and just write clojure shebang scripts

15:59 jballanc: hiredman: how do you tell lein to slurp up a directory's worth of files and run some code against them?

15:59 that's boot's bread-n-butter

16:00 hiredman: jballanc: exactly, that to me, is obviously something my application should be doing

16:00 jballanc: dysfun: until you try to pre-compile some ns and include some resources...

16:00 hiredman: right, which is what I mean when I say that boot is a task runner

16:00 dysfun: i don't use aot. that's asking for trouble regardless of your build tool

16:00 jballanc: it becomes one with your application

16:01 lein always stands alone as "the tool"

16:01 but boot is much more open to melding with app code

16:01 hiredman: that sounds terrible :)

16:01 ridcully: i was just pulling some philosophical legs. lein has ~(...) and run/alias which makes it also somewhat "imperativeish". after all it's a darn fine tool, but i am glad we have both to pick from

16:01 dysfun: it probably *is* terrible, but it's quite handy :)

16:02 jballanc: hiredman: it's really not much different from the attitude of "it's just a library" that pervades the clojure mindset

16:02 TimMc: ystael: Haha, not your fault -- I'm actually filing a bug on tower to make that default setting more obvious.

16:03 jballanc: half the time I'm invoking boot tasks from the REPL...that's not something I've ever done with lein

16:04 (not something I think you *can* do with lein)

16:04 hiredman: jballanc: I think there are afew things to saw there, but I think the biggest is one is I think you have your arrows reversed, an all singing library sounds more like a framework to me

16:04 you could

16:04 dysfun: 'half the time'? wow, you've got quite a dynamic workflow

16:04 jballanc: you have no idea

16:04 hiredman: I fiddled around with it, writing an nrepl middleware

16:05 dysfun: i can recommend having a release manager who will tell you to stop being an idiot if you fuck the build

16:05 hiredman: didn't care for it enough to actually complete it

16:05 jballanc: boot definitely sits near, if not on, the border between framework and library...really comes down to how you use it

16:06 we tend to view it as "the thing that manages files and classloaders"

16:06 which, btw, even if you don't use boot as boot, you owe it to yourself to check out Pods

16:06 a-maz-ing

16:06 dysfun: pods are the killer feature of boot

16:07 they're why i can still have autotest-like functionality even when i've not compiled my code in half an hour

16:07 hiredman: *shrug* I have fiddled with classloader sandboxing

16:07 ben_vulpes: > pods

16:07 triggered

16:10 hiredman: back in the dawn of time, there was a library called fnparse, which was released, but tied to an old version of clojure (or clojure-contrib)

16:11 I used fnparse for some parsing inside clojurebot, so clojurebot had to stay on that old version of clojure

16:12 at the time clojurebot evaluated code in the same clojure runtime as it ran

16:13 so I started looking for alternatives, including jboss module sandboxing type things

16:14 for complete isolation, you end up needing to serialize stuff that gets passed across the boundary anyway, so you may as well just run things as services

16:14 which is what I ended up doing

16:15 https://github.com/hiredman/polycosm

16:19 jballanc: I hear ya, and for more complicated things I definitely agree

16:19 with boot it's nice to be able to do simple stuff (like run JRuby alongside a cljs build) without worrying

16:20 I've tempted fate to even use Pods to run multiple compojure apps in the same JVM...but haven't actually finished that little project

16:20 I might have a different oppinion of pods if I do

16:23 hiredman: so, to me, that sounds pretty typical of what I hear from people pushing boot, completely ignoring the fact that exists an entire java ecosystem dedicated to being able to take a bunch of webapps and run them together in the same process

16:24 in fact, pods are built on a project called shimdandy, which is from project odd, which is a jboss project, which is a big part of that ecosystem

16:25 like, stop building whatever, just deploy wars to immutant, and you are done

16:26 dysfun: i'm so very very unconvinced that war files are the right solution

16:35 sdegutis: Honestly though, I felt like this was the most accurate Spider-Man yet.

16:35 justin_smith: accurate spiderman: boy gets bit by radioactive spider, cancer, death.

16:36 ridcully: it's pronounced "hired"

16:36 dysfun: boy doesn't get bitten by radioactive spider because that sort of dose of radiation would kill the spider

16:36 sdegutis: boy gets "hired" by a radioactive spider?

16:41 domgetter: Is it possible to memoize a recursive function after it's been written?

16:42 hiredman: yes

16:42 sdegutis: domgetter: sure

16:42 ,((fn foo [] foo)) ;; /cc domgetter

16:42 clojurebot: #object[sandbox$eval25$foo__26 0x3ddb547d "sandbox$eval25$foo__26@3ddb547d"]

16:42 sdegutis: domgetter: that's how you name a function within itself

16:43 hiredman: it is sort of complicated by certain optimizations the compiler may try to do

16:43 and that assumes you defing a function

16:44 sdegutis: ,(let [f (memoize (fn foo [x] (prn x) (if (pos? x) (foo (dec x)) x)))] (f 5) (f 5))

16:44 clojurebot: 5\n4\n3\n2\n1\n0\n0

16:44 sdegutis: domgetter: boom done

16:44 justin_smith: but is it calling the memoized function or the original?

16:44 domgetter: That doesn't memoize the inner calls

16:45 sdegutis: justin_smith: there's only one 5

16:45 domgetter: ,(let [f (memoize (fn foo [x] (prn x) (if (pos? x) (foo (dec x)) x)))] (f 5) (f 6))

16:45 clojurebot: 5\n4\n3\n2\n1\n0\n6\n5\n4\n3\n2\n1\n0\n0

16:45 hiredman: in the past defn macro would insert the functions name as a local binding in the scope of the function body, which means any self recursive calls would not go through the var, which means even if you altered the function in the var to me memoized, recursive calls wouldn't be memoized

16:45 sdegutis: domgetter: wait, you want to rewrite it? wha?

16:45 hiredman: yeah

16:45 exactly the thing you are doing

16:45 * sdegutis is so confused

16:45 hiredman: memoize doesn't change a function, it returns a new function

16:46 foo is bound to the function you create there

16:46 sdegutis: domgetter: ooh I see now

16:46 domgetter: That's why my question was "after it's been written"

16:46 :P

16:46 sdegutis: you want to memoize calls to (foo) within (fn foo ...)

16:46 I see

16:46 * sdegutis out

16:46 hiredman: well it is slightly more complicated than that, because global functions with mutable vars will be slightly different

16:47 without some mount of mutablility between the binding and the function, the answer is no

16:47 domgetter: That makes sense.

16:54 makufiru: Clojure just feels like the hardest language to get started with. :/ I mostly think it's the burden of trying to learn emacs alongside it. Also not knowing Java doesn't help, those stack traces.

16:55 dysfun: makufiru: if it helps, i've been learning lfe recently and i found it harder going in the beginning than clojure

16:55 domgetter: makufiru: Have you tried going through Clojure for the Brave and True?

16:55 makufiru: @domgetter I haven't yet. I'll look into that, thank you

16:56 dysfun: yeah I don't envy you on that one. haha

16:56 domgetter: makufiru: http://www.braveclojure.com/introduction/

16:56 hiredman: don't dismiss the stacktraces, they are fantastically useful, you should learn to read them

16:57 makufiru: Oh really? I thought it was just a bunch of junk being spit out as a result of running on the JVM haha

16:57 dysfun: or at least to scan through them enough to recognise what is talking about your code

16:57 CStorm: i will second the book for the brave and true, really good

16:57 will setup you up with both emacs and clojure

16:58 and will learn you enough to get going

16:58 hiredman: people who complain about stacktraces don't read them, and spend their time in chatrooms asking for help from people that do

16:58 dysfun: that's not necessarily true. i complain about stacktraces precisely because i read them

16:59 and i can't help but notice that it's often quite hard to find your code in amongst all the other frames

17:00 makufiru: I'm used to the excellent error messages of Elm/Rust haha. So the stacktraces feel like trying to intuit ancient scrolls

17:00 CStorm: haha

17:00 dysfun: elm has a repository for confusing error messages so they can work on improvements

17:00 they take it very seriously

17:00 makufiru: It goes a long way. I can personally vouch for how awesome it is

17:01 hiredman: stacktraces and error messages are not the same thing though

17:01 dysfun: i'm not with you on the rust error messages though. way too many bad ones in there

17:01 makufiru: I haven't had very many bad ones yet

17:01 admittedly just doing a lot of basic stuff with it

17:02 dysfun: you don't tend to deal with stacktraces much in either elm or rust, however

17:02 hiredman: a stacktrace shows you the call the callstack that produced an error, which is useful information aside from an error message

17:02 ridcully: if you are used to some "seven things went wrong here" of e.g. python the trillion lines of java can be quite daunting

17:03 it's a skill to hone, to "interprete" the actual error

17:06 clyfe: https://github.com/AvisoNovate/pretty makes stacktraces a bit more bearable

17:06 momerath_: there's some tradeoffs between usefulness (to some), friendliness (to beginners and the common case), and performance in stack traces, but there's also a lot of work to do before you really run up hard against those tradeoffs. Clojure could use some work, but the stack traces are plenty useful, especially if you capture them as objects.

17:08 dysfun: there aren't really performance implications. hotspot will happily optimise out exceptions in hot paths

17:09 (and with it your debuggability... c'est la vie)

17:10 momerath_: keeping the trace data is overhead, even if you can avoid affecting the performance characteristics of the app on a not-overloaded machine

17:11 dysfun: hotspot will simply optimise out traces in hot code paths

17:11 momerath_: and indeed, in some cases, they throw out your production debugability - i'd call that a tradeoff

17:11 hiredman: actually, the jit will throw the stacktrace away in some cases, and you'll get an exception without a stacktrace

17:12 everyone pretty much hates that and turns it off, because they wan't the stacktrace

17:12 (the jvm jit, it is a jvm setting)

17:12 momerath_: we know

19:12 tolstoy: I kinda wish stack traces would print the error at the top AND bottom of the trace. :)

19:14 Most of the time, all I need is the error message, but having to search backwards for it tempts me into JUST printing the error message.

21:12 jumanji: hi fellas I'm new to clojure can I please get some help http://stackoverflow.com/questions/37357895/how-to-group-collection-of-datoms-tuples-and-get-total-sum-of-its-attribute-v

21:16 patham9_: https://gist.github.com/patham9/e85e18b390c70221614133d6a87be31e <- how can this be explained?

21:16 these should be equal collections but for some reason it isnt consistent here

21:18 tolstoy: Maybe (prn ...) that last one to see if there's no an errant string in there where you don't expect it?

21:19 patham9_: there is no string involved in the comparison check

21:19 just clojure data structure

21:20 as the gist shows

21:20 tolstoy: I was thinking about that case where when you print something, the strings aren't quoted, but you're right, that doesn't seem to be an issue here.

21:21 Maybe do a (type ...) to see? I don't know.

21:21 patham9_: clojure claims that datastructures are the same if they have the equal entries. this is the case here. however the equality check fails

21:21 will try

21:23 1 is a clojure.lang.Symbol oh ^^

21:23 domgetter: what does quoting a vector do?

21:23 patham9_: while the other 1 is a Long ^^

21:24 tolstoy: I think quoting a vector quotes all the things in the vector?

21:25 ,(map type [1 2 *])

21:25 clojurebot: (java.lang.Long java.lang.Long clojure.core$_STAR_)

21:25 tolstoy: ,(map type '[1 2 *])

21:25 clojurebot: (java.lang.Long java.lang.Long clojure.lang.Symbol)

21:26 domgetter: oh, interesting

21:27 tolstoy: Anyway, I was thinging that patham9_'s issue was that not all the values in each vector were the same, even if they printed as if they were.

21:27 patham9_: yes, as I said, type mismatch between Symbol and Long

21:27 thanks a lot for showing me the type comand

21:27 *command

21:28 made a quoting mistake

21:28 inside of my functions

21:37 hm its very interesting that a number was able to become a symbol

21:37 i wonder how I can do this on purpose

21:37 ah (symbol (str 10)) of course

Logging service provided by n01se.net