#clojure log - Jan 10 2015

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

1:29 dagda1_: if I have a vector like this [:a :b] can I add metadata to each element?

1:51 luxbock: dagda1_: keywords can't have meta data

2:12 DomKM: Anyone have a good workflow for autotesting cljx namespaces on compile?

5:48 cfleming: $mail andyf Here are the changes you were asking about the other day: https://github.com/cursiveclojure/riddley/blob/master/src/cursive/riddley.clj. I use metadata to mark interesting parts of the form: I always expand the whole top-level form, ::top-form is the form actually selected by the user, i.e. the one I actually display to them. When requesting an expansion I tag an element in the form with ::expand-to, and only the path from the root to

5:48 lazybot: Message saved.

5:48 cfleming: that form is expanded. Forms which are actually expanded are marked with ::expanded so I can mark them in the IDE.

5:48 $mail andyf <cut off> that form is expanded. Forms which are actually expanded are marked with ::expanded so I can mark them in the IDE.

5:48 lazybot: Message saved.

6:22 hellofunk: cemerick or anyone: does Austin or Piggieback rely on websockets or ajax (xhr) for its magic?

6:45 andyf: cfleming: Thanks for the link

6:46 cfleming: andyf: No problem, let me know if you have more questions

6:47 hellofunk: My knowledge of this isn't perfect, but I believe that Piggieback doesn't provide a transport, that's a layer bridging nREPL to a CLJS REPL. Austin is an ajax transport on top of Piggieback, Weasel is a websocket one

7:31 Biofobico: can anyone please explain me what reduce does? (reduce + [1 2 3 4 ])

7:33 tikotus: 1+2+3+4

7:33 ,(reduce + [1 2 3 4])

7:33 clojurebot: 10

7:36 Biofobico: so it's usage is to sum what is inside the '[' ?

7:40 tikotus: No, it sums them because you give it + as the function. It applies the given function for the two first values, then applies the function for the result and the third value. Then it applie the function for the result and the fourt value.

7:41 The function can be any function that takes 2 parameters

7:41 PeakCode: So the example can be seen as (+ (+ (+ 1 2) 3) 4)

7:44 Biofobico: so its a more condensed version of what PeakCode wrote?

7:47 mearnsh: ,(reduce #(if (even? %2) (conj %1 %2) %1) [] (range 10))

7:47 clojurebot: [0 2 4 6 8]

7:47 mearnsh: hm

7:47 PeakCode: Well, you wouldn't usually have a literal as collection, but a collection you got from somewhere.

7:48 Biofobico: tried (+ [1 2 3 4]) which gave me an error

7:48 PeakCode: ,(apply + [1 2 3 4])

7:48 clojurebot: 10

7:50 Biofobico: thank you

7:51 im learning clojure (never programmed before) so excuse my noob questions please :P

7:55 PeakCode: NP. https://clojuredocs.org/ is a great resource with many code examples. See for example https://clojuredocs.org/clojure.core/reduce.

8:02 soro: What is a good book it to start with clojure?

8:02 kungi: soro: I liked Clojure in Action

8:02 soro: But there are many others. Have a look at this Stackoverflow: https://stackoverflow.com/questions/2544043/clojure-learning-resources

8:02 triss: I've got a seq of seq's - whats the simplest way of getting this as a vector of vectors?

8:04 Duke-: (mapv vec seq-of-seqs)

8:04 Biofobico: PeakCode: Thank you. Will check it out because this reduce thing hurts my little brain :)

8:04 PeakCode: :-)

8:06 kungi: Duke-: I thought about mapv vector but got this while trying [[(1 2 3)] [(1 2 3)]]

8:06 Duke-: there's a difference between vector and vec

8:06 kungi: Duke-: I see that now :-)

8:06 triss: ah cheers Duke-!

8:06 of course

8:06 kungi: Biofobico: Your brain will grow in time to adjust to reduce and the like.

8:08 Biofobico: :)

8:14 im finding that reduce is a strange word for which it does

8:16 mi6x3m: Biofobico: what would be a better one?

8:19 kungi: Biofobico: It is also called fold in some contexts

8:21 Biofobico: I think since im not a programmer, i can't seem to find an use for it

8:22 kungi: Biofobico: then think about it that way. Reduce reduces a collection to one value. That's how it makes sense.

8:24 Biofobico: kungi: can't thank you enough. You just gave me an ah-ah moment

8:24 kungi: Biofobico: :-) Always happy to help

8:25 hipsterslapfight: that value can also be a collection of course :v

8:25 reduce is <3

8:25 Biofobico: so (reduce [ 1 2 3 4 5] (6 7 8 9)) is somewhat valid?

8:25 im sure that code is wrong

8:26 but for the sake of simplicity it will make something like (1 2 3 4 5 6 7 8 9)?

8:27 kungi: Biofobico: that's what ,(reduce conj [1 2 3] [4 5 6] [7 8 9]) does

8:27 conj = conjoin

8:28 ,(reduce conj [1 2 3] [4 5 6] [7 8 9])

8:28 clojurebot: #<ArityException clojure.lang.ArityException: Wrong number of args (4) passed to: core/reduce>

