#clojure log - Jul 04 2015

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

0:22 turbofail: finally taking the plunge back into cljs-land

0:22 it's been a while

0:23 vas: Committee on the Jewish Law and Standards? :P

0:24 turbofail: i think you transposed some letters

0:24 unless it's one of those reordered french initialisms

0:25 vas: it must be. first result on duckduckgo when i searched "cljs"

0:29 Would you recommend cljs over clj for a dynamic web application?

0:32 turbofail: eh? i would normally use both

0:32 cljs for the front-end, clj for the backend

0:33 i suppose you could use node.js on the server end but i'd generally prefer JVM clojure when available

1:43 zematis: I'm trying to get sum the values of :key in [{:key 57 :other-key 72} {:key 27} ...]. Any suggestions?

1:44 tatut: ,(reduce + (map :key [{:key 1} {:key 10}]))

1:45 clojurebot: 11

1:46 TEttinger: ,(reduce + (map :key [{:key 1} {:key 10} {}]))

1:46 clojurebot: #error {\n :cause nil\n :via\n [{:type java.lang.NullPointerException\n :message nil\n :at [clojure.lang.Numbers ops "Numbers.java" 1013]}]\n :trace\n [[clojure.lang.Numbers ops "Numbers.java" 1013]\n [clojure.lang.Numbers add "Numbers.java" 128]\n [clojure.core$_PLUS_ invoke "core.clj" 955]\n [clojure.lang.ArrayChunk reduce "ArrayChunk.java" 63]\n [clojure.core.protocols$fn__6514 invoke "...

1:46 TEttinger: ,(reduce + (remove nil? (map :key [{:key 1} {:key 10} {}])))

1:46 clojurebot: 11

1:46 TEttinger: good suggestion though, tatut

1:46 (inc tatut)

1:46 lazybot: ⇒ 1

1:47 zematis: Thanks. Just learning functional programming now :)

1:47 TEttinger: it's a great fit for a lot of problems

1:47 a lot of people (including me at first) have a hard time adapting to not mutating stuff all the time

1:48 zematis: I'm having fun. Should probably be learning java more deeply first for my career, but it's not as interesting.

1:48 TEttinger: it eventually becomes second nature though. learning java at the same time as clojure could be interesting, but they both can use java libs!

1:49 tatut: the problem statement said nothing about :key potentially missing! ;)

1:49 zematis: Yep. That's one of the things that led me here instead of haskell.

1:49 tatut: It shouldn't be in my use case

1:50 TEttinger: I've definitely gotten used to switching languages a lot. contributing to a java lib, writing abhorrent C# for asset processing for a game, writing clojure snippets to solve what would be ugly tasks in those languages (in a fraction of the code, usually), lua a lot lately

1:52 I've noticed that the only thing that really trips me up is whether I need semicolons at the end of a line or not when I switch from lua or perl to java or C#

1:53 lisp-style code doesn't have this problem :)

1:53 kungi: TEttinger: The last time I switched to lua I immediatly got stumped by its "data structure"

1:53 zematis: I've noticed that problem too. Whenever I move from C/Java to python my lines end up with semicolons at the end for the first few minutes.

1:53 TEttinger: kungi, yeah the table is interesting

1:54 metatables are very handy, writing a lib that uses them now

3:51 Pupeno: I'm proud of my tic-tac-toe solution. It's the first time I feel I was *thinking in Clojure*: https://www.refheap.com/105203

4:48 TEttinger: nice Pupeno

11:12 biellls: Hi everyone

