2017-06-28
| 00:41 | mgaare | Why don't specs implement IFn |
| 00:43 | mgaare | It would be so nice to have something like, (s/spec a-bunch-of-predicate-logic-here :gen useful-generator) and have the result be callable as a normal pred |
| 01:01 | amalloy | aren't specs able to do a bunch of things? conform a thing, act like a predicate, produce an error report...which of those things should it do when called as a function? |
| 01:03 | mgaare | I think they should act like a predicate |
| 01:04 | mgaare | as it stands now, maybe I'ved missed it but I don't see a way to get the same functionality that, say, int? has. Where it can be used as a spec, can be called as a predicate function, and has a generator |
| 01:04 | amalloy | my point is, what if someone else disagrees? why should a predicate be the most interesting thing? |
| 01:04 | amalloy | suppose i think it would be far better if it produced an error report instead |
| 01:05 | amalloy | isn't it great that when i read your code, i can tell what you're using a spec for by what function you call on the spec? |
| 01:06 | mgaare | another solution would be if spec supported adding generators to functions |
| 01:07 | mgaare | or I wonder if you could do that with fdef |
| 01:07 | mgaare | hmm |
| 01:07 | mgaare | doesn't seem that way |
| 01:09 | mgaare | amalloy: and to your question, right now we both lose because they do nothing at all ;) |
| 01:09 | amalloy | no, i win because i actually think it's great to have named functions for doing things |
| 01:11 | mgaare | So do I, but it seems like our predicates are destined to be second class citizens to the built-in clojure ones, as they get generator support and we have no apparent way to add it to ours |
| 01:13 | amalloy | what's this about generator support for core functions? i haven't really kept up with development |
| 01:17 | mgaare | a whole slew of predicates in core have generators. So you can call s/gen on a variety of things like nat-int?, inst?, string? and so forth and it returns a generator. But the mechanism for this is a hard-coded list in clojure.spec.gen.alpha |
| 01:23 | amalloy | is it? i agree that that sounds pretty silly. i can't find the source for that, though |
| 01:23 | amalloy | i could easily be missing it, of course. as i said, i'm not current |
| 01:34 | mgaare | it's a little tricky to trace. `s/gen` calls down to `gensub` which calls `specize` on the predicate, which calls `spec-impl` for objects, that reifies a thing that will call spec.gen.alpha/gen-for-pred to get a generator, which is this here https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/gen/alpha.clj#L187 that gets the generator out of this map https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/ |
| 01:34 | mgaare | gen/alpha.clj#L128 |
| 01:38 | amalloy | huh. confirmed, that seems lame |
| 01:53 | dysfun | spec is a bit crack |
| 01:54 | TEttinger | a spec for crack though, now that woul be something |
| 02:04 | TEttinger | hm. thinking about https://github.com/rschmitt/dynamic-object and (de)serialization of hand-written values as opposed to machine-written ones |
| 02:05 | TEttinger | I love clojure's approach to writing data for prototyping, and I don't know if I have seen or used something better at that use |
| 02:07 | TEttinger | I don't know if there's any kind of way that homogeneous, no-types-declared collections will easily translate to Java, which is of course pretty strict on types if you want to get your IDE to do any work |
| 02:22 | dysfun | TEttinger: sure, List<String> is just List<Object> at runtime |
| 02:23 | dysfun | well, it's more like List at runtime, really, but you get my point |
| 02:24 | TEttinger | yes, but the IDE won't be too helpful if you constantly cast each Object you get from a List to String in Java code |
| 02:24 | dysfun | oh well |
| 02:24 | dysfun | shouldn't rely on an IDE then |
| 02:25 | TEttinger | you shouldn't rely on coffee then |
| 02:25 | dysfun | i shouldn't, you're right. |
| 02:25 | TEttinger | heh |
| 02:25 | TEttinger | unneeded performance boost! |
| 02:26 | dysfun | meanwhile, i've given up trying to be polite in an email and left it at "call me" |
| 02:26 | TEttinger | , maybe |
| 02:26 | clojurebot | #error {\n :cause "Unable to resolve symbol: maybe in this context"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.RuntimeException: Unable to resolve symbol: maybe in this context, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6719]}\n {:type java.lang.RuntimeException\n :message "Unable to resolve symbol: maybe in this... |
| 02:26 | TEttinger | ,:maybe |
| 02:26 | clojurebot | :maybe |
| 02:26 | TEttinger | there. |
| 02:27 | dysfun | hello, maybe |
| 02:27 | TEttinger | this is your cousin, maybee |
| 03:35 | Wxckol | Hey |
| 03:36 | TEttinger | hello |
| 03:36 | dysfun | wotcha |
| 03:39 | Wxckol | What does Clojure is for? |
| 03:39 | dysfun | it's a general purpose programming language |
| 03:39 | Wxckol | general purpose like what? |
| 03:39 | Wxckol | im a general ignorant :D |
| 03:39 | dysfun | well, in short, you can do most things with it |
| 03:40 | Wxckol | What do you do with it? |
| 03:40 | dysfun | build websites mostly |
| 03:40 | Wxckol | Can you show me something you built with it? |
| 03:41 | dysfun | https://github.com/irresponsible/anarchy is a simple business logic engine i built in clojure |
| 03:42 | Para_ | "business logic engine" sounds horribly engineered solution looking for a problem |
| 03:42 | Wxckol | Mh, ok (thank you), but it's beyond my knowledge for now. What about a website, can you show me a website? |
| 03:42 | Para_ | YMMV of course, rule engines have never appealed to me |
| 03:42 | dysfun | it's not a traditional rule engine |
| 03:43 | dysfun | i can show you a website, but that it's written in clojure will make no difference to your experience of it |
| 03:43 | Wxckol | Just wanted to take a look you know |
| 03:43 | dysfun | that's the thing about general purpose programming languages - they can all do much the same things, they just do them differently |
| 03:43 | dysfun | well, https://bleeckerburger.co.uk/ is written in clojure, but what i said above |
| 03:44 | Wxckol | So you write all this with code? No gui at all |
| 03:44 | Wxckol | ? |
| 03:47 | dysfun | yup |
| 03:47 | dysfun | i mean the html can be trivially edited in a gui if you want, but i choose not to |
| 03:48 | dysfun | well, actually the template engine probably knackers gui editability |
| 03:48 | dysfun | but it started as html and then i just edited the template in |
| 03:49 | dysfun | https://github.com/tonsky/datascript-chat # here is an example of a more dynamic app written in clojurescript |
| 03:49 | dysfun | the burger site actually has a very shiny admin panel written in clojurescript too, but i can't show you that |
| 04:25 | Hanonim | Yop |
| 04:25 | dysfun | hiya |
| 04:25 | Hanonim | good ? |
| 04:26 | dysfun | okay i guess, tired |
| 04:29 | osfabibisi | apparently Kafka think they have solved Exactly Once |
| 04:30 | dysfun | well, they're presumably trying to compete with flink |
| 04:30 | dysfun | (flink claims exactly-once, so it's nice to know we've made such progress with the provably hard german tank commander problem |
| 04:35 | Hanonim | osfabibisi: only in special occasions |
| 04:35 | osfabibisi | so what's the flink-definition-of-exactly-once? |
| 04:36 | dysfun | i suspect "exactly once except when something goes wrong" |
| 04:36 | osfabibisi | ah |
| 04:36 | osfabibisi | so "not exactly once" |
| 04:37 | dysfun | well, we have a mathematical proof of the hardness of this problem, so... |
| 04:38 | dysfun | anyway, exactly once except in case of error is basically as good as you can get, it's what you do in the case of error that matters |
| 04:39 | osfabibisi | ah ok |
| 04:39 | osfabibisi | and does that work out being better than at-least-once in practice? |
| 04:39 | dysfun | it depends what you're doing |
| 04:40 | dysfun | there is also the other category - at most once |
| 04:41 | dysfun | at most once is what linkedin use for emails, because noone complains if they don't receive yet another fucking email |
| 04:44 | TEttinger | linkedin can show restraint on emails??? |
| 04:44 | TEttinger | how surprising |
| 04:44 | TEttinger | I think they're just a spam council |
| 04:45 | dysfun | no, the outage of linkedin services can show restraint on emails |
| 04:46 | Carr0t | I wonder if they've done it by decoupling message sending from message sent checking. e.g. message is sent with UUID. Instead of resending on no ack you just keep asking "Have you got the message with UUID X" until you get either a yes or no. ON yes, mark as sent. On no, resend message with new UUID and start asking about *that* one |
| 04:47 | dysfun | ah, but what happens if the machine receives it but then goes offline before it's done processing? |
| 04:47 | osfabibisi | ah yes, I understood that better written down ;-) |
| 04:49 | osfabibisi | ah yes, this is the thing that Paxos is meant to solve? (still not entirely sure what that is, I keep confusing it with the stock cube) |
| 04:51 | dysfun | paxos and raft attempt to solve this problem, yes |
| 04:52 | TEttinger | they just need to verify that they have received the message before the message was sent. a clear violation of causality, just like uber is a clear violation of taxi regulations |
| 04:53 | torrent-of-ions | Is "Joy of Clojure" worth buying considering a) I'm a Common Lisper, and b) it's only v1.4 of Clojure? |
| 04:53 | osfabibisi | I liked JoC |
| 04:54 | dysfun | it depends on whether it's the sort of book you like to read |
| 04:54 | TEttinger | clojure's different enough from CL, so having a good book helps |
| 04:54 | torrent-of-ions | Oh, it's 1.6 actually |
| 04:54 | TEttinger | not very old huh |
| 04:54 | TEttinger | 1.7 was transducers? or was that 1.6? |
| 04:54 | torrent-of-ions | 1.7 |
| 04:54 | dysfun | 1.7 |
| 04:55 | TEttinger | I still haven't used a transducer knowingly |
| 04:55 | torrent-of-ions | I suppose those additions don't change the fundamentals |
| 04:56 | torrent-of-ions | I'm still a bit unclear about fundamentals like names and symbols |
| 04:56 | dysfun | no, not at all |
| 04:57 | dysfun | i use transducers quite a lot, but i don't write new ones |
| 04:58 | torrent-of-ions | Also I hate buying technical books just before a new version comes out, but I can't find any news about a 3rd edition |
| 04:58 | torrent-of-ions | I'll order it then |
| 04:58 | dysfun | i can't see a new edition this year |
| 04:58 | dysfun | or in the near future, generally |
| 04:59 | dysfun | not with significant changes anyway, maybe a reprint |
| 05:20 | osfabibisi | dysfun: are you back on Clojure, or just visiting? ;-) |
| 05:21 | Hanonim | it's weird, after quite a while with clojure, i start missing imperative langs |
| 05:21 | dysfun | i am writing clojure, but not contributing to it |
| 05:21 | osfabibisi | aha |
| 05:22 | osfabibisi | contributing to core? or to your irresponsible modules? |
| 05:22 | dysfun | core. i am still doing irresponsible because i use them too |
| 05:22 | osfabibisi | good good |
| 05:25 | dysfun | i've stopped bothering with the site though |
| 05:25 | deadghost | hmm did dysfun jump langs? |
| 05:25 | dysfun | it will eventually turn into the irresponsible software guild and include stuff written in kotlin, or if i ever build the damn thing, kotlisp |
| 05:26 | Hanonim | so you've chosen kotlin for your lisp ? |
| 05:26 | dysfun | it's seeming likely, but it's not definite |
| 05:27 | Hanonim | because of the type system ? |
| 05:28 | dysfun | i use a lot of java libraries. using them is often a pain in clojure because it tries to have its two worlds thing, whereas kotlin just does java++ |
| 05:28 | osfabibisi | oh? we've found Java interop in clojure quite painless |
| 05:28 | dysfun | kotlin is not going to win any awards for outstanding revolutionary design, it's just java++, done quite wel |
| 05:28 | dysfun | well* |
| 05:29 | dysfun | it is often painless, but equally often painful |
| 05:50 | osfabibisi | ah, hehe |
| 05:51 | dysfun | :D i just talked a potential client into making an improved offer |
| 05:51 | osfabibisi | woohoo |
| 05:55 | audriusm | how will you improve the offer? :) |
| 05:55 | dysfun | well i improved it by asking them to improve it |
| 05:55 | dysfun | and mentioned what their competition were offering |
| 06:00 | osfabibisi | is it a fun gig? |
| 06:01 | dysfun | no, but it's less un-fun that commuting 2 hours a day |
| 06:01 | dysfun | than* |
| 06:10 | osfabibisi | this is true |
| 06:13 | dysfun | gosh, there are engineers up the pylon i see out of my window. it's the most interesting thing i've seen this week |
| 08:05 | Makis | Is there a trick to using Himera and tryclj because I keep getting errors even when I use some existing examples? |
| 08:28 | osfabibisi | so the Clojure example in http://www.bradcypert.com/5-programming-languages-you-could-learn-from/ |
| 08:28 | osfabibisi | why doesn't that blow it's stack? does lazy-cat do some magic there, even though it's not tail recursive? |
| 08:43 | raek | osfabibisi: yes. lazy-cat is a macro. the body of the lazy-cat form is wrapped up in a thunk and that thunk is returned immediately. |
| 08:43 | dysfun | i just looked at the source, it uses concat |
| 08:43 | dysfun | so if it's a very big list, and you realise it all at once, it likely *will* blow its stack |
| 08:45 | raek | oh, you're right. it calls concat immediately. but the arguments to concat are wrapped in lazy-seq which is the thing that delays the computation |
| 08:46 | dysfun | also that quicksort assumes uniquity in the source collection |
| 08:46 | raek | so (lazy-cat e1 e2 ... e1000) becomes (concat (lazy-seq e1) (lazy-seq e2) ... (lazy-seq e1000)) |
| 08:46 | dysfun | it will actually malfunction if an item goes twice by the looks of it |
| 08:46 | raek | dysfun: why would a big list blow the stack? |
| 08:47 | dysfun | raek: because each call to realise a new element adds a stack frame |
| 08:48 | dysfun | haskell only gets away with pervasive laziness because the VM is built around it |
| 08:48 | raek | hmm right. |
| 08:49 | raek | if the partition is perfectly balanced the lazy sequences becomes nested at a depth of log2 n |
| 08:49 | dysfun | yes. in general we assume the stack size is log(heapsize) |
| 08:50 | dysfun | much garbage collection theory is based on that |
| 08:51 | raek | but I suppose you could design a pathological input that makes all the partitions maximally unbalanced, so that the stack depth needed would indeed be proportional to n! |
| 08:51 | dysfun | even with a perfect balance, it's vulnerable to large inputs |
| 08:52 | dysfun | periodically someone turns up asking about a very strange stack overflow bug that appeared in production and it usually turns out it's concat's fault |
| 09:33 | osfabibisi | raek, dysfun: sorry, got called away after asking that question |
| 09:33 | osfabibisi | so essentially that function *will* blow its stack on large inputs? |
| 09:34 | dysfun | pretty much |
| 09:34 | osfabibisi | cool. that was my guess, but I thought I'd ask in case I'd misunderstood ;-) |
| 10:31 | dysfun | so i spent an hour today tracking down a bug that would have been pointed out by even a half-arsed type system |
| 10:31 | TMA | would it help if (lazy-cat e1 e2 ... e1000) were (concat (lazy-seq e1) (lazy-cat e2 ... e1000))? |
| 10:32 | dysfun | it might be less awful than the original in a large collection, but it still suffers the same basic flaw |
| 10:32 | dysfun | but making it lazy seems rather pointless |
| 10:36 | osfabibisi | is there an existing function for (defn check [message predicate] (when (predicate message) message)) ? |
| 10:36 | osfabibisi | e.g. you can (some-> thing (check some-predicate?) (...)) |
| 10:38 | dysfun | er, isn't that... some-> ? |
| 10:40 | osfabibisi | hmm, not exactly, some-> would just path the truthy value of the predicate |
| 10:40 | osfabibisi | it's a converter from truthy predicate into threading macro guard |
| 10:40 | dysfun | cond-> ? |
| 10:41 | dysfun | but i think from your description not quite |
| 10:43 | osfabibisi | I can't figure out cond-> at all tbh ;-) |
| 10:44 | osfabibisi | I think the `check` definition should do for now, ta for suggestions |
| 10:44 | dysfun | ,(for [i (range 10)] (cond-> i odd? #(* 2 %))) |
| 10:45 | clojurebot | (#object[sandbox$eval40$iter__36__41$fn__42$fn__43$G__44__45 0x23881a68 "sandbox$eval40$iter__36__41$fn__42$fn__43$G__44__45@23881a68"] #object[sandbox$eval40$iter__36__41$fn__42$fn__43$G__44__45 0x3c7db411 "sandbox$eval40$iter__36__41$fn__42$fn__43$G__44__45@3c7db411"] #object[sandbox$eval40$iter__36__41$fn__42$fn__43$G__44__45 0x60b8e71d "sandbox$eval40$iter__36__41$fn__42$fn__43$G__44__45@60b8e... |
| 10:45 | dysfun | ,(for [i (range 10)] (cond-> i odd? (#(* 2 %)))) |
| 10:45 | clojurebot | (0 2 4 6 8 ...) |
| 10:46 | osfabibisi | ,(map #(* 2 %) (range 10)) |
| 10:46 | clojurebot | (0 2 4 6 8 ...) |
| 10:46 | osfabibisi | I still don't understand your example |
| 10:46 | osfabibisi | if it's odd, it multiplies by 2? wouldn't that be (2, 6, 10, ...) ? |
| 10:46 | dysfun | well, it's like -> except i inserted the predicate odd? in the middle |
| 10:48 | dysfun | to be fair, *i* was expecting to see something different as well |
| 10:48 | osfabibisi | yeah, I have no idea what it's doing though |
| 10:48 | osfabibisi | ah, heh |
| 13:05 | TimMc | Thing I'm really tired of in Clojure: Not being certain I can find all the places my code looks at a really generically named key in a datastructure. |
| 13:05 | TimMc | Like if I have something under :data in a map, there's no good way to grep for all references to that. |
| 13:07 | osfameron | hmm. that's a weakly typed issue in general isn't it? |
| 13:07 | osfameron | or possibly a hint that you should rename your map keys to something less generic |
| 13:07 | dysfun | namespaced keywords (which rich has been banging on about a lot recently) *almost* solve that one |
| 13:08 | dysfun | (as in you need to substitute something that reads namespaces for grep in order to solve the typical case) |
| 13:09 | osfameron | it's interesting that Haskell just avoids having arbitrary maps of data and makes you model your structures up front |
| 13:09 | osfameron | I sometimes wonder if that trade-off would be worth it |
| 13:09 | dysfun | michael snoyman said he'd quite like to remove tuples |
| 13:09 | osfameron | I mean, on larger-scale projects than I've worked on |
| 13:10 | technomancy | osfameron: you don't need haskell for that; racket encourages that too |
| 13:10 | osfameron | at least tuples are typed-ish, but yeah |
| 13:10 | osfameron | right, I should have said "e.g. Haskell" ;-) |
| 13:10 | osfameron | the generic maps thing is Oh So Convenient for as long as you can actually remember and reason about what you're doing |
| 13:10 | osfameron | (which for me isn't very long, as I am a simple squirrel) |
| 13:10 | dysfun | it's really good for prototyping |
| 13:11 | dysfun | and it's very good for eliminating boilerplate |
| 13:11 | dysfun | but as your code grows up, i don't think it scales |
| 13:11 | osfameron | right, I've not done sufficiently complex things in Haskell to worry about boilerplate tbh |
| 13:11 | osfameron | I should fix that... |
| 13:11 | dysfun | i got lost down the GHC.Generics hole a couple of months back |
| 13:12 | dysfun | the other thing maps are good for is interop with json |
| 13:12 | justin_smith | the compromise that works best for me is arbitrary hash-maps locally (eg. within one set of tightly coupled namespaces in one project) and schemas / defrecords between subsystems, enforcing specific keys or even using types instead of mappy things |
| 13:13 | justin_smith | kind of like how shorter names are OK in small scopes but as scope gets larger you want more and more description in a name |
| 13:13 | TimMc | osfameron: Yeah, weakly typed and some related stuff. If I had schemas on all this code, I could find references to the schemas... |
| 13:13 | justin_smith | plain-old-arbitrary-data is great in small scopes, more reification and validation of structures is good for things that crosses boundaries |
| 13:14 | justin_smith | haha |
| 13:14 | osfameron | justin_smith: interesthing - though again, at odds with dysfun's comment that it's the *weakly* typed stuff that is trivial to serialize and pass as JSON ;-) |
| 13:14 | technomancy | also we need letrec |
| 13:14 | osfameron | the French painter? ;-P |
| 13:15 | dysfun | no, we don't need letrec, we need letnonrec |
| 13:15 | justin_smith | osfameron: right, I think more structure at boundaries and less in the implementation is the path to sanity, not the opposite |
| 13:15 | TimMc | so we should use SOAP is what I'm hearing |
| 13:15 | technomancy | dysfun: reference to https://news.ycombinator.com/item?id=8226934 |
| 13:15 | justin_smith | oh man... |
| 13:16 | osfameron | TimMc: eeeeek! |
| 13:18 | dysfun | shouldn't you sit *on* the sofa to watch doctor who? |
| 13:18 | justin_smith | TimMc: SOAP is terrible, but I'd still want the type checking at the edges to be strictest - because that's where you get the real cascading problems, is stuff that comes from furthest away, because you need to read unrelated code to understand it, and the odds that no one dev read both parts of the codebase increase... |
| 13:18 | technomancy | dysfun: you start out on the sofa and you end up behind the sofa once the daleks show up |
| 13:18 | technomancy | it's traditional |
| 13:18 | justin_smith | also it's attention span friendly / uses human working memory nicely |
| 13:19 | technomancy | justin_smith: starting to sound like grpc |
| 13:19 | TimMc | also aparently everyone watches movies on their smartwatches or whatever these days so it's not like you need to be in any one place |
| 13:19 | dysfun | yeah, there's no better experience than watching a movie on a tiny tiny screen |
| 13:20 | TimMc | I pipe all YouTube videos to my NumLock light. |
| 13:20 | justin_smith | dysfun: I assume you've seen the classic David Lynch rant https://www.youtube.com/watch?v=wKiIroiCvZ0 |
| 13:20 | justin_smith | technomancy: I'm unfamiliar... is it any good? |
| 13:20 | technomancy | justin_smith: well... no? maybe? I don't know. |
| 13:21 | technomancy | it has some neat features, but protobufs are annoying. |
| 13:21 | technomancy | grpc alone is pretty nice; it gives you distributed tracing, cancellation of requests, automated backoff, etc |
| 13:22 | technomancy | but it's usually paired with protobufs which are efficient but extremely awkward to use from clojure |
| 13:22 | technomancy | I think grpc with edn or transit would be pretty nice |
| 13:22 | dysfun | s/to use from clojure// # FTFY |
| 13:22 | technomancy | with specs at the service boundaries, of course |
| 13:22 | justin_smith | of course |
| 13:23 | technomancy | the problem is if you use protobufs then people think that means you don't need spec, but protobufs are lame |
| 13:23 | dysfun | nah, you need to generate protocolbuffers from specs |
| 13:23 | technomancy | hm... now there's an idea |
| 13:23 | dysfun | because specs are better than types |
| 13:24 | justin_smith | the system I have in development now uses transit event sourcing on kafka topics to build a state deterministically from a log, and all the job logic is driven by the state in a model derived from petri nets (pool of available workers, pool of jobs, join the two to represent an "in progress" object, etc.) |
| 13:24 | technomancy | is it common to keep specs for a service in a separate dependency artifact from any implementation? |
| 13:24 | justin_smith | where the jobs and workers and in progress tasks are all reified in a hash map that is guaranteed identical in every process |
| 13:25 | dysfun | technomancy: i haven't seen anyone do that yet |
| 13:25 | justin_smith | technomancy: I do this for things that talk to two separate codebases, I have app a, app b, and then schemas c which is referenced by a and b |
| 13:25 | justin_smith | this was designed before spec though |
| 13:26 | justin_smith | that seems likea pretty natural choice |
| 13:26 | justin_smith | I think spec is a bit too new for there to be real systems with N>1 apps in communication and specs in between |
| 13:27 | hiredman | itym the tooling for protobufs is lame |
| 13:27 | technomancy | surely N=2 isn't that rare? |
| 13:27 | justin_smith | oh yeah, some people put their cljs in separate apps right |
| 13:28 | technomancy | hiredman: well sure, but what motivation to improve the tooling is there? even if it were decent it still isn't very compelling vs transit or msgpack |
| 13:35 | hiredman | yeah, I am not sure for message passing kind of things, I am thinking more about storing data, I've had bad experiences processing lots of edn |
| 13:36 | technomancy | interesting; yeah that's different |
| 13:40 | gpk | Is Clojure any good for small command line programs? I'm thinking that the jvm overhead would be too high |
| 13:40 | justin_smith | the jvm overhead is fine! it's the clojure compiler that's too heavy for that |
| 13:41 | justin_smith | though cljs + node for command line can work (see for example lumo which is a self hosting clojurescript you can install via npm) |
| 13:41 | technomancy | I would recommend racket or ocaml for small programs instead |
| 13:41 | justin_smith | yeah, ocaml is great for small things that have to start up quickly |
| 13:41 | gpk | I currently use python |
| 13:41 | hiredman | it depends on what you find acceptable |
| 13:41 | gpk | Or C |
| 13:42 | hiredman | I write a lot of command line stuff in clojure, but most of it is sort of fire in forget or it is a long running task anyway |
| 13:44 | gpk | justin_smith: can one not compile clojure ahead of time? |
| 13:45 | justin_smith | you can, but there's still a certain amount of bootstrapping of clojure itself |
| 13:46 | gpk | What's it like compared to SBCL binaries? |
| 13:46 | gpk | Those are actually fast enough even though they are huge |
| 13:47 | justin_smith | it's not the size, it's the amount of work done on startup, and there's nothing outside of experimental unofficial forks that eliminate that startup work |
| 13:47 | gpk | I'm wondering what project I'm going to rewrite to teach me clojure, by the way |
| 13:50 | technomancy | sbcl just dumps its whole memory image, right? |
| 13:50 | technomancy | I don't think you can come close to that on the jvm |
| 13:50 | gpk | Yeah |
| 13:50 | gpk | As far as I understand |
| 13:51 | technomancy | gpk: do you have a use case in mind or is this more of just a general question? |
| 13:53 | gpk | Just a general question... It would be nice to use Lisp instead of Python |
| 13:53 | gpk | But I write a lot of tiny things |
| 13:53 | technomancy | IMO racket is a much better python replacement |
| 13:54 | technomancy | or hylang of course |
| 13:54 | gpk | Unfortunately racket is not cool... |
| 13:54 | gpk | Sounds stupid, I know |
| 13:55 | technomancy | racket is h*ckin' cool; you take that back |
| 13:55 | gpk | *I* know it's cool |
| 13:55 | technomancy | but if it's not cool enough you could always try... |
| 13:55 | technomancy | rackjure! |
| 13:55 | gpk | Unfortunately nobody will pay me to write Racket |
| 13:56 | technomancy | http://docs.racket-lang.org/rackjure/index.html <- racket with sunglasses on |
| 13:56 | gpk | *won't |
| 13:58 | gpk | So I think Clojure sits more where CL is |
| 13:59 | gpk | Which is good because it does some things better than CL I think |
| 13:59 | gpk | I always thought it would be cool to get paid to write Lisp as well |
| 14:05 | osfameron | <dysfun> because specs are better than types <-- oh? |
| 14:07 | dysfun | osfameron: (not serious) |
| 14:07 | dysfun | it's one of my current pet peeves that people keep saying this when it blatantly isn't true |
| 14:08 | osfameron | ah |
| 14:09 | technomancy | huh; refactor-nrepl claims to be able to do slamhound-type things |
| 14:09 | technomancy | this looks pretty nice; does it work well? |
| 14:11 | technomancy | apparently not? |
| 14:11 | dysfun | you may be overestimating how many people have tried it |
| 14:12 | technomancy | no, I mean I tried it and it doesn't work |
| 14:12 | dysfun | oh, heh |
| 14:12 | technomancy | why the heck would a query for find-usages require a namespace/name *and* a filename, line number, and column number? |
| 14:13 | Frozenlock | technomancy: slamhound? |
| 14:13 | Frozenlock | oh https://github.com/technomancy/slamhound |
| 14:14 | osfameron | "Slamhound rips your ns form apart and reconstructs it. No Dutch surgeon required." (I have no idea what that means, which makes the description a little bit less useful than something, er, descriptive, but it sounds cool, I guess) |
| 14:14 | dysfun | osfameron: essentially it will rewrite your imports |
| 14:15 | technomancy | the "usage" section has a demo |
| 14:17 | osfameron | I think the first time I looked at the docs I gave up by then as I didn't know what problem it was trying to solve |
| 14:23 | osfameron | ah I see, it's the "tidy up imports" function that some IDEs have |
| 14:23 | osfameron | I've occasionally wanted that, will try vim-slamhound |
| 14:24 | osfameron | (still don't think the project description sells it well though ;-) |
| 14:26 | technomancy | it's not quite like Java's "tidy up imports" because the implementation is a brute-force recompile-all-the-things, so I felt a metaphor of violence was appropriate |
| 14:26 | Frozenlock | technomancy: refactor-nrepl did pretty well when I left it 'guess' namespaces. Granted it was always with the canned solutions (str -> clojure.string) or with namespaces I was already using elsewhere in the project. |
| 14:26 | TEttinger | dysfun: depends on what types you mean. I'm sure specs are better than some type systems. JS' for instance... |
| 14:27 | technomancy | Frozenlock: I'm more interested in the find-usages |
| 14:29 | technomancy | so yeah... this just doesn't work as documented |
| 14:29 | technomancy | too bad |
| 14:42 | lxsameer | which protocol is related to a derefable value ? |
| 14:50 | justin_smith | lxsameer: the easiest way to check is with supers |
| 14:51 | justin_smith | eg. (supers (class (future))) and (supers (class (atom nil))) |
| 14:51 | technomancy | boom; had lein refuse to download a transitive dependency that used a non-TLS repo which wasn't directly visible in project.clj |
| 14:51 | technomancy | woot |
| 14:51 | justin_smith | nice |
| 14:52 | technomancy | coincidentally nice that the first time this occurred I happened to be on coffee shop wifi |
| 14:52 | lxsameer | justin_smith: thanks a lot man |
| 14:54 | Frozenlock | technomancy: living dangerously |
| 14:55 | technomancy | Frozenlock: it would be dangerous, if I weren't using this new version of lein that refuses to do idiotic things for me =) |
| 14:55 | technomancy | idiotic things which every actually-released version of lein will happily go ahead and do =( |
| 14:58 | lxsameer | is a custom type which defined with deftype |
| 14:58 | lxsameer | mutable ? |
| 14:58 | justin_smith | types aren't mutable, but deftype fields can be |
| 14:59 | justin_smith | optionally |
| 14:59 | lxsameer | justin_smith: ow, thanks |
| 15:39 | ridcully_ | osfameron: it also tries to do :refer etc. e.g. write >! somewhere, slamhound adds async with a refer. or use str/join and it will require clojure.string as str etc |
| 15:49 | osfameron | ah, it has a catalogue of common functions? |
| 15:49 | ridcully_ | dunno. i'd guess it just looks around |
| 15:50 | ridcully_ | not sure, if it does the same thing for imports too |
| 16:10 | octarinemole | hello everyone! i have this strange question: why is (<= 0.1 1/10) true but all of (< 0.1 1/10), (= 0.1 1/10) and (> 0.1 1/10) are false? |
| 16:11 | Para_ | Clojure has fractional type support. |
| 16:11 | Para_ | Or rather, rational. |
| 16:11 | Para_ | ,(type (/ 1 3)) |
| 16:11 | clojurebot | clojure.lang.Ratio |
| 16:12 | octarinemole | ,(<= 0.1 1/10) |
| 16:12 | clojurebot | true |
| 16:12 | octarinemole | if that's true, shouldn't also one of the following be true? |
| 16:12 | Para_ | "is equal" of that part matches |
| 16:12 | octarinemole | ,(< 0.1 1/10) |
| 16:12 | clojurebot | false |
| 16:12 | octarinemole | ,(= 0.1 1/10) |
| 16:12 | clojurebot | false |
| 16:12 | Para_ | No. |
| 16:12 | Para_ | That's not how that works in any language :) |
| 16:13 | octarinemole | why is that? (sorry, it just looks mathematically pleasing...) |
| 16:13 | Para_ | It reads out loud "is equal OR smaller than" |
| 16:13 | Para_ | so it's (or (< 0.1 1/10) (= 0.1 1/10)) |
| 16:13 | Para_ | ,(or (< 0.1 1/10) (= 0.1 1/10)) |
| 16:13 | clojurebot | false |
| 16:13 | octarinemole | yes, so either (= 0.1 1/10) or (< 0.1 1/10) should be true as well, shouldn't it? |
| 16:14 | Para_ | ...well probably the other way around but anyhow |
| 16:14 | octarinemole | there you go, you get |
| 16:14 | ridcully_ | ,(== 0.1 1/10) |
| 16:14 | clojurebot | true |
| 16:14 | octarinemole | ,(= (<= 0.1 1/10) (or (< 0.1 1/10) (= 0.1 1/10))) |
| 16:14 | clojurebot | false |
| 16:14 | Para_ | ,(or (= 0.1 1/10) (< 0.1 1/10)) |
| 16:14 | clojurebot | false |
| 16:14 | Para_ | Hum. |
| 16:14 | Para_ | Well anyway. The <= is not decomposable. |
| 16:15 | Para_ | < and = are not the same as <= in a sense; it goes into land of type coersions |
| 16:15 | octarinemole | i see... |
| 16:15 | Para_ | Just read it out loud and don't think everything can be split into parts. |
| 16:15 | Para_ | You're not Aristotle, atoms have already been discovered :) |
| 16:15 | octarinemole | ok, well, thanks |
| 16:16 | TEttinger | ridcully_ had it |
| 16:16 | ridcully_ | ,(BigDecimal. 0.1) |
| 16:16 | clojurebot | 0.1000000000000000055511151231257827021181583404541015625M |
| 16:16 | Para_ | Yeah there's that. |
| 16:16 | octarinemole | that is why i chose 0.1 in the first place... |
| 16:16 | TEttinger | octarinemole: there's 0.1M for higher precision easily |
| 16:16 | octarinemole | i just expected some properties of the comparison operators which are apparently not there |
| 16:16 | TEttinger | but yeah == is mathematical equality |
| 16:17 | Para_ | Well it applies to all languages as well, if you care about fractions/ratios, make sure you work with compatible types at all points. |
| 16:17 | TEttinger | I believe <= is the or of < and == |
| 16:17 | technomancy | Para_: most languages don't have ratios |
| 16:17 | Para_ | ,(or (== 0.1 1/10) (< 0.1 1/10)) |
| 16:17 | clojurebot | true |
| 16:17 | Para_ | technomancy: That's their downfall :) |
| 16:17 | octarinemole | @TEttinger ok, thanks, i think that helps me a lot! |
| 16:17 | TEttinger | since <= is mathematical, not as general as = |
| 16:17 | technomancy | Para_: I would say that having decimal literals default to floats is the downfall |
| 16:18 | technomancy | it's much better to default to accuracy and opt-in to speed |
| 16:18 | Frozenlock | indeed |
| 16:19 | Frozenlock | Most people that first come to a computer won't have the reflex of saying "hmm, I wonder if the computer will lie to me and NOT register the number as I entered it." |
| 16:19 | octarinemole | :) |
| 16:19 | Para_ | technomancy: That's why Groovy defaults to BigDecimals with actual accurate precision, as well. |
| 16:19 | technomancy | Para_: huh; that's the first sensible thing I've heard about it =) |
| 16:19 | Para_ | Frozenlock: Well most people are really technically inept as well. |
| 16:19 | Para_ | "WHERE IS THE FACEBOOK BUTTON" |
| 16:20 | Frozenlock | Para_: right, so we shouldn't make it harder. |
| 16:20 | Para_ | They won't understand the difference or what it entails. |
| 16:20 | Para_ | So don't waste your tiime. |
| 16:34 | IamDrowsy | I just had a look at the Clojure source and actually (= 0.1 1/10) resulting to false has nothing todo with 0.1 not being exact |
| 16:34 | IamDrowsy | ,(= 1/2 0.5) |
| 16:34 | clojurebot | false |
| 16:35 | IamDrowsy | equal (=) check for the category of a number (Integer, Floating, Ratio, Decimal) and will always give false when it doesn't match |
| 16:36 | technomancy | IamDrowsy: depends how you define your terms; "inexact" is commonly used to describe types rather than values |
| 16:36 | ridcully_ | ,(= 1M 1) |
| 16:36 | clojurebot | false |
| 16:37 | justin_smith | (= 1N 1) |
| 16:37 | justin_smith | ,(= 1N 1) |
| 16:37 | clojurebot | true |
| 16:39 | octarinemole | ,(== 0 (- 0.1 1/10)) |
| 16:39 | clojurebot | true |
| 16:39 | octarinemole | ,(== 0 (- 0.1 (BigDecimal. 0.1))) |
| 16:39 | clojurebot | true |
| 16:39 | justin_smith | there's also zero? |
| 16:39 | ridcully_ | ,(= (BigInteger. "1") 1N) |
| 16:39 | clojurebot | true |
| 16:39 | octarinemole | these look a bit funny to me... it is like 0.1 can act as exactly one tenth, but also as "roughly" one tenth |
| 16:40 | justin_smith | ,(zero? (- 0.1 1/10)) |
| 16:40 | clojurebot | true |
| 16:40 | justin_smith | octarinemole: before you use a double in a calculation it's other because it's less precise, after you use it it is contagious, you get a double back |
| 16:41 | technomancy | justin_smith: oh man that last one |
| 16:41 | justin_smith | yup |
| 16:42 | octarinemole | so... in (- 0.1 1/10) I should get a non-zero double, shouldn't I? |
| 16:43 | ridcully_ | ,(+ 0.1 2/10) |
| 16:43 | clojurebot | 0.30000000000000004 |
| 16:44 | octarinemole | it casts the fraction to a double and then does the operation? |
| 16:46 | octarinemole | funny that |
| 16:46 | octarinemole | ,(class (- 0.1 (BigDecimal. 0.1)) |
| 16:46 | clojurebot | #<RuntimeException java.lang.RuntimeException: EOF while reading> |
| 16:47 | octarinemole | (class (- 0.1 (BigDecimal. 0.1))) |
| 16:47 | octarinemole | ,(class (- 0.1 (BigDecimal. 0.1))) |
| 16:47 | clojurebot | java.lang.Double |
| 16:47 | octarinemole | so it also downgrades the bigdecimal to a double when possible |
| 16:47 | dysfun | when it doesn't make sense to keep it as a rational, it will convert it to a double |
| 16:47 | octarinemole | ,(class (- 0.1M (BigDecimal. 0.1))) |
| 16:47 | clojurebot | java.math.BigDecimal |
| 16:48 | dysfun | and in this case, you gave it a double first |
| 16:48 | octarinemole | ok, i think i'm starting to understand it enough |
| 16:48 | octarinemole | ,(class (- (BigDecimal. 0.1) 0.1)) |
| 16:48 | clojurebot | java.lang.Double |
| 16:48 | ridcully_ | i think order has nothing to do? |
| 16:48 | dysfun | ah so it is |
| 16:49 | octarinemole | thanks everyone! |
| 16:49 | ridcully_ | it will use double once one of those is there, since it gives the most wrong result |
| 16:49 | octarinemole | :-D |
| 16:49 | dysfun | "most wrong" really depends on how much of it's being used |
| 16:49 | dysfun | i would have said the logical approach would be to do the opposite |
| 16:50 | technomancy | using a not-double would give the mistaken appearance of precision |
| 16:50 | ridcully_ | is there even a way to get a Ratio from a double? |
| 16:51 | dysfun | i think "mistaken" depends heavily on what you're doing |
| 16:52 | dysfun | i can envision several useful scenarious for the behaviour i descirbe |
| 16:52 | technomancy | without knowing about the context, using the less-exact representation is the only safe choice |
| 16:56 | dysfun | i don't know, it kind of offends me slightly because i've used so many languages that auto-upgrade to bignums |
| 17:00 | octarinemole | ridcully_: you can write a function that extracts the nominator and denominator from BigDecimal, then pass that to the Ratio constructor... maybe this should be built in? |
| 17:01 | dysfun | that doesn't do a rario from a double |
| 17:01 | technomancy | (->> (for [...] (future ...)) (map deref) doall) ; <- is that even ... I mean |
| 17:01 | technomancy | you're derefing every single future as it's constructed |
| 17:02 | technomancy | so... you're completely defeating the purpose of futures? |
| 17:02 | justin_smith | yeah, the doall should come first |
| 17:02 | dysfun | there are infinite solutions for fractions, so the best you can do is multiply it until you've eliminated the point to get the numerator and denominator |
| 17:02 | justin_smith | unless you only want to work with (chunk size) futures at a time? |
| 17:02 | technomancy | I always try to come up with an explanation that isn't "whoever wrote this was very confused" but ... |
| 17:02 | hiredman | it would make sense if the doall was before the map |
| 17:03 | justin_smith | technomancy: "I want either 1 or 32 futures to run at a time" |
| 17:03 | hiredman | lazy creating futures -> forcing the lazily created futures -> waiting for them all to compute |
| 17:04 | hiredman | that is almost certainly what they meant to do |
| 17:04 | technomancy | yeah... and chunking accidentally makes it work sorta the way they meant? |
| 17:04 | octarinemole | dysfun: well, the double has finite precision, so you can definitely represent it as a fraction that way, and the methods on BigDecimal can help a lot |
| 17:06 | justin_smith | oh man, that's their only tweet |
| 17:06 | justin_smith | and they just made another one... |
| 17:07 | technomancy | it's like being around for a birth. such a magical moment. |
| 17:07 | Frozenlock | with blood and shit everywhere |
| 17:07 | Frozenlock | magical |
| 17:09 | osfameron | what's the big hex string on their first tweet? |
| 17:20 | octarinemole | ridcully_: found it: |
| 17:20 | justin_smith | they're well typed Bröwer. |
| 17:20 | octarinemole | ,(clojure.lang.Numbers/toRatio (BigDecimal. 0.1)) |
| 17:21 | clojurebot | 1000000000000000055511151231257827021181583404541015625/10000000000000000000000000000000000000000000000000000000 |
| 17:21 | justin_smith | lol |
| 17:22 | octarinemole | and somewhat embarassingly... |
| 17:22 | octarinemole | ,(clojure.lang.Numbers/toRatio 0.1) |
| 17:22 | clojurebot | 0/1 |
| 17:22 | justin_smith | well, it's a ratio |
| 17:23 | octarinemole | true :) |
| 17:28 | justin_smith | ,(clojure.lang.Ratio. (biginteger 666) (biginteger 420)) |
| 17:28 | clojurebot | 666/420 |
| 17:29 | postpunkjustin | sick ratio |
| 17:30 | octarinemole | yeah, and then the equals method is a bit odd... |
| 17:32 | octarinemole | i don't think the constructor is meant to be invoked directly as it doesn't normalize the fraction... |
| 17:32 | justin_smith | yeah, I inferred the constructor wouldn't normalize when I saw the output for 0.1 |
| 17:33 | octarinemole | and then the equals method assumes they're normalized |
| 17:33 | octarinemole | ,(== 12/14 (clojure.lang.Ratio. (biginteger 6) (biginteger 7))) |
| 17:33 | clojurebot | true |
| 17:33 | octarinemole | ,(== 12/14 (clojure.lang.Ratio. (biginteger 12) (biginteger 14))) |
| 17:33 | clojurebot | false |
| 17:33 | octarinemole | so it's a no-no :) |
| 17:35 | lxsameer | how can I get a function by name with out requiring the namespace ? in runtime |
| 17:35 | justin_smith | resolve finds it, if any other code caused the code to be loaded at least |
| 17:36 | justin_smith | ,(resolve 'clojure.java.io/file) |
| 17:36 | clojurebot | #'clojure.java.io/file |
| 17:36 | justin_smith | that finds the var |
| 17:36 | justin_smith | you can call a var, or deref to get the actual thing out of it |
| 17:39 | lxsameer | justin_smith: but what if there were no file requiring that ns ? |
| 17:39 | technomancy | lxsameer: leiningen has a resolve-require function |
| 17:40 | technomancy | (defn resolve-require [x] (require (symbol (namespace x))) (resolve x)) |
| 17:40 | amalloy | lxsameer: why are you trying to do this? this is a weird thing to do |
| 17:40 | lxsameer | technomancy: hmmm is that going to work on production ? i mean in a jar |
| 17:40 | technomancy | lxsameer: yes, but it's better to have the call path known at compile-time if possible |
| 17:40 | lxsameer | amalloy: indeed it is. |
| 17:42 | lxsameer | amalloy: i'm trying to point to a function from an edn data |
| 17:42 | Frozenlock | lxsameer: is this to make plugins? |
| 17:42 | amalloy | how much do you trust the source of this edn data? if you let them call arbitrary functions, they can do pretty much anything |
| 17:43 | lxsameer | Frozenlock: not a plugin but maybe concepts would be the same |
| 17:43 | lxsameer | amalloy: it's a trusted source |
| 17:44 | justin_smith | so this is effectively DI via edn? |
| 17:45 | justin_smith | what if a consumer passed you a function instead? |
| 17:45 | lxsameer | justin_smith: some how yes |
| 17:45 | lxsameer | justin_smith: over wire ? |
| 17:45 | lxsameer | justin_smith: basically this happens on wire |
| 17:46 | justin_smith | OK - so they ask for some namespace, you might not have loaded it yet for whatever reason, but they trust you can find it if they ask for it? |
| 17:46 | lxsameer | justin_smith: both side has several functions but i want to conditionally specify the callback |
| 17:46 | lxsameer | justin_smith: yeah exactly |
| 17:47 | justin_smith | so it's a weird ad-hoc RPC thing |
| 17:47 | lxsameer | justin_smith: yes |
| 18:08 | technomancy | ad-hoc rpc isn't that weird |
| 19:12 | assoc-in | Has anyone called a jar from clojure? I have a jar for an external resource and it looks like I could clojure.java.shell to call it, but I am not sure how I would close it. |
| 19:24 | justin_smith | assoc-in: you could either invoke the jar's main from within your own vm, allowing you to access its api that hopefully has some way to shut it down or cancel it, or launch a shell script via shell/sh that runs it async and returns the pid (that you can later reference to kil it via shell ) |
| 19:24 | justin_smith | or, you can also use ProcessBuilder to create a Process, which has a method to shut it down (and has a lot of useful options that shell/sh doesn't) |
| 19:25 | justin_smith | there's a library called conch that wraps ProcessBuilder but I think using the real API is much less error prone |
| 19:54 | assoc-in | Okay I was able to get it to work with this. It looks like you need to call sh again. (shell/sh "sh" "-c" "java -jar ......") |
| 19:54 | justin_smith | shell/sh doesn't us sh (despite the name) |
| 21:08 | llsouder | https://pastebin.com/M8CAHhEG |
| 21:08 | llsouder | what is this argument? https://pastebin.com/M8CAHhEG |
| 21:08 | llsouder | doh! |
| 21:08 | llsouder | [{{username "username" password "password" next "next"} :params |
| 21:08 | llsouder | session :session :as req}] |
| 21:08 | llsouder | this is a vector with a map |
| 21:09 | llsouder | and the first key of the map is a map???? |
| 21:09 | llsouder | why cant my evil brain parse this?!?!? |
| 21:10 | llsouder | (defn do-login |
| 21:30 | llsouder | so while I was waiting I found the :as which told me it is destructuring |
| 21:31 | llsouder | so not the session :session makes sense to me. the name session is bound to the value of the :session key |
| 21:32 | llsouder | so now* |
| 21:32 | llsouder | that username stuff is still hurting my brains |
| 21:38 | llsouder | ok, that took a while but I got it. "username" is a key and binds to username etc |
| 21:39 | llsouder | yeah, thanks for letting me work that out LOL! Clojure documentation test.... passed! |
| 22:04 | assoc-in | justin_smith:Yes quite odd behavior. I think the documentation could be more clear about that |