8:28 kungi: ,(reduce conj '([1 2 3] [4 5 6] [7 8 9]))

8:28 clojurebot: [1 2 3 [4 5 6] [7 8 9]]

8:31 Biofobico: Again, can't thank you guys enough :)

8:32 (reduce into [[1 2 3] [:a :b :c] '([4 5] 6)]) [1 2 3 :a :b :c [4 5] 6]

8:32 makes perfect sense now

8:36 hellofunk: can anyone explain to me what happens in a server environment when you a browser sends an ajax request to Ring and then sends another while the first is still processing, is this synchronous on the server side? i.e. the first request must complete before the second is processed by the server?

8:38 PeakCode: No, they should be processed in parallel.

8:38 hellofunk: PeakCode: that is the default behavior from the Ring server, to multithread ajax requests?

8:40 PeakCode: Yes. Are you experiencing something else?

8:40 hellofunk: PeakCode: no i haven't taken a deep look, but it made me think while I try to figure out the best approach to a problem

8:41 PeakCode: OK.

9:39 AeroNotix: was playing with some ideas to more declaratively express agents: https://gist.github.com/2a7cb69f29a4d109447f

9:42 imanc: if I try this in the repl: (1 2 3 ~(inc 4)) i get : (1 2 3 (clojure.core/unquote (inc 4))) whereas I see examples online where this is evaluated to (1 2 3 5)

9:43 err, that original statement should be quoted: '(1 2 3 ~(inc 4))

9:44 mearnsh: &`(1 2 3 ~(inc 4))

9:44 lazybot: ⇒ (1 2 3 5)

9:44 mearnsh: notice ` vs ', syntax quote vs quote

9:44 imanc: oh quote vs backtic..

9:45 mearnsh: you generally use unquote inside syntax quotes

9:46 imanc: OK thanks - I missed that there was a difference between quotes and syntax-quotes

10:19 gfredericks: semisort-key: https://www.refheap.com/95939

10:20 ^ can use that to semishuffle an infinite collection too

10:45 OscarZ: are macros just compile-time magic? they will be just normal functions runtime?

10:50 gfredericks: OscarZ: under normal usage the macros don't really exist at runtime

10:51 OscarZ: e.g., at runtime (f x) is indistinguishable from (-> x (f))

10:51 OscarZ: ok..

10:51 i somehow got this crazy idea that macros do some crazy bytecode manipulation stuff with the arguments at runtime when they are actually called

10:52 gfredericks: not remotely

10:52 they're a lot simpler than that :)

10:52 they just transform the code (not bytecode) at compile-time

10:52 OscarZ: what are the typical use cases for macros? what do people usually run into first that you need macros for?

10:53 yes, ok

10:53 gfredericks: well the first reason to learn about macros as a beginner is because you're using them all the time

10:53 from clojure.core and libraries

10:54 OscarZ: i guess when-not is a macro? its not obvious to me why its not a normal function..

10:54 gfredericks: conditionals like when-not want to do delayed evaluation

10:55 if when-not were a function, the code that you're trying to *conditionally* execute would get executed before when-not was even called

10:55 if that doesn't make sense, just try writing when-not as a function and see what happens :)

10:56 OscarZ: oh right.. its called call-by-name or something when arguments are evaluated before calling a function ?

10:56 like in java etc..

10:57 gfredericks: I haven't heard that; but I don't know what to call it; strict evaluation perhaps

10:57 OscarZ: yes thats it

10:57 i think haskell is not like that.. ran into it somewhere

10:58 gfredericks: right haskell is the primary counterexample

10:58 or...nonexample

10:59 OscarZ: is "if" normal function?

10:59 gfredericks: no, there are a handful of primitives called "special forms" -- if is one of them

10:59 but if it weren't, it would be a macro instead of a function for the same reason as when-not

11:00 OscarZ: but is "if" special for the very reason that it only evaluates the branch that is required?

11:01 gfredericks: when-not does that too

11:01 but when-not does it by generating code that uses if

11:01 i.e., when-not is written in terms of if

11:01 you have to have some base thing to rewrite to

11:01 and that base thing is if

11:02 which is implemented in the compiler

11:02 with the other special forms, like fn, loop, let

11:02 OscarZ: oh right.. somehow i thought when-not = if-not .. but its different :)

11:05 gfredericks: OscarZ: http://www.infoq.com/presentations/macros-clojure-west-2013

11:05 OscarZ: hey its by you.. ill check it out :)

11:08 i guess this doesnt work? (defn when-not [test code] (if (not test) (do code) nil)

11:09 gfredericks: ~tias

11:09 clojurebot: tias is try it and see

11:09 OscarZ: ok

11:12 fail :) ok.. it works if i use it like (when-not false 15) .. but i'd like to have some code in there.. not just a value

11:13 gfredericks: right

11:14 OscarZ: so it takes a body of code, thats why i need to use a macro.. its a bit like passing a function but its inline or something.. its a bit weird compared to what im used to

11:16 like in java.. its a call like whenNot(false, new Function() { ... }); ...hmm maybe i shouldnt follow this line of thought :(

11:17 but yeah, i think i understand it a little better .. going to watch the video now

11:19 gfredericks: OscarZ: yeah you can get the same behavior by using functions -- it's just more verbose

11:19 macros are mostly about getting new syntax

11:20 OscarZ: yes, i can understand it better now that clojure is a bit special.. when-not looks just like normal function call but you are actually passing in a block of code

11:21 gfredericks: by "using functions" I mean using higher-order functions to delay evaluation like you just showed, not "using a function instead of a macro" like we were discussing earlier

11:22 OscarZ: ok

11:24 what if you did something (when-not false '(some-code-block)) .. can you pass in a block of code like that ?

11:25 im not sure if i completely understand what ' does.. at least it doesnt call it right away

11:25 gfredericks: you can do it but the only way to "execute" the code is manually by calling eval, which is problematic in a few ways

11:25 at that point you're just doing macros yourself instead of using the built-in feature

11:27 OscarZ: ok.. cool

11:27 yes its much more clean that way

11:28 gfredericks: I think there's a few minutes in the talk on exactly that approach though

11:28 OscarZ: watching now

11:51 emaczen: can someone tell me how to user cider to get a repl connected to the remote server?

11:53 csd_: How can I use sort-by with `for`? I'm not sure how to package what for returns into a collection for sort-by.

12:01 Oh I see the problem--I'd had sort-by within the `for`.

12:12 hellofunk: is anyone familiar with the book Principles of Concurrent and Distributed Programming?

12:13 by Ben-Ari

12:37 OscarZ: gfredericks, cool presentation, thanks! it made me understand the homoiconicity thing a little better.. didnt really understand whats going on

12:48 Atarian: Anybody got time to answer a stupid question from a procedural programmer?

12:49 hellofunk: ,~question

12:49 andyf: ~anyone

12:49 clojurebot: Just a heads up, you're more likely to get some help if you ask the question you really want the answer to, instead of "does anyone ..."

12:49 #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: question in this context, compiling:(NO_SOURCE_PATH:0:0)>

12:50 hellofunk: lol whoops

12:50 Atarian: heheheh

12:50 Righto. So I understand why immutable objects are better than mutable ones, threadsafe &c

12:51 But in procedural programming, Im used to making a struct to hold data, then modifying it. All my functions know where it is...

12:51 chouser: that's called global state

12:51 Atarian: In Clojure, if I am right, modifying a data structure returns a new structure

12:52 chouser: that's right

12:52 andyf: Actually, if anyone has any specific benefits to immutable data that they can explain in a way that programmers who have not used them before could ?get?, I?d love to hear them. I don?t know how to explain it well yet, other than something like ?it gives you a lot of new invariants you can be sure of vs. when using mutable data?. What I mean are: benefits even in a single-threaded program.

12:53 Atarian: So how do I deal with the new structure? Where do I (for lack of a better way of putting it) keep it where all of my functions can get to it?

12:53 hellofunk: Atarian: andyf: notable benefit: new data structure, yes, but the shared structure of similar data structures means that it is not constructed from scratch, therefore is highly efficient compared to mutable data structures

12:53 Atarian: I'm not knocking Clojure btw, I just think if I understand this I'll have that lightbulb moment

12:53 andyf: Atarian: In many cases, it is passed on to the next function or step of the code without needing to keep it anywhere.

12:53 chouser: Atarian: Clojure comes with some reference types that mutate, holding subsequent versions of immutable values.

12:54 andyf: But yes, when you do need to keep it somewhere that is stateful, most people use an atom in Clojure to store values that change over time.

12:54 but sparingly.

12:54 Atarian: Andyf: chouser: Right, OK... so I return the unnamed, modified structure to the next function down the line

12:55 chouser: Though you should be aware that coming from a procedural background, you're going to rely on those reference types more often than you will once you're more familiar with functional-style programming.

12:55 andyf: hellofunk: That is a reason that immutable data structures are much more efficient than the simple way of implementing them. That is good, but I'm looking for benefits in how you can think about your program, not how fast they run.

12:55 emaczen: How can I connect/start a remote REPL?

12:55 chouser: Atarian: well, there's that approach as well, passing immutable values down along the line. That's actually closer to "functional style"

12:56 Atarian: emacsen: C-c M-J

12:56 chouser: Atarian: an approach more familiar to you, that you'll use less often later, would be (def my-global-state (atom 42)) ...and then you can (swap! my-global-state inc) for example to change it, and @my-global-state to get the current value

12:56 Atarian: C-c M-j even

12:57 hellofunk: andyf: i think that counting on that performance benefit means you can do things with data sharing and passing data around, mutating it, making "copies" are all valid options you may not considern in environments where they are expensive.

12:57 Atarian: chouser: brain is taking a little to catch up if I go a bit quiet lol

12:58 andyf: hellofunk: I am all for structural sharing, no question about it. What I'm looking for is, given that it can be made efficient, why do people who have used them for a while like them so much?

12:59 What advantages do they give you, when you use them most of the time, that you could explain to someone who has never written programs in that style?

13:00 emaczen: Atarian: That started a local server

13:00 hellofunk: andyf: for me a lot of it has to do with not having to think about or worry about the performance hit with passing around large data or making copies of it with small changes. i'd have not done that in an environment without structural sharing becauase of the performance penalties

13:00 Atarian: Keep waiting

13:00 andyf: I suppose one of the biggest ones is that in programs where mutability is the norm, even single-threaded programs, it requires careful documentation and/or following conventions to know what parts of a big data structure any given function can mutate, and that can be difficult to keep track of mentally.

13:00 chouser: Atarian: these are exactly the questions to ponder. Answering them to my own satisfaction is exactly what I had to do to become confidant as a functional programmer.

13:00 Atarian: emacsen: or lein repl from the command line

13:01 emaczen: Atarian: I've tried lein repl :connect http://ip:port/repl

13:01 Atarian: chouser: I'm trying to understand atoms - You can swap! a map/list/other type into it, and use it like a variable name to access it from other places?

13:03 andyf: Atarian: reset! on an atom is nearly the same as assigning a value to a global variable. swap! is an atomic "read the current value, use this function to calculate its next value, and assign it that value"

13:03 Atarian: emacsen: I honestly just use C-c M-j from emacs or lein repl from bash, no magic

13:04 emaczen: Atarian: How does it know to connect to a remote host as opposed to localhost?

13:04 andyf: but many people use swap! on atoms even in single-threaded Clojure programs, since the next value they want is a function of the current value, e.g. conj a new value to the end of a vector.

13:04 Atarian: Ooh, sorry I didn't know you were trying to connect to a remote server

13:04 My bad

13:05 emaczen: Atarian: No worries

13:05 Atarian: Thanks for your thoughts chouser and andyf, I'm off to try and write a few functions to understand this better

13:07 hellofunk: does anyone know what the default is for Ring with it comes to throttling the number of simultaneous ajax requests (which i guess are just "requests" since the server doesn't know the difference)?

13:07 weavejes_: hellofunk: Ring itself doesn't throttle anything. Different adapters have different thread pool sizes, however.

13:08 hellofunk: weavejes_: by "adapter" what are you referring to?

13:08 weavejester: do you mean middleware handles that?

13:08 weavejester: hellofunk: A ring adapter is the thing that turns a handler into a HTTP server.

13:09 The Ring Jetty adapter has a maximum thread pool size of 50.

13:09 hellofunk: is it safe to assume that all requrests that come into a Ring server are handled in parallel?

13:09 weavejester: hellofunk: Yes... depending on the adapter.

13:10 I believe httpkit has only 4 threads by default.

13:11 hellofunk: weavejester: compojure.handler/api would be an example of an adapter?

13:11 weavejester: hellofunk: No

13:12 hellofunk: That's just a function that pulls in some standard middleware

13:12 (and incidentally is deprecated)

13:12 ring.adapter.jetty/run-jetty is an example of an adapter.

13:12 hellofunk: weavejester: the shiny new chestnut lein template uses compojure.handler./api.

13:13 since run-jetty seems to be the default adapter in all the project tempaltes i've seen, this is one that handled all requests in parallel?

13:14 and if it has a thread pool size of 50, this would suggest that it handled at most 50 simultaneous requests, or can a single thread handle more than one request at once?

13:14 weavejester: hellofunk: compojure.handler/api has been deprecated for a little over 3 months now. They should probably change that.

13:14 hellofunk: weavejester: i will let them know

13:14 weavejester: hellofunk: All adapters I'm aware of handle requests in parallel.

13:15 hellofunk: Even httpkit has a thread pool of 4.

13:15 And yes, if it has a thread pool size of 50, it can handle at most 50 simultaneous requests.

13:15 Each request is handled by a separate thread.

13:18 hellofunk: weavejester: this changelog seems to suggest the made the switch from your handler last month, but when I use the lein template, it still uses it: https://github.com/plexus/chestnut/blob/e0a1be042df633dd245af586476ef4fc899d3544/doc/CHANGELOG.md

13:19 weavejester: hellofunk: Yeah, looks like they've made the change, but haven't released it yet.

13:37 hellofunk: weavejester: what is a good lein tempalte i can look at now that shows the new defaults instead of compojure.handler?

13:40 weavejester: hellofunk: Hm... I'm not sure you need a template for that. You could just use the chestnut snapshot, though, if you're already familiar with that.

13:40 hellofunk: lein new chestnut blah --snapshot

13:41 PeakCode: hellofunk: If you need your web server to be able to handle really many requests in parallel you should look for a server that can handle requests asynchronously, like http-kit or Pedestal.

13:45 hellofunk: PeakCode: how many requests would warrant a change to http-kit? are we talking dozens or thousands?

13:47 and it would seem to me that even if http-kit handles things asynchronously, ultmiately they'd get processed faster with jetty if it has over ten times the threads available

13:50 arohner: hellofunk: closer to thousands, IMO

13:50 hellofunk: threads start to slow down when threads >> CPU cores

13:50 PeakCode: hellofunk: Every thread consumes memory and brings overhead to the processing, but for a dozen simultaneous requests it matters little.

13:52 hellofunk: http-kit can handle thousands of simultaneous requests, but not with normal Ring handlers.

13:52 Chaze: #scala

13:53 PeakCode: hellofunk: The async processing detaches the thread from the request and therefore it can suffice with just four threads.

13:53 weavejester: Asynchronously processing requests means that you're not wasting threads on blocking I/O processes.

13:54 hellofunk: can anyone point me to an example of a setup with http-kit that would be ready for thousands of requests? what Ring handlers would you use?

13:55 weavejester: hellofunk: You'd need to structure your application a little differently.

13:55 Well, normal Ring can handle thousands of requests.

13:56 Async doesn't give you more CPU

13:56 It just means there's less wasting of resources waiting for I/O

13:57 A Java process can have a thousand threads without too much problem.

13:57 But tens or hundreds of thousands of threads becomes a problem.

13:58 Async is useful for apps that are heavy on I/O and open connections like websockets.

13:58 hellofunk: ok, interesting. my needs are not there at this point so i don't think it warrants time to research http-kit more.

13:59 Atarian: Anyone see what's wrong with this? getVals returns a map:

13:59 (def character (atom {}))

13:59 (reset! character getVals)

13:59 (print (character :name)))

13:59 weavejester: Is getVals a function?

13:59 Atarian: Yep

13:59 This is the error

14:00 ClassCastException clojure.lang.Atom cannot be cast to clojure.lang.IFn dungeonworld.core/-main (core.clj:30)

14:01 andyf: Function calls must have parens around them, e.g. (getVals) if there are no arguments.

14:01 Atarian: OK cheers, I'll try that - and no, no args

14:02 andyf: otherwise your reset! call above sets the value of the character atom to the value of the function itself.

14:02 PeakCode: weavejester: Has there been any progress lately regarding async processing in Ring?

14:02 andyf: not the value returned when calling the function.

14:03 Atarian: (reset! character (getVals))

14:03 doesnt work either, still get the cast error

14:03 andyf: also, to get the current value of an atom, you must use (deref character), or the shorthand form @character

14:04 weavejester: PeakCode: What do you mean?

14:04 andyf: e.g. (print (@character :name))

14:04 Atarian: Still not quite as bad as pointers hehe

14:05 PeakCode: weavejester: IIRC there is a GitHub issue regarding this?

14:05 weavejester: PeakCode: Not that I can see.

14:08 Atarian: Thanks Andyf: And yes, I just had a moment of enlightenment when you explained how the parenthesis assigns the result not the fn

14:10 PeakCode: weavejester: Sorry, I thought of https://github.com/clojure-liberator/liberator/issues/91 for Liberator.

14:10 andyf: Atarian: cool. Yeah, functions are values, and they can be passed as arguments, or returned as values from function calls. You can save them in data structures, if you wish, e.g. as values in a map. Calling a function is often done as (function arg1 arg2 ...), but there are many other ways, e.g. (map function collection)

14:11 Atarian: This is where my hardcoded C brain is going to have a bit of trouble, cheers

14:11 andyf: It is a bit like C function pointers

14:11 but there are several differences

14:14 weavejester: PeakCode: I had been considering async approaches for Ring, and the subject has come up a few times in the past, but so far I haven't had a need to do so.

14:23 hellofunk: i'm seeking recommendations on books or great resources to help train my brain program-wide asynchronous architecture. there is the Ben-Ari book i've heard about, any other advice? I'm very curious about strategies that go beyond all the use cases I've had till now, which mainly involve UI handling and callback management

14:31 PeakCode: weavejester: Is there any specific reason for the ring-jetty-adapter to still use Jetty 7?

14:33 weavejester: Ignore. Just found https://github.com/ring-clojure/ring/issues/177.

14:34 hellofunk: interesting quote: " computerized systems in modern cars have half a million lines of

14:34 code, and that electronics account for 25% of their cost and this percentage is

14:34 forecast to increase"

14:34 sorry about that multiline paste

14:35 mavbozo: hellofunk: from which article/writing?

14:36 hellofunk: mavbozo: from a book by Ben-Ari about concurrency

14:36 mavbozo: Principles of the Spin Model Checker

14:37 though i think he more well-known for his other book about general concurrency architecture

14:47 mavbozo: hellofunk: interesting. added that book to my reading list.

14:49 hellofunk: weavejester: PeakCode: I am rather curious what happens when two different Ring requests simultaneously attempt to mutate a common global value. since the parallelization is implicity, how would the system handle this? is it safe?

14:49 weavejester: hellofunk: Clojure refs, atoms and agents are designed to be used concurrently.

14:50 hellofunk: Atoms and refs are always updated atomically.

14:50 hellofunk: weavejester: ok, the fact that the mutations are caused by a Ring request is actually trival in this case

14:50 weavejester: hellofunk: Right, it's the same as any multi-threaded program.

15:17 arrdem: anyone aware of packages using mvn qualifiers other than -alpha-<n>?

15:17 Biofobico: is there a best practice for this: (:b my-keyword) or (my-keyword :b)

15:18 arrdem: Biofobico: both of those are a type error...

15:18 &(:foo :bar)

15:18 lazybot: ⇒ nil

15:18 arrdem: okay. meaningless. not an error.

15:19 Biofobico: :P

15:19 mavbozo: &(:P :P)

15:19 lazybot: ⇒ nil

15:20 arrdem: Biofobico: if you have a constant keyword, it's idiomatic to use it in the function call position rather than using the target lookup structure in the function call position.

15:21 Biofobico: IMO using lookup structures as functions directly should be avoided for uses of (get) which does the same thing and is somewhat more explicit.

15:25 Biofobico: arrdem: didn't understand half of what you wrote. I'm learning clojure by following a tut and testing "oposite ways" to see what happens

15:25 justin_smith: arrdem: I think that nil-returning behavior of get on non-lookup entities is responsible for X% of Clojure's "wait why did it blow up there?" phenomenon

15:26 arrdem: justin_smith: agreed, however It's A Feature™

15:26 justin_smith: Biofobico: get is like the dude, get doesn't care if you can actually get anything from the argument, it's all like "shurg, have a nil"

15:26 ,(get get get get)

15:26 clojurebot: #<core$get clojure.core$get@75ccf88d>

15:26 arrdem: bahahaha

15:26 (inc justin_smith)

15:26 lazybot: ⇒ 167

15:27 mavbozo: (inc justin_smith)

15:27 lazybot: ⇒ 168

15:27 arrdem: Biofobico: does justin_smith's code snippet there make sense?

15:28 dnolen_: Om 0.8.0 is out, new way to include the dependency [org.om/om "0.8.0"] have fun!

15:28 rhg135: Useful obfuscation technique

15:29 Biofobico: arrdem: tested and it returns nil

15:29 arrdem: $grim clojure.core/get

15:29 lazybot: http://grimoire.arrdem.com/1.6.0/clojure.core/get

15:29 arrdem: need to patch that...

15:29 Biofobico: clojure is my 1st programming language so bear with me please :)

15:30 arrdem: ofc

15:31 jaen: Hello, I'm trying to set up code reloading with http-kit by following something I did a long time ago and which actually worked, but for some reason it's not working when I'm doing the same thing in the new project. When I copy the ring reload middleware verbatim and sprinkle it with some debugging printlns it seems as if it recognizes some changed namespaces, but `(require ns-sym :reload)` somehow fails to

15:31 reload the namespace. Any hints where/how I can start debugging this?

15:31 Biofobico: get makes sense now after reading about it

15:32 arrdem: get is pretty straightforwards.

15:33 Biofobico: im founding the language pretty clear so far

15:34 hellofunk: justin_smith: i do not get your list of four gets

15:34 Biofobico: only issue i'm having, is writing the code "from the inside out"

15:34 justin_smith: ,(get [] :some-key :default)

15:34 clojurebot: :default

15:34 justin_smith: hellofunk: ^ does that help?

15:34 hellofunk: justin_smith: ah yes

15:34 it returned the function get itself, of course

15:35 justin_smith: hellofunk: indeed, and any instance of "get" in your code can thus be replaced with (get get get get) (certain macros excluded, of course)

15:35 arrdem: (dec justin_smith)

15:35 lazybot: ⇒ 167

15:36 hellofunk: lol

15:36 ,((get get get get) [1 2] 1)

15:36 clojurebot: 2

15:39 andyf: huh, new function 'random-sample' comin in 1.7.0. Seems more specialized than a lot of things in clojure.core

15:40 justin_smith: andyf: what is it exactly?

15:40 andyf: filter with a specified probability of keeping each element in a given collection

15:40 arrdem: that's... oddly specific indeed

15:40 andyf: sounds like something generative testing would use, but yeah, weird.

15:41 justin_smith: as in ##(filter (fn [_] (< (rand) 0.5)) (range 10)) ?

15:41 lazybot: ⇒ (2 3 6 7 8 9)

15:42 andyf: https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L7325

15:42 justin_smith: I must be psychic!

15:42 andyf: or my verbal description was spot on :)

15:42 justin_smith: indeed

15:43 andyf: Yeah, all the transducer stuff I knew about, but that one escaped my notice until today when I was looking at all of the new Vars added to clojure.core since 1.6.0

15:46 Complete list of new public Vars in clojure.core from 1.6.0 to 1.7.0-alpha5, for the curious: ->Eduction cat completing dedupe eduction ensure-reduced random-sample run! transduce unreduced update volatile! volatile? vreset! vswap!

15:49 justin_smith: Ooh, the 4 gets thing goes on my list of obfuscated Clojure tricks

16:02 Not much in my collection yet: https://github.com/jafingerhut/jafingerhut.github.com/blob/master/notes/obfuscated_clojure.clj Oddly enough justin_smith is highly represented :)

16:04 mavbozo: (inc andyf)

16:04 lazybot: ⇒ 22

16:10 arrdem: Thoughts on trying to stretch Grimoire to support the host languages of Clojure dialects? Eg. add Java docs for Clojure, JS for CLJS, .net for CLJCLR

16:10 seems like it adds a lot of potential pain

16:12 andyf: At least for Java, I've found the existing Java API docs hosted by Oracle to be very Google-able, and inter-class/method/etc. links are often good.

16:12 arrdem: yeah I don't think there's a lot of room to win there, the idea was just that Clojure artifacts may sometimes contain Java classes as well and that it could be worthwhile to document both.

16:13 Obviously I'm not gonna compete successfully with Oracle for java.lang docs :P

16:13 andyf: oh, you mean add part or all of the underlying Java implementation of Clojure/JVM to Grimoire?

16:13 arrdem: yeah

16:14 I'm restructuring the schema Grimoire uses so that ClojureScript can be supported properly and I'm taking the opportunity to ponder other possible changes as long as I'm making another round of breaking ones.

16:14 andyf: The audience seems like it would be limited to those wanting to modify Clojure, or add new custom data structures.

16:16 hellofunk: andyf am i confusing you with someone else, or do you have a blog post somewhere about programming chess moves?

16:16 andyf: hellofunk: Not me

16:17 Unless you mean some emails to the Clojure Google group about a performance problem involving the old Clojure hash function, which came to light because of an N-queens benchmark someone wrote.

16:18 hellofunk: no, something else i ran across a couple days ago but can't remember where i saw it

16:35 jaen: Hmm. I think I figured out my issue - the original code closed over `handlers` top level `def` directly while the new code used bidi's new option to specify a handler-resolving function and it closed over the def in the function that called it. In the former case namespace relaod changed the handlers, in the latter it did not.

16:35 Is there some subtle Clojure behaviour I'm missing here?

16:39 mrdiesel_: hi , i build my first program in C but i have this error message 'Process terminated with status 255 (0 minute(s), 0 second(s))'

16:39 arrdem: mrdiesel_: wrong channel

16:40 mrdiesel_: ok sorry

16:40 arrdem: jaen: paste code and/or error please that's not enough for us to help you with.

16:40 andyf: Not sure if I know exactly what you mean, but sometimes people use explicit Vars in their code, which adds another level of indirection at function call time, but it handles some reload cases more dynamically, e.g. call #'namespace/fn-name instead of fn-name

16:40 jaen: arrdem: that's basically it - https://gist.github.com/jaen/1e69cf7ec23adb350351

16:41 Just differs in how the top level def is closed over.

16:42 Glenjamin: jaen: defs aren't closed over

16:42 well, sort of

16:42 jaen: Yeah, I noticed sort of

16:42 Glenjamin: when you refer to something that is def'd, you get a reference to the var

16:43 and then the var is derefed when that function is called

16:43 mrdiesel_: this is the program 'include <stdio.h>

16:43 main()

16:43 {

16:43 printf("hello world");

16:43 }

16:43 '

16:43 Glenjamin: giving the effect of late-binding

16:43 andyf: mrdiesel_: still wrong channel

16:43 Glenjamin: function arguments are not late-bound, so you're dereffing the var in make-handler

16:43 jaen: Also no one likes pasting code into the channel, use gist or pastebin or somesuch

16:43 Glenjamin: I see

16:44 mrdiesel_: ohhhhhh sorry again

16:45 arrdem: ,1

16:45 clojurebot: eval service is offline

16:45 arohner: While trying to 'lein ring war', I'm getting "Exception in thread "main" java.lang.Exception: Found lib name 'foo.system' containing period with prefix 'foo.main'. lib names inside prefix lists must not contain periods". Any ideas?

16:46 arrdem: arohner: sounds like your NS form somewhere is hosed

16:46 arohner: arrdem: yeah, that's what google seems to say, but `lein uberjar` works just fine for me

16:46 arrdem: arohner: trying to do (:require [a.b [c.d :as ..]]) is the only way I know to get that error.

16:46 weavejester: arohner: Perhaps the :ring settings are wrong? A bad symbol name, perhaps?

16:47 arrdem: arohner: I'm gonna guess that you're not AOTing something that's in error so the illegal ns form slides through but that's just a guess.

16:48 arohner: AFAICT, I'm AOT'ing every file

16:48 which leads me to think weavejester's :ring theory is interesting

16:48 weavejester: lein-ring does generate namespaces for the servlet

16:49 So if the symbols it's using are wrong, that generated code might be triggering the error.

16:49 arohner: weavejester: do :init and :handler have to be in the same ns?

16:49 weavejester: arohner: They shouldn't need to be.

16:50 arohner: Which version of Lein-Ring are you using, btw?

16:50 arohner: 0.9.0

16:50 is :handler a fn of no args, or a var?

16:51 weavejester: Hm. The war-generating code has been changed a little between 0.8.13 and 0.9.0. It's possible you're hitting a bug that wasn't found in testing

16:51 arohner: *value

16:51 weavejester: :handler is a symbol to a var

16:51 jaen: Glenjamin: so then what should I do? Copypasting bidi's `make-handler` function just to be able to close over a `def` doesn't strike me as the greatest idea ever : |

16:51 arohner: ok, with no :init, it just compiled the war. I'll try to repro again

16:51 weavejester: arohner: You could try downgrading to 0.8.13, to see if the problem is the new code.

16:52 arohner: weavejester: k, I'll try that once this compile finishes

16:52 Glenjamin: jaen: we'd need to see more code to provide context

16:52 from the example you gave, its unclear why you wouldn't just use the first one

16:53 arohner: weavejester: {:init foo.main/-main, :handler foo.system/ring-handler} works fine in 0.8.13, not in 0.9.0

16:54 commenting out :init makes 0.9.0 compile

16:54 `lein ring war` in both cases

16:54 jaen: Because I think libraries are here to have their functions called instead of copied&pasted. The first code has a verbatim copy of bidi's ring handler making function. I would like to be able to just call it.

16:55 weavejester: arohner: Could you open an issue for it?

16:55 arohner: sure

16:58 jaen: Glenjamin: but I think I relised what's the solution - calling the function wwith #'handlers should provide the desired result, yes?

16:58 And it seems it works.

16:58 Thanks for pointing me in the right direction.

17:15 espinho: is it possible to have automatic real time eval in emacs like the live repl feature in lighttable?

17:18 hellofunk: espinho: just about anything is possible in emacs but i don't know of anything like that having been developed. it just takes someone to sit down and write it. i doubt it will happen.

17:18 dnolen_: http://localhost:4000/2015/01/10/faster-validation-through-immutability/

17:19 oops http://swannodette.github.io/2015/01/10/faster-validation-through-immutability/

17:21 espinho: hellofunk: Thank you

17:27 AWizzArd: dnolen_: looks like an interesting read

17:27 dagda1_: If I have a function that returns a lazy seq like this https://gist.github.com/dagda1/d99bf42f31e99ae3b07c can I loop...recur lazily?

17:28 arohner: weavejester: I have a running beanstalk instance on 0.8.13, thanks

17:28 when I SSH into the box, I see 'There are 20 security update(s) out of 70 total update(s) available'. Thought EB handled that for you?

17:28 do I need to specify a more recent AMI?

17:28 AWizzArd: dagda1_: You won’t have to use recur, you can call the function by name directly and have it return a lazy concatenation via lazy-cat

17:31 dagda1_: AWizzArd: thanks, I have not heard of lazy-cat, I"ll check that out

17:32 rhg135: dnolen_, am i correct in thinking it's basically a tagging system?

17:32 justin_smith: dagda1_: why not next instead of (seq (rest ...))

17:33 dagda1_: justin_smith: I'm not entirely sure what the difff is between rest and next

17:33 dnolen_: rhg135: yes

17:34 rhg135: cool, a-la-carte tagging would be nice

17:36 arrdem: code review - Either[Success, Failure] mini-lib https://www.refheap.com/95960

17:36 dagda1_: justin_smith: why would next be better in this situation

17:37 arrdem: dagda1_: ∀x (= (seq (rest x)) (next x))

17:38 eh I guess that fluokitten has Just, but that's not what I want here.

17:39 AWizzArd: ,[(rest nil) (next nil)]

17:39 clojurebot: [() nil]

17:40 bbloom: arrdem: what do you need this for?

17:41 Kristien: I'm about to make a video game in Clojure.

17:42 arrdem: bbloom: this is a beefed-up version of the way that I'm already handling errors in lib-grimoire because you may or may not actually be able to read documentation or examples.

17:43 bbloom: arrdem: errors like when making web requests or something?

17:44 arrdem: bbloom: yep.

17:44 bbloom: arrdem: what should a caller do when they get an error? retry? fail?

17:45 arrdem: bbloom: either, client defined. when Grimoire (which uses this as a backend) gets a failure it has error message display logic.

17:45 bbloom: arrdem: *shrug* i understand why haskell uses monadic either, but it really seems like you just want exceptions here

17:46 arrdem: bbloom: I did a version of this that used exceptions, but when writing the web error handling logic I found I missed a lot of exception cases and that this was a little easier to reason about.

17:46 AWizzArd: dagda1_: you saw that example? rest vs next?

17:47 bbloom: arrdem: wouldn't a typical client only really have one (or some small number of) top level place where it handles exceptions?

17:47 dagda1_: AWizzArd: yes, next returns nil, that clears it up

17:47 bbloom: rather than error handling logic everywhere

17:49 arrdem: bbloom: agreed which comes with a consequent condemnation of the Grimoire website for not decomplecting datastore access from view rendering enough.

17:50 meh

17:57 thheller: dnolen_: I just noticed that asserts in CLJS regenerate the whole assert form on the client

17:57 (assert (string? bar))

17:57 https://gist.github.com/thheller/ba1802e6e752097c00f5

17:58 seems like we can generate that string when compiling no?

17:59 like throw (new Error("Assert failed: (string? bar)"))?

17:59 bbloom: thheller: probably. should be an easy patch. why not give it a try?

17:59 thheller: just checking ;)