11:12 Does anyone know why (class 'true) returns Java.lang.boolean instead of symbol?

11:13 ,(class 'true)

11:13 clojurebot: java.lang.Boolean

11:13 biellls: ,(class 'somename)

11:13 clojurebot: clojure.lang.Symbol

11:13 justin_smith: ,(class '1)

11:13 clojurebot: java.lang.Long

11:13 justin_smith: ,(class ':foo)

11:13 clojurebot: clojure.lang.Keyword

11:13 justin_smith: ,(class '[])

11:13 clojurebot: clojure.lang.PersistentVector

11:14 justin_smith: there must be a reader specifically for true (and it makes sense that there would be)

11:15 biellls: It's just that I've been trying to port the scheme metacircular evaluator in SICP, and this makes some things not work

11:16 I wondered if there was a good reason why it's implemented like this

11:17 justin_smith: biellls: self evaluating things in clojure evaluate to themselves when quoted. There's a consistency to that.

11:17 dnolen: biellis: ' doesn't create symbols, it's just quoting

11:17 justin_smith: oh yeah

11:17 dnolen: if '(1 2 3) produced symbols that would be pretty useless

11:17 justin_smith: ,((juxt identity type) (symbol "true"))

11:17 clojurebot: [true clojure.lang.Symbol]

11:18 biellls: True

11:18 I just thought that quote meant that it wouldn't be evaluated

11:19 justin_smith: biellls: that's why I mentioned readers

11:19 dnolen: biellls: except some things self-evaluate, pretty sure this is the case for Scheme as well

11:19 justin_smith: reading and evaluating are separate

11:20 Bronsa: biellls: it's not evaluated indeed. it's read as a symbol

11:20 ,(class (read-string "true"))

11:20 clojurebot: java.lang.Boolean

11:20 Bronsa: err, i meant as a boolean

11:21 biellls: as you can see, in the expression `(quote true)`, `true` is already a bool at read-time. just as 1 is a number and :foo a keyword

11:21 biellls: In a scheme repl 'true returns 'true

11:21 Bronsa: same applies for nil and false

11:22 biellls: OK thanks

11:22 justin_smith: biellls: what about '#t

11:22 true isn't special in scheme is it?

11:22 dnolen: biellls: (eq? '#t #t) => #t

11:22 Just tested this in Petite Chez Scheme

11:22 biellls: You're right, '#t evaluates to #t

11:23 dnolen: same for (eq? '1 1) => #t

11:24 Bronsa: biellls: and it's the same in cl, t is equal to 't, nil is equal to 'nil

11:24 biellls: I guess that's the difference then, true isn't special in scheme and it is in clojure

11:24 Bronsa: biellls: but #t is special in scheme and is not in clojure. they just have different representations

11:26 biellls: I guess the only way around is to rename true and false to something else in my evaluator

11:27 justin_smith: why not #t and #f - there's a nice symmetry to that

11:27 dnolen: biellls: note SICP likely used true and false because they *couldn't* use #t and #f

11:27 biellls: but you *can*

11:27 biellls: ,'#t

11:27 clojurebot: #<RuntimeException java.lang.RuntimeException: EOF while reading>

11:28 biellls: It throws an exception In my repl

11:28 justin_smith: oh, maybe not, :(

11:28 biellls: haha

11:28 dnolen: biellls: er, sorry # is a reader thing

11:28 Bronsa: biellls: yeah you can't use the clojure reader for that

11:28 dnolen: biellls: in core.logic I ended up using t# and f# I think in the early days.

11:28 Bronsa: dnolen: that's going to be weird with `

11:29 dnolen: Bronsa: yeah in my case it didn't matter much. I don't remember enough about the metacircular evaluator chapter in SICP whether it matters much

11:29 Bronsa: pretty sure it doesn't, they don't use splicing in quoted forms.

11:30 biellls: dnolen: Good point about using true and false because they couldn't use #t and #f, I hadn't thought about that

11:31 I should implement a lisp reader sometime to better understand how they work

11:34 Bronsa: biellls: oh, do so, you can write one in just a hundred-ish lines of code

11:34 assuming you just want s-exps and not the fancy clojure extensions on top of it

11:37 biellls: I will do it when I finish SICP (if it's not part of the book)

11:37 Thanks justin_smith dnolen and bronsa

11:47 I'd like to ask an unrelated question about something I've been thinking about doing, but I don't know if it's a good idea

11:47 tmtwd: in lein repl I'm doing (load-file "bob_test.clj") and it doesn't work

11:48 file not found exception

11:49 nvm

11:49 fixed it

11:49 biellls: Usually clojure is fast enough, but sometimes you need speed that is close to java, and optimized clojure is not pretty

11:51 But we don't really like working in java

11:52 I was thinking of making a language that is similar to C in syntax and compiles to Java or JVM byte code and has syntactic sugar to make working with clojure data structures and calling clojure functions easier

11:53 luxbock: isn't Java a language that has syntax similar to C and compiles down to JVM byte code?

11:54 biellls: Yes, but with similar to C I also meant not OO

11:57 justin_smith: biellls: but OO isn't a syntax

11:57 oh, "also", missed that, sorry

12:00 biellls: I didn't explain myself well

12:01 But check a hello world in C and Java to see what I meant

12:03 Also, it would make it easier to manipulate clojure data structures

12:03 And call clojure functions

12:06 My motivation was partly because I read somewhere that making a mixed clojure and java project was a painful experience

12:07 I haven't tried it yet, so I don't know if that's accurate

12:07 justin_smith: biellls: depends which API you use I think, and the using java from clojure part is easier

12:20 biellls: ok, I will try to do a mixed java and clojure project sometime and see if I run into any difficulties.

12:28 tmtwd: how do I check the last character of a string in clojure

12:28 ?

12:29 luxbock: ,(last "foo")

12:29 clojurebot: \o

12:30 ringer1: ,(last "foo")

12:30 clojurebot: \o

12:30 tmtwd: ah okay

12:30 how come I get this error? http://pastebin.com/8jbJUHw4

12:31 ringer1: very cool (first visit here and I am easily impressed)

12:31 andyf: jlast is linear time in length of string. If you want constant time access to last char, try (get s1 (dec (count s1)))

12:32 oddcully: tmtwd: :else is no function

12:32 andyf: tmtwd: You've got incorrect extra parens in your cond

12:32 tmtwd: where?

12:32 justin_smith: ,(:else {:else "is kind of a function, but not like that"})

12:32 clojurebot: where is log

12:32 "is kind of a function, but not like that"

12:32 andyf: Clojure's cond is different than Common Lisp's

12:32 tmtwd: okay

12:32 ill check out the else docs

12:32 i'm used to scheme and stuff

12:32 oddcully: tmtwd: well it could, but here you just want to have it as the final clause

12:33 Bronsa: tmtwd: it's not correct even without the else btw

12:33 andyf: (cond (= "" arg) "Fine" :else "unknown")

12:34 biellls: :else is just convention though, right?

12:34 tmtwd: oh I see

12:34 biellls: Any keyword is truthy

12:34 Bronsa: biellls: yes

12:34 tmtwd: so this is one of the ways clojure ruthlessly dispenses with parens :)

12:34 Bronsa: you can use whatever truthy value you want

12:34 andyf: tmtwd: When it doesn't replace them with square brackets, yes :)

12:41 bensu: git 1git

12:41 sorry, wrong app

12:41 tmtwd: http://pastebin.com/0yT8gtCv is this the right way to check that '?' is the last character of the parameter

12:41 ?

12:42 I checked in the repl, it seemed to suggest yes, but I'm failing some tests

12:42 biellls: ,(last "Hello")

12:42 justin_smith: tmtwd: oh, to test the last char, the best bet is .endsWith

12:42 clojurebot: \o

12:42 biellls: Returns a character

12:42 justin_smith: ,(.endsWith "hello" "o")

12:42 clojurebot: true

12:43 biellls: Or use \?

12:43 justin_smith: tmtwd: here's your issue ##(= "a" \a)

12:43 lazybot: ⇒ false

12:44 justin_smith: biellls: the advantage of endsWith is it doesn't need to turn your whole string into a seq

12:44 it's a well optimized built in method

12:45 biellls: ok, thanks

12:45 tmtwd: ah i see

12:52 biellls: Just tested it

12:52 Strangely last runs faster in my computer

12:54 justin_smith: biellls: criterium says .endsWith is 250x faster

12:55 biellls: cite https://www.refheap.com/105306

12:56 well, not 250x

12:56 ,(/ 231.5 1.566)

12:56 clojurebot: 147.82886334610473

12:56 justin_smith: ~150x

12:56 clojurebot: Pardon?

12:57 justin_smith: that's still an insane difference in execution speed

12:57 biellls: Cool, I guess I will have to upgrade my benchmarking tools

12:57 I used the time macro

12:57 oddcully: maybe measuring unrealized lazy stuff?

12:57 justin_smith: criterium is pretty great

12:57 oddcully: last of a string?

12:58 oddcully: justin_smith: yeah. just guessing.

12:59 justin_smith: oddcully: a random gc hit could throw time off easily

12:59 but anyway, criterium is generally reliable, even though it takes ages to run

12:59 oddcully: well just guessing. i have not seen the benchmark of OP

13:00 justin_smith: best profiles.clj deps for dev: pallet/alembic, criterium, tools.trace

13:00 imho

13:04 andyf: .endsWith can be slow if there is reflection happening (in general, any Java interop call is subject to this slowness)

13:04 (set! *warn-on-reflection* true) can help find these

13:04 type hints to help eliminate them

13:04 justin_smith: true

13:05 I wonder if a type hint would speed up the implicit seq call in the version using last

13:08 vas: happy 4th friends

13:10 justin_smith: nope, can't even type hint a string literal (d'oh, of course)

13:12 csd_: Hi, i'm trying to understand the purpose of the macros in the attached. What purpose do they serve here? http://pastebin.com/hqxXPH5z

13:12 From the apache storm source

13:12 justin_smith: csd_: it means that if the user pases +, your code gets #'+

13:13 (for the clojure-bolt macro that is, same for conf-fn-system)

13:13 tmtwd: how to check for an empty string? ie "" or " "?

13:13 .emptyString? arg?

13:13 justin_smith: ,(defmacro pr-var [x] `(pr (var ~x)))

13:13 clojurebot: #'sandbox/pr-var

13:14 justin_smith: ,(pr-var +)

13:14 clojurebot: #'clojure.core/+

13:14 csd_: justin_smith: so the purpose of the macro is to pass a quoted var to the fn?

13:14 justin_smith: csd_: right, like most (every?) macro, the only reason you actually need it is syntactic convenience

13:15 csd_: im surprised that this couldn't be pushed down to the fn* though

13:15 justin_smith: csd_: a function can't decide how it's args are interpreted

13:15 ,+

13:15 clojurebot: #object[clojure.core$_PLUS_ 0x40ecd6ee "clojure.core$_PLUS_@40ecd6ee"]

13:15 justin_smith: you can't get #'+ from that

13:16 I mean you could do a tree search of interned values, but there isn't a *sane* way to get #'+ from that

13:16 s/tree search/linear search/

13:17 csd_: justin_smith: why do the args need to be wrapped in (var)?

13:17 justin_smith: csd_: probably because they want to embed them in a closure, but ensure that changes to the var are reflected in the running code

13:18 csd_: if you just passed in + as an arg to a higher order function, the resulting code would not see any redefinition of + during dev

13:18 (here's where + is a less illustrative example, of course)

13:19 csd_: when you call a var as a function, the value is looked up each time, ensuring that you see redefinitions without having to recreate your closure (and probably do some stateful stop/start thing)

13:20 csd_: so i could for example bind the (var foo) to something within a let binding, run that concurrently, and change the value across the different threads and have it be safe?

13:20 justin_smith: right, vars are thread-safe

13:22 csd_: unrelated, i'm looking for examples of well written clojure applications. storm and cascalog seem like good potential ones. can you think of any others?

13:23 justin_smith: csd_: anything from weavejester

13:23 csd_: ztellman's libs can be very enlightening

13:23 any of the prizmatic stuff

13:23 *prismatic

13:24 csd_: yeah those are all libraries though

13:24 right?

13:24 justin_smith: yeah, I missed the applications part, sorry

13:24 csd_: circleci

13:27 csd_: ok

13:28 thanks let me know if you think of any others

13:28 justin_smith: I know of lots of apps (I wrote most of the code even...) but can't share them :)

13:30 csd_: i wish there was a good resource on writing large clojure apps. Seems to me like there's a need for that

13:30 justin_smith: yeah

13:30 it would be a lot of work

13:30 csd_: yeah

13:31 justin_smith: but between component and prismatic/schema, I can see how a lot of the big picture structure and smaller scale specification would work, so that's a head start I guess

13:32 csd_: i never feel comfortable with how i should handle program state, i.e. whether to have the so called God object or what

13:32 justin_smith: component sorts that out nicely for me

13:33 there's individual components responsible for some data, functionality or stateful resource, then a map describing who needs to use which

13:33 csd_: I feel most comfortable right now defining everything in `def`, within a large hashmap

13:34 justin_smith: have you tried stuartsierra's component lib?

13:34 csd_: then i can do something like (:path-cache @platform) and be returned the Java cache object i need

13:34 justin_smith: because it's very close to that

13:35 csd_: i've looked at it but haven't tried it. even that though, when looking at the docs, i'm confused about where to define the objects initially

13:35 justin_smith: csd_: you define a record, that has a start method, that creates and initializes all stateful objects, and can if you wish also capture other stateless things to provide alongside

13:36 csd_: but is the record initialized within a def, or within a function?

13:36 that's what trips me up

13:37 it's sort of splitting hairs ultimately

13:37 justin_smith: csd_: the normal thing is to use def to create the record, then use alter-var-root to run the start method that actually creates the stateful stuff

13:37 it's one top level record

13:38 but I have one case where I put the component inside an agent, because I want to be able to create it fresh inside a launcher, without using a def-inside-def, and unlike an atom an agent won't retry (which would be silly for stateful resources)

13:39 csd_: retry in what sense?

13:40 justin_smith: csd_: atoms retry when concurrent modification attempts happen

13:40 while agents simple lock and do one at a time

13:40 *simply

13:41 csd_: i need to sit down sometime and just go through clojure.core. there's so much stuff in there that i don't know much about

13:43 justin_smith: thanks for letting me throw questions at you, gonna go make some lunch

13:44 justin_smith: np, headed out for an errand myself

13:46 donbonifacio: been migrating a 4K LOC of clj to cljc. tests on lein: 220ms, node: 90ms

13:55 tmtwd: how do I switch directories for cider?

14:27 scottj: tmtwd: maybe M-x cd?

14:27 tmtwd: be more specific

14:28 tmtwd: let me try M-x cd

14:31 yeah, my M-x cd directory is in the right directory, but when I try to (load "myfile.clj") it does not work, filenotfound exception

14:31 what is the C-c C-l of cider?

14:31 iow

14:32 scottj: C-c C-l

14:33 cider-load-file

14:34 rs0: has anyone looked at Clojure<->Neovim integration?

14:35 Neovim now has an experimental built-in terminal emulator. it seems like lein integration could consist of "start a terminal running lein within vim, send it text from some other buffer"

14:35 in other words, exactly what emacs has apparently been doing since the Carter administration or something

14:35 justin_smith: rs0: that's what inferior-lisp does

14:35 rs0: what cider does includes getting doc strings

14:36 autocomplete

14:36 showing args as you use a function

14:36 rs0: true. i've used emacs and slime a few times when playing with common lisp

14:36 lodin_: Anyone else feel a need for a good lens implementation in Clojure?

14:37 rs0: lodin_: why?

14:38 lodin_: lens kind of strikes me as a defrecord analogue for haskell, but with more type theory, and more feature pragmas (e.g. template haskell)

14:38 lodin_: rs0: Several reasons. One is that update-in only takes so you far. It does not update over every element in a vector, for instance. Another is that polymorphic lenses make for good interfaces since you can "add keys ad hoc" and don't need to rewrite data structures to fit a certain format.

14:39 rs0: i don't understand your second point, but it sounds interesting

14:39 do continue

14:40 when i was looking at lenses i was desperate for a clojure-style "what problem are we trying to solve?" treatment

14:40 lodin_: rs0: If you have a record that has all the keys that you need, but with the wrong names, e.g. :user instead of :username, then you need to write the record.

14:41 s/write/rewrite/

14:41 or at least augment.

14:42 If you instead of doing (get info :username) you do (view info username), then any record (if you dispatch on types) can implement username and "redirect" to :user.

14:43 rs0: the "key name mismatch" problem strikes me as a bit fishy

14:43 i'd need to see it in a practical context

14:44 lodin_: rs0: Would it be better if you had something with units in it? The typical example would be fahrenheit vs celsius.

14:45 (Then you'd use an iso.)

14:45 justin_smith: lodin_ so you'd have one record, and some code could update it in farenheight and other code could update in celcius to control the same value?

14:47 lodin_: justin_smith: Yes, or you have two records, one using fahrenheit and one using celsius and a function that does (view stuff temperature) and expects fahrenheit.

14:47 rs0: i dunno... i'm reminded of rich hickey's rule of thumb that "if your data is making decisions, your design is wrong"

14:47 this sounds a bit like a return to Smart Data

14:47 justin_smith: rs0: it's the lens making the decisions though, and lenses are not data

14:48 tmtwd: whats the difference between defn and defn-?

14:48 rs0: tmtwd: defn- is private

14:48 tmtwd: rs0, to the ns?

14:48 rs0: tmtwd: it defines a function that's private to the namespace, yes

14:48 tmtwd: thanks

14:49 rs0: hard to google, i suspect

14:49 i remember that was a big concern when C# came out

14:51 lodin_: rs0: Not familiar with Smart Data.

14:52 rs0: lodin_: it's not a technical term... I'm basically using it to refer to OO-style data that isn't semantically transparent

14:52 lodin_: rs0: But this is just ad hoc polymorphism, but essentially bundle two functions (get and update) into a convenient interface.

14:58 rs0: lodin_: but you still think that lenses would be beneficial/coherent in a dynamically typed language like clojure?

14:58 lodin_: or at least, no worse than the present state of affairs?

14:59 lodin_: rs0: brb.

15:09 hyPiRion: rs0: so something à la define-setf-expander or defsetf in common lisp?

15:10 rs0: hyPiRion: I don't know enough about CL *or* lenses to say

15:11 lodin_: rs0: Absolutely.

15:12 rs0: It's just about presenting your data through an interface.

15:12 rs0: but importantly, lenses compose.

15:15 rs0: The only issue with Clojure being dynamic is that it cannot implement pure (with Haskell type a -> f a).

15:17 And as far as I understand, the Haskell lens library uses applicative functors for good reasons (although I don't know the details for the implementation).

15:20 rs0: The core use case is handled in Clojure by the *-in functions, but lenses generalize those functions so that you can look inside any object, not just associatives.

15:21 rs0: lodin_: i'm not sure that that's more appealing than just having a defprotocol that you can use to implement navigation. i saw stuart halloway do exactly that in a gist showing how to find nils in arbitrarily nested clojure collections

15:22 lodin_: rs0: How do you mean? (Do you have a link to the gist?)

15:27 rs0: An example: how would a function look that takes a sequence and a function and updates the second last item with the provided function? Like (defn update-2nd-last [xs f] ...).

15:30 rs0: lodin_: oh, that sounds like a job for transducers! =)

15:31 lodin_: i'm not sure why you can't maintain a small internal buffer (which is all you'd end up doing with transducers anyway)

15:32 lodin_: I'm not sure how that would look. Could you write some example code?

15:34 Actually, let's say that you have a table represented as a sequence of sequences, and you want to update the second last element in the first row. So the first dimension is rows, the second is columns.

15:35 So my previous function update-2nd-last would take (first table) as input.

15:36 The point that I'm failing to get to, is that with lenses you can write (update xs [0 reverse' 1] f) where reverse' is an iso.

15:37 You can also define default values as part of the lens, i.e. not as part of the data structure. So you don't need a magic object that overloads calls to get.

15:47 For instance, instead of (update-in {} [:foo] (fnil inc 0)) you can write (update {} (key' :foo 0) inc). (Sometimes this is what you want, sometimes not.)

15:49 So about dynamic languages, I think lenses find new uses cases there, the above with default values being one of them.

15:51 rs0: lodin_: i'm not sure this really fits into the overall Clojure philosophy

15:51 lodin_: rs0: How's that?

15:52 rs0: lodin_: well, even though lenses are functions, the intention is to view data through them, right?

15:52 lodin_: rs0: or update, yes.

15:52 rs0: right

15:53 lodin_: I think one of Clojure's biggest contributions to software engineering is the emphasis on reducing everything to generic reusable data, and directly using/passing around values everywhere you can

15:53 lodin_: rs0: How does lenses change that?

15:55 rs0: lodin_: with lenses, you're introducing this sort of object-y translation layer between your data and the code that is looking at it. it's more than just a common abstraction (e.g. the seq protocol), because it seems like you're implementing specific semantics/special cases for different types of data that, fundamentally, have the same shape underneath (e.g. two different defrecord types)

15:56 lodin_: rs0: That's not what lenses are, but lenses can be used for that.

15:56 But (update [0 reverse' 1] f) is nothing like that.

15:56 It's just abstracting away the (set ... (f (get ...))) pattern.

16:00 The cool thing about having polymorphic lenses is that you can implement "username" on Associative so that it just accesses :username. If you then have a record you can implement username so that it accesses :user.

16:01 rs0: lodin_: can't you do that with extend-protocol?

16:02 lodin_: rs0: You mean have a method that returns the username, like (defprotocol Username (username [_] "Returns the username."))?

16:02 rs0: lodin_: additionally, years of Java development has led me to appreciate the transparent way that Clojure serializes data. you pretty much know exactly how something will get written out

16:03 lodin_: rs0: Lenses are completely separate from how the data is represented, just like protocol methods are not part of the data.

16:04 Lenses are function that the user decides he/she wants to use.

16:04 s/function/functions/

16:04 rs0: lodin_: right, and defrecord lets you define methods as well

16:04 lodin_: rs0: Precisely.

16:04 rs0: actually, let me ask you something

16:04 can you compare and contrast lenses with http://rschmitt.github.io/dynamic-object/ ?

16:04 lodin_: So all that lenses do, is to bundle two methods, get and update.

16:05 This makes it possible to abstract away (set ... (f (get ...))) and similar patterns.

16:06 rs0: After looking at it for a few seconds, I'd say it's quite different.

16:10 rs0: lodin_: the main similarity is that it provides a set of functions that provide a "view" into data in a Clojure map

16:11 lodin_: however, they can do more than just return values. for instance, they can perform implicit type conversions. T -> Optional<T>, Date -> Instant, and so on

16:12 lodin_: rs0: With DynamicObject, how would you do the equivalent of update-in?

16:13 rs0: lodin_: unsolved problem

16:13 lodin_: rs0: So it's completely different. :-)

16:13 A major point of lenses is that they compose.

16:14 rs0: lodin_: although you remind me, i was wondering about implementing update-in and the like in Collider https://github.com/rschmitt/collider

16:14 lodin_: So that first you abstract away (set ... (f (get ...)) and then you can abstract (set ... (set ... (f (get ... (get...)))))

16:14 etc etc.

16:15 rs0: hm

16:15 what does that look like with sets?

16:16 vectors actually are Associative

16:16 but sets, well, you can basically do two things with them: test for membership and iterate over the contents

16:17 lodin_: rs0: Presumably you would have a mapping function in there. Also abstractable with lenses.

16:17 Not exactly lenses, but traversals.

16:17 Same same, but different. :-)

16:18 rs0: I dunno, man... maybe I'm biased

16:18 at this point I'm fairly predisposed to view concepts coming out of the Haskell space as navel-gazing until proven otherwise

16:19 I studied Haskell before Clojure. I was blown away by Clojure's far simpler and more direct solutions to problems I actually have

16:19 lodin_: You're not convinced that (update table [0 reverse 1] f) is a good abstraction?

16:19 reverse', i mean.

16:19 rs0: what is reverse'

16:19 lodin_: it's the iso i mentioned above.

16:20 justin_smith: unholy union of get-in and -> I see?

16:20 lodin_: rs0: iso being a lens that doesn't actually look deeper, but just makes things look different. So reverse' would make the sequence look reversed to the following steps.

16:22 rs0: Another would be (update 32 [fahrenheit<->celsius] inc), returning 33.8.

16:22 Err, ignore [] since they're not needed.

16:23 Compare with (-> 32 fahrenheit->celsius inc celsius->fahrenheit).

16:25 rs0: lodin_: since i mentioned this and you wanted to see it: https://gist.github.com/stuarthalloway/b6d1c8766c747fd81018

16:25 tmtwd: How can I use repl to call functions directly instead of like this? (#'rna-transcription/transform-rna "C")

16:25 justin_smith: tmtwd: for starters, you can use require

16:25 lodin_: rs0: Or (let [x {:temp 32}] (->> x :temp fahrenheit->celsius inc celsius->fahrenheit (assoc x :temp)))

16:25 justin_smith: ,(require '[clojure.string :as s])

16:25 clojurebot: nil

16:25 justin_smith: ,s/join

16:25 clojurebot: #object[clojure.string$join 0x26f1ea7d "clojure.string$join@26f1ea7d"]

16:26 lodin_: rs0: The lens equivalent: (update x [:temp fahrenheit<->celsius] inc).

16:26 zematis: Is there a way to filter exactly n items out of a vector with a pred?

16:27 justin_smith: zematis: something like drop?

16:27 lodin_: zematis: filter is lazy, so you just take n items. Or is that the answer to another question? :-)

16:27 rs0: i think he means "the first n items matching the pred"

16:27 or... something

16:28 zematis: I want to only drop items where (= pred? true), and end up with a vector that contains m - n items.

16:28 Could do it with sort and then drop, but that wouldn't preserve the ordering.

16:29 justin_smith: zematis: (take n (filter p? c)) ?

16:29 lodin_: zematis: I see what you mean.

16:29 zematis: justin_smith: There may be less than n items after the filter.

16:29 justin_smith: zematis: take doesn't care

16:30 it just sets a maximum

16:30 zematis: Hmm. Then that should work!

16:30 Thanks!

16:30 justin_smith: ,(take 100 [:a :b])

16:30 clojurebot: (:a :b)

16:30 zematis: Oh, no, that won't work.

16:30 lodin_: I think you mean filter as in remove, not separate and keep, right?

16:30 zematis: Here, I'll give an example.

16:31 rs0: zematis: you want to exclude the first N items in the vector matching pred?

16:31 zematis: rs0: yep

16:31 lodin_: zematis: You tricked everyone by saying filter. :-)

16:31 zematis: Ah. Still new at this :)

16:31 rs0: lodin_: i don't blame him. this is why Collider collections advertise both "filter" and "exclude"

16:32 ok. i guess i'd create an empty transient vector and loop/recur

16:32 depends on how performance sensitive your use case is

16:32 zematis: Shouldn't be that sensitive.

16:32 rs0: can't rrb-vectors be appended or something?

16:33 lodin_: rs0: Back to lenses. Do you see what I'm getting at with the (set ... (f (get ...))) examples?

16:34 tmtwd: are cursives paredit bindings the same as ciders/emacs, or can they be configured as such?

16:34 rs0: lodin_: i'm not completely sure how this is more general than update-in, or possibly a souped-up update-in that you could build using some new protocol

16:35 lodin_: rs0: That new protocol is the protocol of lenses! :-)

16:35 rs0: If you would soup-up update-in you would reinvent lenses, I'm quite sure.

16:35 Poorly, probably.

16:36 I did precisely that, but before learning Clojure.

16:36 justin_smith: a combo of get-in, update-in, and ->

16:36 rs0: lodin_: I'm not sure why Associative isn't adequate

16:37 lodin_: that interface encapsulates the notion of a collection that contains elements "at" some sort of location or address that can be specified

16:37 (unlike sets which can merely be traversed)

16:37 lodin_: rs0: There was a library that I saw ones that introduces wildcards for get-in, so you could write (get x [:foo * :bar]) to update all the bar keys in the sequence contained in (:foo x).

16:38 rs0: I don't know what that means

16:38 lodin_: rs0: err, i wrote get, i meant update.

16:39 rs0: so is this, like, shell globbing?

16:39 the * represents all paths to different :bar keys?

16:39 lodin_: If we had a traversal called seq', you could write (update (range 5) seq' inc) ; (1 2 3 4 5).

16:39 But since lenses and traversals and what not (http://i.imgur.com/ALlbPRa.png) compose, then you can also do

16:39 rs0: ,(map inc (range 5))

16:39 clojurebot: (1 2 3 4 5)

16:40 lodin_: (update {:foo (range 5)} [:foo seq'] inc) ; {:foo (1 2 3 4 5)}

16:41 rs0: lodin_: that diagram is worse than the Scala collections library class hierarchy

16:41 lodin_: rs0: I know, it's horrible.

16:41 Fortunately, this is Clojure, so you don't need to look at it. ;-)

16:41 rs0: lodin_: well, the types are still there

16:42 lodin_: rs0: Sure. And for good reason.

16:42 rs0: lodin_: just because I don't have to look at them doesn't mean they go away

16:42 lodin_: You don't have to care when using it though, as long as you do things that make sense.

16:43 rs0: oof. that's not reassuring. that sounds like foreshadowing

16:45 lodin_: Anyway, the point is that there are different things capable of doing different things, but they all compose.

16:46 It's like you can only do (comp g f) if g has arity one in Clojure, because it doesn't make sense otherwise.

16:47 rs0: something just occurred to me. I have a sense that Haskell people are concerned with whether an idea composes with itself (e.g. applicative functors do, monads don't), and Clojure people are concerned with whether ideas compose with *other ideas*

16:49 i seem to see a lot of research out of the Haskell space of the form "look at this insanely complicated abstraction! it COMPOSES!"

16:49 say, Arrows or something

16:51 actually, arrows are a generalization of monads, so I guess they fall into the abstraction-to-end-all-abstractions category

16:53 lodin_: Dunno, I'm not that familiar with what's going on in Haskell.

16:53 rs0: Can you give an example of ideas that compose with each other in Clojure?

16:55 rs0: lodin_: well the basic idea behind most of clojure's features is orthogonality and simplicity. they do one thing and can be composed in different ways to combine their effects. have you ever seen stuart halloway's breakdown of the features of classes and their Clojure analogues?

16:57 lodin_: rs0: Don't know which talk/article you're refering to, but isn't the same true for Haskell?

16:58 rs0: lodin_: I think that a lot of the Haskell code out there that actually does useful stuff tends to look like that. the main problem solving strategy is usually "call a function"

16:59 lodin_: A good strategy, no?

16:59 rs0: lodin_: but Haskell also has a more academic component with grad students studying hardxxxcore type theory

16:59 lodin_: well, I also think that pervasive non-strict evaluation is a bad design choice for a general purpose programming language. it leads to a ton of incidental complexity

17:00 lodin_: rs0: I'm more interested in which ideas you mean compose in Clojure, and how that contrasts with Haskell.

17:03 rs0: why the hell don't i just have a bookmark folder or something with all the clojure talks

17:04 tmtwd: anyone use exercism?

17:05 rs0: lodin_: okay, for example

17:06 lodin_: rs0: I know Clojure, but I don't know Haskell, so I can't contrast it.

17:06 rs0: lodin_: in Clojure, you have eager evaluation and no concept of "effects" tracked by the type system

17:07 lodin_: ... beyond lazy vs strict evaluation, and static typing.

17:07 rs0: lodin_: just a sec

17:07 lodin_: in Haskell, you have pervasive non-strict evaluation, and the IO monad is used to sequence effects (and in effect to mark impure functions as contaminated)

17:08 lodin_: monads are probably the most famous abstraction associated with Haskell, and they don't actually compose with each other. and this results in an interesting situation, even above and beyond the IO monad itself

17:08 lodin_: if you want to combine the effects of the Writer monad and the State monad, you have to create a monad transformer stack

17:09 lodin_: so all of the monads in the stack need to know about each other; it's somewhat monolithic that way

17:09 lodin_: Right.

17:09 rs0: lodin_: you also now have all this incidental complexity--for instance, the order of monads in the stack is critical to get right

17:10 lodin_: Is that really incidental complexity though?

17:10 rs0: absolutely!

17:10 monad transformers have nothing to do with the problem you're trying to solve

17:10 lodin_: Reversing the order of the stack changes the semantics, no?

17:11 rs0: the stack itself is incidental complexity

17:11 tmtwd: anyone know why my functions don't work as expected ? http://pastebin.com/ZYNjzfxT

17:11 lodin_: rs0: Please elaborate.

17:11 rs0: i don't need monad transformers to reason about my code, any more than I need C++ multiple virtual destructor inheritance to reason about my program's resource management

17:12 lodin_: tmtwd: You're testing for "C" instead of \C etc. I think that's the problem.

17:12 rs0: a haskell program, at the highest level, lives in a monad transformer stack, and all of the wrapping and unwrapping and lifting and effect sequencing is something that's on your head every day when you go to work

17:13 a Clojure program, or an OCaml program (for a statically typed example), doesn't force your architecture into the procrustean bed of one all-purpose abstraction

17:14 I think monad transformers are an example of taking things too far. it's basically saying "if monads aren't getting the job done, you're not using enough of them"

17:14 (since monad transformers are also monads)

17:15 lodin_: rs0: What's the worst/deepest monad stack you've used?

17:16 tmtwd: lodin_, ah, good tip :)

17:16 rs0: lodin_: I haven't done enough practical Haskell for things to get to that point. I think most people in practice use RWS and RWST, plus something for IO

17:17 lodin_: tmtwd: You might want to check out (case ...) as well.

17:18 tmtwd: Or actually, just use a map if that's what you need. (def handle-char {\C \G, \A \U, \T \A, \G \C}), yes, the map can be used as a function.

17:20 tmtwd: lodin_, oh kinda like haskell?

17:20 lodin_: tmtwd: Dunno. :-)

17:21 Or you mean case in Haskell?

17:21 rs0: or the haskell 'map' function?

17:21 (as opposed to the map data type)

17:21 tmtwd: so, we can't throw something in the else statement of cond? http://pastebin.com/F7UimAA8

17:22 lodin_: tmtwd: I think so, but you're missing a dot.

17:22 tmtwd: lodin_, oh no nvm . I failed at understanding you

17:22 lodin_: Can you instantiate Throwable btw?

17:23 tmtwd: lodin_, yeah that was it

17:23 i dunno thats just what I saw somewhere

17:23 but it loads now

17:24 lodin_: I guess you can then.

17:25 I usually throw Exception. Don't know why.

17:27 rs0: If it's any comfort though, I wanted lenses before I knew lens existed. I know about lens because I was trying to reinvent them.

17:29 I'm quite sure I wouldn't have done as good a job though, so I'm glad someone else has done pretty much all the hard work.

17:30 scottj: lodin_: have you seen https://github.com/megakorre/glasses ? (idk anything about it, just found it searching clojars for lens)

17:31 lodin_: scottj: Yes.

17:34 tmtwd: How to turn ("G" "A" "B") into "GAB"?

17:34 lodin_: There's a few other implementations as well, but iirc the other ones use the [getter setter] approach, rather than the [value updater] approach.

17:34 tmtwd: concat?

17:34 lodin_: tmtwd: (apply str ...)

17:34 clojurebot: concat is considered a cat

17:34 Bronsa: tmtwd: apply str

17:35 tmtwd: , (apply str ("a" "b"))

17:36 clojurebot: #error {\n :cause "java.lang.String cannot be cast to clojure.lang.IFn"\n :via\n [{:type java.lang.ClassCastException\n :message "java.lang.String cannot be cast to clojure.lang.IFn"\n :at [sandbox$eval25 invoke "NO_SOURCE_FILE" 0]}]\n :trace\n [[sandbox$eval25 invoke "NO_SOURCE_FILE" 0]\n [clojure.lang.Compiler eval "Compiler.java" 6792]\n [clojure.lang.Compiler eval "Compiler.java" 6755]\n...

17:36 tmtwd: i don't think that quite works

17:36 oddcully: ,(apply str ["a" "b"])

17:36 clojurebot: "ab"

17:36 lodin_: tmtwd: As a general rule, use [] instead of () when writing literal sequences.

17:36 oddcully: ,(apply str '("a" "b"))

17:36 clojurebot: "ab"

17:36 lodin_: ,(apply str '(x y))

17:36 clojurebot: "xy"

17:36 lodin_: Probably not what you wanted.

17:37 I was bitten by that when I was learning Clojure.

17:38 tmtwd: lodin_, ah gotcha, the return from repl was with parens

17:39 lodin_: scottj: The problem with glasses is that there's no way to handle e.g. using a purely functional random number generator in the updater function.

17:41 scottj: Like (update x [:foo mapped-vec] #(add-noise rng %))

17:41 scottj: I'm expecting rs0 to react now, but I think we need a monad for that. :-)

17:42 rs0: damnit i stopped paying attention

17:42 let me read up

17:42 scottj: lodin_: recall the names of other clojure lens implementations?

17:43 lodin_: scottj: Not really, I've only used glasses, but I can see if I find any that i recognize.

17:43 rs0: "purely functional random number generator"

17:43 why not just create a java.util.Random with a deterministic seed?

17:44 if... this is about testing or something

17:45 scottj: https://speakerdeck.com/markhibberd/lens-from-the-ground-up-in-clojure

17:45 lodin_: Have you seen gfrederick's talk on rngs?

17:45 scottj: iirc, that one uses [value updater] approach, aka the "functor approach".

17:48 I'm not sure now glasses work, I think it's analoguous but spelled differently. But I'm really not sure.

17:48 amalloy: rs0: if it's an immutable RNG, you can do things with it you couldn't do with a Random

17:49 rs0: scottj: i've actually seen both versions of that slide deck

17:49 amalloy: like you can spin up three threads pursuing different strategies, and give them each the same seeded RNG instance, to get reliable random numbers from all of them

17:49 rs0: amalloy: ThreadLocalRandom?

17:49 actually, you can't control the seed in that case

17:50 well, that's still nothing a thread-local variable can't fix. if you shared an immutable test runner between those threads you'd still get non-deterministic behavior

17:50 er

17:50 immutable RNG instance

17:50 i'm writing up a junit issue and i'm distracted

17:50 amalloy: huh? no you wouldn't

17:52 rs0: oh i see, you're referring to taking the state of the RNG and holding on to it in each thread

17:52 so your RNG is basically a pure (next) function over a given seed and some pseudorandom number generation algorithm

17:52 amalloy: yes, of course. anything else wouldn't be immutable

19:02 lodin_: My solution now would be to do (let [rng-atom (atom rng)(update x [:foo mapped-vec] #(let [[rngadd-noise @rng %

19:02 err

19:03 My solution now would be to do (let [rng-atom (atom rng) new-x (update x [:foo mapped-vec] #(let [[rng v] (add-noise @rng %)] (reset! rng-atom rng) v))] [@rng-atom new-x]).

19:05 It would be cleaner to have something like (update-with rng x [:foo mapped-vec] add-noise), where add-noise takes two arguments and returns a tuple.

19:10 and update was (fn [x lens f] (update-with nil x lens (fn [_ v] (second (f v))))).

19:10 err,

19:10 and update was (fn [x lens f] (update-with nil x lens (fn [_ v] [nil (f v)])))

21:46 TEttinger: huh, I'm not sure if this is a lein thing or a java problem or a cursive/intelliJ problem.

21:46 java.lang.Exception: Problem opening jar C:\Users\noto\.m2\repository\org\clojure\clojure\1.7.0\clojure-1.7.0.jar

21:46 caused by having a dep on [org.clojure/clojure "1.7.0"]

21:47 there's one error after that: Caused by: java.util.zip.ZipException: error in opening zip file

21:51 I just unzipped it manually, it is totally a valid jar/zip

21:54 huh, I think it may have been me pressing the run button twice by mistake in Cursive

22:22 cfleming: is there a reason why Cursive doesn't show lein run as an option in the Leiningen Tools window?

22:22 there's uberjar, which I think also requires a -main method

22:23 I can sorta work around it by calling (-main) at the end of the file, but then it gets called when AOT compiling as well

22:24 justin_smith: TEttinger: or when you require that namespace even

22:25 TEttinger: just a wild guess - is run hooked up to the standard "run project" UI?

22:43 TEttinger: justin_smith, nope

22:43 justin_smith: TEttinger: looks like Run -> Edit Configurations ? https://cursiveclojure.com/userguide/repl.html

22:43 TEttinger: run project doesn't run -main

22:44 oh?

22:44 justin_smith: I mean that page is about starting a repl, but it shows things that appear to be related to setting up run configuration...

22:44 TEttinger: I see VM params and Script params

22:45 justin_smith: script params "clojure.main -m your.ns" would work

22:45 TEttinger: it looks like this: http://i.imgur.com/8IZ6T6K.png

22:45 dash m?

22:45 justin_smith: oh, it might be just "-m your.ns"

22:46 one moment, checking

22:48 TEttinger: I have an "unused global declaration -main" warning too :|

22:48 justin_smith: because of a (defn -main ...) ?

22:48 is the ns set up to be your main ns in project.clj?

22:48 TEttinger: yes

22:48 :main ^:skip-aot infinite-raid.core

22:49 justin_smith: interesting

22:49 TEttinger: I created a template with `lein new app infinite-raid`

22:49 justin_smith: (I don't really work on this project from this computer, so testing took a while)

22:50 TEttinger: changed to use clojure 1.7.0, added a dep and usage of a java lib, that's about it

22:50 justin_smith: if you set it to execute "lein run -m your.ns/-main" it should just work

22:50 TEttinger: as a new run config?

22:50 justin_smith: try that from a repl of course, but it works here

22:50 TEttinger: yeah

22:52 TEttinger: I use things like that in order to run the same project from a jsvc wrapper or just through regular clojure.main, both possible from the same uberjar (without lein it's slightly different in invocation, but same concept of course)

22:57 TEttinger: justin_smith: it ended up working out by making a new lein run config, with just the arg `run`

23:00 justin_smith: TEttinger: oh, that's easy!

23:00 TEttinger: haha

23:00 justin_smith: that was nearly my first guess

23:02 TEttinger: well it looks like I'll be writing some java and some clojure now to try to make a game. even though I have a decent amount of art I made, it still isn't enough for some sorts of games, so this is going to be full-color text-based type of thing.

23:02 it's always entertaining to see how much shorter the clojure is :)

23:25 rhg135: Is this supposed to hang, or did I miss something. https://www.refheap.com/105312 networking is a pain

23:29 justin_smith: rhg135: you can use jstack to see what each of the threads in your vm is doing

23:30 rhg135: is jstack one of the jvm tools?

23:32 justin_smith: rhg135: yeah, it comes with the vm

23:32 it shows a stacktrace for every thread, if you give it your app's pid

23:33 rhg135: I'm using a debugger, just didn't think to pause the vm

23:41 this is getting weird

23:43 apparently when I load-file it, it doesn't even connect, and when I type it on that port, 8088, it doesn't connect either

23:43 but when i type it and use a different port it hangs and the trace reveals netty is waiting

Logging service provided by n01se.net