18:02 SagiCZ1: hi, does anyone know if the java call System.loadLibrary() is dependent on a package it is called from? it stops working when i move the source file with this call to a different package

18:34 thheller: CLJS-970 done

18:38 hellofunk: when you open a repl on a project with multiple profiles, how does it know which profile to select?

18:43 arohner: hellofunk: I'm pretty sure it uses a default profile (I don't know which one that is)

18:44 it's probably like dev+repl or something

18:53 emaczen: how can I setup/connect a remote repl?

18:55 gfredericks: emaczen: welp.

18:56 emaczen: um

18:56 emaczen: `lein repl` starts an nrepl server and tells you what port it's listening on

18:57 and `lein repl :connect <port-num>` starts an nrepl client

18:57 `lein help repl` might be helpfully detailed

18:57 imanc: is there a short and easy way for running a clj file, equivalent to say: python afile.py

18:57 gfredericks: imanc: there are special plugins and scripts that people have written, but nothing easy built in

18:58 clojure is a java library, not an executable of any sort

18:59 imanc: ok, i thought lein might have made something available equivalent ot lein repl

18:59 gfredericks: imanc: depending on what you consider easy, the fact that the clojure.main class can take a filename and execute it might be helpful

18:59 imanc: lein run afile.clj

18:59 gfredericks: imanc: there's a lein plugin called one-off I think that can do that and has features for dependencies too

18:59 imanc: okay, nice. - i'll check itout

19:01 gfredericks: is this pretty much the defacto way of running clj files: java -cp clojure.jar clojure.main file.clj

19:01 gfredericks: imanc: well I don't think anybody's happy typing that by hand on a regular basis but otherwise that's a perfectly fine way to do it

19:01 imanc: yeh agreed re's not wanting to type that

19:02 andyf: justin_smith: Regarding you and arrdem discussing get returning nil in many cases, there is a ticket http://dev.clojure.org/jira/browse/CLJ-1107 you could vote on if you agree with the proposed change. Looks like arrdem already has.

19:02 hellofunk: lazybot: are you just a little baby machine wearing tights at home, listening to Kenny G ???

19:02 lazybot: hellofunk: Oh, absolutely.

19:03 arrdem: andyf: I vote on most things :P

19:04 andyf: You and Bronsa :)

19:04 arrdem: yeah but Bronsa actually submits lots of good patches. I just complain and work on docs :P

19:04 andyf: well one of those is helpful to others :)

19:05 arrdem: http://i.imgur.com/88VdCIO.png hence I try not to post :D

19:07 emaczen: gfredericks: I've been trying lein repl :connect http://host:port/repl without any success

19:08 gfredericks: I'm using the Luminus framework, if that helps

19:09 imanc: so if i installed clojure via the lein installation script, where would clojure.clj be?

19:11 gfredericks: emaczen: oh this is clojurescript? if so then I have no idea

19:11 AWizzArd: imanc: you can find the clojure.clj in home/.m2/repository/org/clojure/clojure/1.6.0/clojure.jar

19:13 imanc: AWizzArd: nah, i can go as far as : /.m2/repository/org/clojure but in that directory i just have pom.contrib and tools.nrepl with no clojure.clj in those directories

19:14 AWizzArd: Then the clojure.jar was probably not downloaded yet.

19:14 imanc: AWizzArd: lein repl works tho? Does that not rely on clojure.jar?

19:14 AWizzArd: When doing “lein deps” it should somewhere say that it fetched clojure.jar

19:15 imanc: oo Couldn't find project.clj, which is needed for deps

19:15 ah OK

19:16 AWizzArd: Indeed, Leiningen depends on Clojure. And when you inspect ~/.lein/self-installs/leiningen-2.5.0-standalone.jar, then you’ll find that Clojure is inside there.

19:16 This however is a specific version which Leiningen depends on. Your own project might want to use a totally different version.

19:16 wei: is there a good way to export a memory datamic db? i was running a test instance but now have data i want to keep..

19:18 imanc: AWizzArd: yeh, I just created a random .clj file (not a full project) and wanted to run it. i've now done lein new myproj and a lein deps

19:20 AWizzArd: imanc: in principle you can create a temp dir and place a file project.clj inside. Into this file you write something like (defproject com.imanc/foo "0.1.0" :dependencies [[org.clojure/clojure "1.6.0"]])

19:20 Then you could try “lein repl” and see if that works. Also checking that .m2 path from above again. Chances are that this will indeed download the clojure.jar

19:20 emaczen: gfredericks: This is not clojurescript.

19:22 Bronsa: arrdem: I vote an all the things :P

19:23 imanc: AWizzArd: thanks. lein deps on a test project created the file - it is called clojure-1.6.0.jar though

19:24 AWizzArd: Yes yes, that’s correct.

19:26 gfredericks: emaczen: okay so where did that url come from? nrepl uses tcp sockets not http

19:27 emaczen: gfredericks: I've seen this on stackoverflow

19:27 gfredericks: exactly how do you have your server setup?

19:27 gfredericks: emaczen: I'm not sure what you're trying to accomplish; "remote repl" is a bit ambiguous

19:29 arrdem: Okay, so for the "platform" component of a Grimoire URL what would you use... the Clojure JIRA abbrev for a Clojure dialect or something else?

19:31 emaczen: gfredericks: I'm trying to learn how to connect to a repl (figuring out how on localhost is fine)

19:32 gfredericks: Right now, I just ran `lein ring server` to start my clojure program running locally on port 3000

19:34 gfredericks: If I do `M-x cider-jack-in` locally everything just seems to work (so now I'm trying to get this to work on a remote server, and connect with cider from my laptop)

19:35 gfredericks: emaczen: so you're running a web server? and you want to connect to a repl on the same jvm process as your http server?

19:38 emaczen: cider-jack-in always starts a new jvm btw

19:40 emaczen: there might be ring-related plugins for this already, but if nothing else you can start an nrepl server this way: https://github.com/clojure/tools.nrepl#embedding-nrepl-starting-a-server

19:40 weavejester: Lein-Ring can be started with nrepl

19:40 Though I tend to just use component-based REPLs these days

19:44 AWizzArd: weavejester: what do you mean by “component-based”?

19:46 weavejester: AWizzArd: A design based around Stuart Sierra's component library and "reloaded workflow" design.

20:50 gfredericks: I just thought of a basic clojure-evaluation question that I didn't know the answer to

20:50 ,(defmacro make-if [] 'if)

20:50 clojurebot: #'sandbox/make-if

20:50 gfredericks: ,((make-if) 1 2 3)

20:50 clojurebot: #<CompilerException java.lang.RuntimeException: Unable to resolve symbol: if in this context, compiling:(NO_SOURCE_PATH:0:0)>

21:09 ajmagnifico: http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/

21:09 Tried that link from the page at https://github.com/clojure/algo.monads

21:09 says the domain has expired. Does anyone have an updated link?

21:10 gfredericks: ajmagnifico: have you checked at archive.org?

21:12 ajmagnifico: gfredericks: excellent idea, thanks. I found it there.

21:18 [Blake]: Anyone up for design discussion?

21:50 arrdem: [Blake]: shoot

21:53 [Blake]: OK. I'm writing a theme park type game. I have little plots of land the player can buy and build stuff on. People walk around, get in each other's way, try to see some things and avoid others. Etc.

21:54 arrdem: Sure

21:55 [Blake]: So I'm trying to figure out how to put all the pieces together. Like...it's easy enough to say "This guy walks from this point to that point" on a plot of land.

21:58 It gets harder when there are multiple plots. And plots of land (so far) are plain. But rides have all kinds of info describing them, and getting on them involves getting into a entry queue, on to the ride, and getting into an exit queue (before going back to a plot of land).

21:59 So I'm favoring have a master list of everything that responds to a clock tick. And another list of, say, people and their locations (if they're on a ride or whatever).

21:59 Oh, I'm using an entity-system type thing: Everything in the game has a unique ID.

22:02 That's where I get stuck, I think: At the begining of each turn, I assess the order of actions. And each thing that can act has its own "act" method. Polymorphism. But I'm not sure I'm not just carrying OO baggae.

22:02 er baggage

22:09 swedishfish: sdegutis: have you watched this? https://www.youtube.com/watch?v=P76Vbsk_3J0

22:13 arrdem: [Blake]: So I think that it's reasonable to have things that "tick" but I'd be careful with how your player interacts with this state.

22:17 [Blake]: arrdem: So, I have this master list of people, rides, service techs, natural events (fire, flood)...there's a tick and I get a list of everything that "moves" this turn.

22:17 I guess I just dynamically dispatch based on type?

22:17 arrdem: Yep.

22:17 Protocols FTW

22:18 [Blake]: Heh. OK, well, I guess I shouldn't stress it. I'll do it and learn.

22:18 arrdem: Clojure: what's the worst that can happen? It's immutable.

22:19 [Blake]: I'm not too worried about the player interaction. The player is mostly indirect.

22:19 arrdem: I'm just mentioning the player because a callback based player has really wrought hell in one of my side projects.

22:19 [Blake]: arrdem: I guess my big fear is that I can see passing the entire game state around all over and that seems so very--not in the spirit of Clojure.

22:20 arrdem: Ooh. So...the player triggers code that can disrupt things?

22:20 How...functionally reactive.

22:20 arrdem: sorta kinda

22:20 The issue is that there are dice rolls where players may choose outocomes

22:20 [Blake]: 'k

22:21 arrdem: so you have your grand "turn", but while stuff is resolving there are decision points

22:21 so it's not like Event -> Event -> Event, there are these weird contexts

22:23 [Blake]: arrdem: Interesting. That sounds like it's complex by nature.

22:24 arrdem: [Blake]: sadly

22:24 [Blake]: heh...well, that can be fun (to play if not program)

22:24 arrdem: it makes building an AI player hard because you could fail a dice roll and have to re-plan

22:24 $google privateer press warmachine

22:24 lazybot: [WARMACHINE | Privateer Press] http://privateerpress.com/warmachine

22:25 [Blake]: arrdem: Oh, jeez, yeah. I haven't been into gaming much in recent years but bad AI was the norm when I was.

22:25 arrdem: basically the entire game is based on taking 2d6+base >= stat checks

22:26 so thinking about this: http://swannodette.github.io/2015/01/09/life-with-dynamic-typing/

22:26 is it acceptable to do the same thing with a protocol?

22:27 I've never seen it done, but it seems like it should be...

22:27 sdegutis: swedishfish: youtube's blocked so no

22:28 [Blake]: (reading)

22:29 arrdem: s/protocol/multimethod/g

22:30 [Blake]: So...assertions with protocols/multimethods?

22:31 arrdem: yeah

22:32 What I've been doing is putting assertions on multimethod imp'ls and lifting them to defs, but putting assertions at the API layer in the multimethod would be nice

22:33 [Blake]: arrdem: I could see that working, at least in theory. I've only done a few multimethods so far.

22:35 But it reminds me a lot of Eiffel, the contract pre/post stuff.

22:48 OK, so the way I see it, I'm going to have a "location list". Every object in the game will have an entry in the list saying where it is. So people are on certain plots of land or on particular rides. Rides are on particular plots of land. I think that'll work. That'll be the most dynamic, vis a vis state.

22:49 I mean, I have to keep a bunch of lists for each game-object that can contain other game-objects, but a master list means I can ask "Where is object X" without having to iterate over all the lists.

22:51 arrdem: Sounds reasonable to me

22:51 tomjack: [Blake]: why not use a map instead of a list of entries?

22:52 [Blake]: tomjack: Yeah, it'll be a map objid=>whereitis. I was using "list" casually.

23:01 tomjack: speaking of maps as quotients of sequentials..

23:01 (defn concat' [x y] {:post [(if (= x nil) (= % y) true)]} (concat x y))

23:02 justin_smith: [Blake]: the location list thing reminds me of the component entity system dedign

23:03 tomjack: I guess maybe we want (nil? (seq x)) or something in the :post

23:03 is this a badly placed runtime assertion?

23:04 justin_smith: tomjack: (empty? x) would be better

23:04 tomjack: sure

23:04 [Blake]: justin_smith: Yeah, that's what I'm doing. Everything's split up into lists, which is cool for processing. But trying to organize all the processes...it's not intuitive, you know? It's not like "Here's a park. Here's a ride. Here's a person."

23:04 tomjack: is the benefit from a type system which can express this postcondition not an immediate benefit of stronger types? :)

23:05 also, that's just left unit. how about associativity of concat?

23:06 presumably there is an implicit "the immediate benefit of stronger types [in type systems which have practical implementations today]"

23:07 justin_smith: tomjack: what does associativity mesn in this context?

23:08 tomjack: (prop/for-all [x _ y _ z _] (= (concat' x (concat' y z)) (concat' (concat' x y) z)))

23:09 arrdem: Dependent type systems like Agda and Idris can prove that

23:09 but they're very much research efforts still

23:09 give it a couple years

23:10 tomjack: yes, hence the presumable "practical implementations today" qualification, which is a totally reasonable qualification :)

23:11 the concat' above fails for e.g. args '(nil {}) because (not= () {})

23:15 justin_smith: tomjack: and ##(not= nil ())

23:15 lazybot: ⇒ true

23:17 tomjack: yeah, that postcondition is bad, just an example

23:18 justin_smith: that's an easy gotcha to run into if Clojure is not your first lisp

23:19 cursivecode: I've read multimethods are slow. Are they just slow relative to protocols, or are they slow in general? ... if I used a bunch of conditionals would it be the same as using multimethods?

23:19 tomjack: I guess that "stronger types [with a practical language today]" is mostly about catching errors you can catch easily with pre/post, and the errors you catch in e.g. test.check tests are "stronger types [with a practical language maybe someday]"

23:20 arrdem: cursivecode: slower by ~5-10x than a protocol if I remember @mikera's numbers

23:21 cursivecode: Does anyone know if there's a performance difference in Clojurescript?

23:22 arrdem: thanks

23:28 rritoch: Does anyone know if there is a proof, that LISP is the ideal syntax for a "functional" language? By functional I simply mean a language without operators, not the typical usage of functional. I've been doing some work trying to build a C like grammer with typedef's and dynamic operators and have run into ambiguity cases that can only be resolved at runtime.

23:31 I made a few estimates and calculations and it seems that replacing operators with functions would be more efficient than checking the "type" of a symbol each time a symbol is "seen" by the parser. This lead me to a conclusion that a language without operators would is possibly the most efficient language that can exist with dynamic operators.

23:32 err, would be/is possibly

23:33 I'm just wondering if there is any scientific or mathematical proofs which actually define the LISP syntax as being the optimal syntax when there is a need to add operators at runtime.

23:35 arrdem: You're asking for a proof of compression optimality, which is an NP hard problem. I'm gonna take a shot in the dark here: Non-optimal, the proof is left as an exercise to the reader.

23:37 rritoch: arrdem: Thanks, that answers my question. My estimates were all related to the number of operations it takes to resolve a symbol to a type, vs the number of operations it takes to do a search/replace. Getting hard data for that would be difficult, if not impossible.

23:38 arrdem: With my limited programming language development experience though, even if it is not provable, it does seem that LISP is the ideal syntax for adding "operators" at runtime.

23:41 arrdem: At this point I've reverted from trying to make proof of concept implementation back to the theoretical/research phase since the cost of dynamic operators in an algorithmic language seems to be quite high, both in terms of CPU usage and memory usage.

23:51 On a more positive note. I've proven that the lein-sablecc plugin I developed is fully functional. I was able to build a language and interpreter with it, but things fell apart as soon as I tried to add operators at runtime. More specifically, it would be consulting an object defined with the language during compilation of new sources to determine new operators.

23:56 arrdem: What utility do you see that adding to a langauge beyond complexity

Logging service provided by n01se.net