2016-02-06
| 06:40 | benjyz1 | hello. I'm wrestling with SQL in Clojure |
| 06:40 | benjyz1 | jdbc/insert! for some reason doesn't commit to my database |
| 06:41 | benjyz1 | https://www.refheap.com/114533 |
| 06:42 | benjyz1 | do I need some kind of commit? |
| 06:45 | benjyz1 | ok, nevermind, just some weird error |
| 06:46 | mmeix | (makedb) etc is commented out |
| 06:46 | mmeix | ah .. ok |
| 09:29 | thomasfuston | Are there recommended ways to build GUI applictions with clojure? |
| 09:46 | dysfun | thomasfuston: if by that you mean 'non-browser applications', it's not recommendable |
| 09:46 | CaptainLex | Hehe I was going to say |
| 09:46 | thomasfuston | hmm so its better to have a browserinterface? even to a local app? |
| 09:47 | dysfun | no, it's better to write the app in not-clojure |
| 09:47 | thomasfuston | ok, you saying clojure is best for webdevelopement |
| 09:48 | dysfun | i'm saying clojure is ideal for anything where startup time is not a primary requirement |
| 09:48 | dysfun | and that don't require you to write a gui |
| 09:48 | dysfun | unless it's a web one |
| 09:49 | dysfun | the reason for the gui thing is frankly what java offers is shit and because you're on the jvm your interop with C libraries is piss-poor at best |
| 09:51 | thomasfuston | well i thought i could do a little app in clojure for learning purposes (coming from python), and it should be crossplattform so thought why not clojure |
| 09:51 | dysfun | in practice, if you want a crossplatform gui application, you use qt |
| 09:52 | dysfun | i think the java bindings are pretty outdated last i checked |
| 09:52 | dysfun | and it causes all sorts of deployment issues |
| 09:54 | dysfun | for the cross platform gui client i've got to build in the next few months, we're being slightly more ambitious, separating all the non-gui stuff out into a c++ library with a c wrapper. you can link that against all OSes including mobile |
| 09:54 | dysfun | of course currently i'm building the web client... in clojure+clojurescript |
| 09:55 | dysfun | the server, well that's a clojure webservice |
| 09:56 | dysfun | honestly i'm finding it easier to build for the web than build the clients, significantly so |
| 10:10 | CaptainLex | dysfun: What about using other Clojure targets, like PyClojure? Do those projects still have any momentum? |
| 10:31 | rhg135 | I'm building a small gui app in clojure. Clojure starts fast enough and when you need fs io, it's easier and faster than cljs |
| 10:32 | dysfun | CaptainLex: i'm not convinced they're going anywhere |
| 10:33 | dysfun | i've personally got an llvm-clojure project on the back burner |
| 10:33 | CaptainLex | dysfun: True, but for thomasfuston's purposes - building a GUI app just to get his hands dirty - it might be useful. |
| 10:34 | dysfun | or, y'know, he could use python, which is a more suited tool for his purpose and he happens to know |
| 11:18 | arkh | or, y'know, it could be a fun project to learn more clojure |
| 11:19 | ridcully | never |
| 12:41 | benjyz1 | hello. I'm working on a webapp with Clojure and Clojurescript. Ring delivers JSON with middleware, but I can't get the client to read it correctly |
| 12:44 | benjyz1 | https://www.refheap.com/114540 |
| 12:44 | justin_smith | benjyz1: the input to the handler won't be js, so js->cljs doesn't help |
| 12:45 | justin_smith | benjyz1: you need to use js/JSON to turn the string into js data |
| 12:45 | justin_smith | because it will just be a string |
| 12:45 | justin_smith | (or you can use whichever cljs wrapper for js/JSON you like of course) |
| 12:54 | benjyz1 | thanks. trying with "(js/JSON.parse response)" |
| 12:56 | justin_smith | yeah, that sounds right |
| 12:59 | benjyz1 | Unexpected string... when I try to parse {"foo" "bar"} |
| 12:59 | justin_smith | well that's not valid json |
| 12:59 | justin_smith | are you sending edn format? |
| 12:59 | justin_smith | in that case, you want cljs.reader/read-string or whatever it's called |
| 13:00 | justin_smith | or you could fix your middleware so you actually send json |
| 13:01 | benjyz1 | I'm trying (wrap-json-response) |
| 13:02 | justin_smith | OK, are you setting the content-type header? |
| 13:02 | justin_smith | you might need to make sure your accept header is right on the client side too, I forget |
| 13:06 | benjyz1 | I was looking around whether I can find a template which integrates ring and CLJS |
| 13:11 | macrolicious | does anyone know which rainbow delimiters mode works well in emacs -nw (in a terminall… iterm2 in my case)? |
| 13:11 | justin_smith | macrolicious: it should just be a question of adjusting the faces defined by rainbow-delimiters mode right? |
| 13:12 | macrolicious | ah ok… too noob to know that… sweet... |
| 13:13 | justin_smith | macrolicious: M-x customize-group<return>rainbow-delimeters<return> |
| 13:14 | justin_smith | then from there go to reainbow-delimiters-faces, and edit and preview to hearts content |
| 13:15 | justin_smith | macrolicious: it's also possible to customize these things outside of the customize interface, but customize is handy especially when new to emacs |
| 13:16 | macrolicious | that's really helpful… and I'll be able to pick the right faces... |
| 13:47 | kwladyka | What do you think about #_ in parameters (defn create-uuid! [uidable-id uidable-type type #_expired-in [n time-unit]]) <- is it good idea or should i use [exprired-in-n expired-in-time-unit] ? What is the standard? |
| 13:48 | kwladyka | i think send options is better |
| 13:48 | kwladyka | *second |
| 13:49 | kwladyka | maybe even like that (defn create-uuid! [uidable-id uidable-type type [expired-in_n expired-in_time-unit]] |
| 13:49 | justin_smith | why not (defn create-uuid! [uidable-id uidable-type type [n time-unit :as expired-in]] ...) |
| 13:50 | kwladyka | it is better, thanks! |
| 13:52 | djbkd | i'm starting Web Dev 2.0 book and can't seem to get through the first example...is ring broken on clj 1.7? |
| 13:52 | justin_smith | djbkd: the lein-ring plugin is broken with the latest lein |
| 13:53 | djbkd | ah ok |
| 13:53 | justin_smith | djbkd: if you "upgrade" to an older lein that fixes it |
| 13:53 | djbkd | hahaha |
| 13:53 | djbkd | so, like nodejs :c |
| 13:53 | djbkd | https://twitter.com/BadKittyDaddy/status/696041540896993280 |
| 13:53 | justin_smith | hopefully the next ring will fix this :) |
| 13:53 | justin_smith | djbkd: sad timing for the new lein breakage, with that great book coming out... |
| 13:54 | djbkd | yeah i really like clj as a language |
| 13:54 | djbkd | but such is the nature of open source |
| 13:54 | kwladyka | Anybody from Switzerland here? I am wonder how looks Clojure work there. |
| 13:54 | djbkd | i'll try downgrading, thanks |
| 14:02 | macrolicious | justin_smith: got my delimiters all looking pretty… took a bit to figure out the customization interface, but yay! |
| 14:02 | RedNifre | If the clojure and leiningen versions in the ubuntu repository are very outdated, should I just extract the clojure zip somewhere and put a link named "clojure" in my ~/bin? |
| 14:03 | justin_smith | RedNifre: you don't need a zip |
| 14:04 | justin_smith | RedNifre: lein is a shell file |
| 14:04 | RedNifre | oh, so if I have lein I'm all set? |
| 14:04 | justin_smith | RedNifre: if you are using lein, lein finds and uses whichever version of clojure your project config tells it to |
| 14:04 | justin_smith | so yeah, if using lein, just the script suffices |
| 14:04 | RedNifre | I don't have a project config yet, I'm starting with clojure right now :) |
| 14:05 | RedNifre | okay, sounds good, I'll see how far I get... |
| 14:05 | justin_smith | RedNifre: though I do like keeping the latest clojure jar around for fiddling (faster startup time / simpler tooling as long as I don't need deps) |
| 14:05 | justin_smith | RedNifre: right, lein has a default clojure it will use, but once you create a project the project needs to declare a clojure version |
| 14:06 | justin_smith | RedNifre: if not using a project and therefore not having any deps, yeah, just use clojure.jar, use rlwrap too that makes it nicer |
| 14:06 | justin_smith | but pretty early on you will see the value of lein if you decide to use any deps outside clojure itself and the default jvm stuff |
| 14:08 | RedNifre | I have no opinions yet since I'm just starting out. The tutorial introduces lein and since I see the value of cabal/npm/gradle I'll probably like lein anyway. |
| 14:08 | dysfun | lein is my benchmark for build tools now |
| 14:08 | justin_smith | RedNifre: it is much better than the default usage of any of those, but cabal used properly is probably lein's equal :) |
| 14:09 | dysfun | but noone uses cabal properly, that's why we have stack :) |
| 14:09 | justin_smith | (properly meaning, no sharing of deps implicitly between projects, all things sandboxed but artifacts of same version shared via a cache) |
| 14:09 | dysfun | you know, you'd probably quite like stack |
| 14:10 | justin_smith | I'm sure, I'm still a n99b at haskell though |
| 14:10 | justin_smith | odometer hasn't even rolled over to n00b yet |
| 14:10 | dysfun | right, well even more reason to use stack. it's the best way to get started these days |
| 14:10 | justin_smith | cool |
| 14:10 | RedNifre | Possible, I heard about it peripherily. I also heard something about some cabal nix crossover thingy, does stack work well with nixOS as well? |
| 14:11 | dysfun | and the truth is that once you grok the syntax, just tweak monads until the type works and stop worrying about it |
| 14:11 | justin_smith | RedNifre: leiningen does with jvm deps something very close to what nix does to OS level deps (isolated subsets that are independent but sharing a cache of immutable versions) |
| 14:11 | dysfun | i'm not a nixos user, but loads of hackage has nixos builds |
| 14:12 | dysfun | besides, i'm too busy solving problems with haskell to be a haskell programmer |
| 14:13 | RedNifre | Unfortunately I haven't used Haskell in about a year :/ |
| 14:14 | dysfun | justin_smith: https://github.com/jjl/ctaas/blob/master/src/Main.hs 53 lines in memory hashtable webservice |
| 14:14 | justin_smith | anyway, clojure's great, isn't that why we're all here? |
| 14:14 | RedNifre | maybe? :) |
| 14:14 | dysfun | haskell has a different useful niche |
| 14:15 | justin_smith | back when bytemyapp of "haskell from first principles" fame was a clojure programmer, we actually had to enforce a "no haskell before 7 pm" rule here |
| 14:15 | RedNifre | I'm currently working through various programming languages, Haskell is great but brutal, after that I tried node.js with coffeescript which was bad but highly productive. Now I'm looking at Clojure because it promises to be somewhat as tidy as Haskell, but also as productive as those messy languages. |
| 14:16 | dysfun | ah yeah, i pissed him off once by saying "fucking haskell programmers" |
| 14:16 | justin_smith | RedNifre: right, clojure is a great mix of tidy enough to be sane while pliable enough to do things quickly and refine later, imho |
| 14:16 | dysfun | clojure is my preferred tool when it's a good fit |
| 14:17 | RedNifre | That sounds a bit like a tautology. |
| 14:17 | justin_smith | dysfun: "x is my preferred too when it's a good fit" is a low information statement :) |
| 14:17 | dysfun | not really. it says "if clojure isn't disqualified, i'll pick it" |
| 14:18 | RedNifre | ah, you mean "if it isn't a bad fit"? |
| 14:18 | justin_smith | dysfun: oh, perhaps we understand "a good fit" differently |
| 14:18 | justin_smith | RedNifre: get out of my brain |
| 14:18 | justin_smith | bbl |
| 14:20 | RedNifre | dito |
| 14:25 | RedNifre | Oh no. What exactly happens when I do "lein run"? How much data does it download? |
| 14:26 | RedNifre | Ah dammit. The problem is that my internet connection is broken so I went to a place where there is internet, downloaded lein and a clojure zip and I'm now using my phone's exhausted connection. I guess lein now wants to download several MB of clojure, huh? |
| 14:26 | mmastic | I'm new but I think it would just get your dependencies and whatnot (which could even be the actual compiler). You can always just abort. |
| 14:27 | RedNifre | Well, I just started so I guess it's the compiler. It tried to download clojure 1.8.0, which failed because of networking issues. I have the clojure 1.8.0 zip file though. Can I somehow give that one to lein so it doesn't have to download it by itself? |
| 14:28 | justin_smith | RedNifre: I'd just use "rlwrap java -jar clojure-1.8.0.jar" for now |
| 14:28 | justin_smith | when you have a good connection, do the full deal |
| 14:28 | justin_smith | (with lein) |
| 14:28 | RedNifre | Hm, it says "if you are behind a proxy try setting the http environment variable". This might be the case because of my cellular provider. How do I set this variable and to what? |
| 14:28 | kwladyka | is any reason why "contains?" work only with one key...? |
| 14:29 | RedNifre | justin_smith what does that command do exactly? |
| 14:29 | justin_smith | RedNifre: starts a clojure repl |
| 14:30 | justin_smith | no other things available except the extensive libs offered by the vm itself |
| 14:30 | justin_smith | it's enough for a start, once you need deps, lein handles those nicely, but why fight to make lein work when your net is crap anyway? |
| 14:30 | lodin- | kwladyka: Huh? |
| 14:31 | justin_smith | lodin-: kwladyka wants (contains? m k1 k2 k3) |
| 14:31 | justin_smith | which doesn't work of course |
| 14:31 | kwladyka | lodin- exactly like justin_smith said |
| 14:31 | lodin- | And what would be true if m contained all keys? |
| 14:31 | justin_smith | I don't know why, I agree varargs would be nice |
| 14:31 | amalloy | justin_smith: i think varargs there would be bad |
| 14:32 | lodin- | s/what/that/ |
| 14:32 | amalloy | because there are two reasonable meanings: (every? #(contains? m %) ks), or (some #(contains? m %) ks) |
| 14:32 | lodin- | Precisely, what amalloy said. |
| 14:32 | justin_smith | amalloy: I hadn't considered some being expected |
| 14:33 | noncom|2 | is anyone aware of a simple way to align a dava/joda time by 15 min-intervals? i.e. i want any arbitrary number of minutes to align to the nearest upper 15-minute quant? |
| 14:33 | noncom|2 | *java/joda |
| 14:33 | RedNifre | (if (contains? food :peanuts :seafish) (dont-eat food)) |
| 14:35 | dysfun | obviously that's a too few parameters to if exception waiting to happen |
| 14:36 | lodin- | dysfun: Being sarcastic? |
| 14:37 | dysfun | well yes, but it is. |
| 14:37 | RedNifre | how so? |
| 14:37 | amalloy | dysfun: it's not |
| 14:37 | amalloy | ,(if true 1) |
| 14:37 | clojurebot | 1 |
| 14:38 | dysfun | maybe it's just clojurescript that complains |
| 14:38 | justin_smith | dysfun: no, cljs doesn't complain |
| 14:38 | justin_smith | dysfun: maybe you are thinking of haskell |
| 14:39 | RedNifre | :D |
| 14:39 | dysfun | i'm really not. i've just spent two days fixing clojure to work under cljs |
| 14:39 | RedNifre | ,(if false 1) |
| 14:39 | clojurebot | nil |
| 14:39 | lodin- | dysfun: Not that I would complain if it complained. The opposite, actually. |
| 14:39 | justin_smith | dysfun: I use single branch if all over my cljs code. This was a point of contention with another def (he didn't believe in ever using when), so I assure you cljs takes this without complaint. |
| 14:40 | justin_smith | *another dev |
| 14:40 | dysfun | well i can't explain the error messages i've received then |
| 14:40 | justin_smith | if he was a def I could have prevented him from going out of scope, but now we have to hire a guy |
| 14:40 | justin_smith | dysfun: perhaps it was too many args to if |
| 14:40 | dysfun | ah yeah, could be |
| 14:40 | RedNifre | What's a def and how does single branch if relate to not using when? |
| 14:41 | justin_smith | RedNifre: haha, I typod def for dev, as in coworker |
| 14:41 | dysfun | well a single branch if would act as a when, so the idea is not needed when |
| 14:41 | RedNifre | ah, missed the correction, now it makes sense :) |
| 14:41 | justin_smith | RedNifre: (when p? (f)) is the same as (if p? (f)), when is only different when it has more than one form |
| 14:41 | justin_smith | RedNifre: and my ex coworker hated when |
| 14:42 | dysfun | i quite like when |
| 14:42 | justin_smith | and would use single branch if instead, we argued about it |
| 14:43 | justin_smith | my argument for replacing single-branch if with when is that if I read when, I know I don't have to scan for a second form - I know all forms are used together if any are |
| 14:43 | justin_smith | so it's a reading comprehension gain |
| 14:43 | dysfun | agreed |
| 14:43 | RedNifre | I don't know enough about when to have an opinion on this yet. :) |
| 14:44 | dysfun | the truth is life is too short to really care |
| 14:44 | justin_smith | the opposing side is that "when is only for side effects since it accepts multiple forms" - I'll acknowledge reasonable devs can disagree on this, but the other side is wrong |
| 14:44 | RedNifre | horray, lein downloaded 1.8.0 and executed my example lein project and it outputted "Hello world!". |
| 14:44 | justin_smith | nice |
| 14:45 | dysfun | yeah, that's a good strategy for many tools |
| 14:45 | mmastic | Is there perhaps anyone here that came from Haskell? |
| 14:46 | dysfun | i came back to clojure after taking some time out to do haskell |
| 14:46 | justin_smith | RedNifre: once I had an error with some new deps while working in a cafe, so I deleted my m2 cache (where lein keeps all your deps) and tried to reset things. I have since learned that deleting the m2 cache is never actually needed except for the exact case I created then: the wifi in the cafe was giving me a small html file containing a click to agree and use the internet form instead of the jar files I was attempting to download |
| 14:47 | RedNifre | mmastic yes, me, sort of. |
| 14:47 | mmastic | Oh, I have and I'm trying to understand how API works here, especially with dynamic typing. Do you not check for types at all? I don't imagine it's idiomatic to assert the type of every single argument, right? |
| 14:47 | dysfun | justin_smith: well it would have failed its checksum then wouldn't it? |
| 14:47 | justin_smith | the result of this was a lot of things with names like "clojure.jar" and "ring-plugin.jar" etc. which all contained the same stupid snippet of html instead of clojure code :P |
| 14:47 | justin_smith | dysfun: hell, without a pom how would I get a checksum? the pom files contained html too |
| 14:47 | dysfun | mmastic: you tend to worry more about the shape of things than their type |
| 14:48 | dysfun | right, but it's never going to find the checksum matches, is it? |
| 14:48 | RedNifre | mmastic no idea, I'm on the first page of a clojure tutorial here. |
| 14:48 | justin_smith | mmastic: in practice we care much more about protocols and interfaces and multimethods. If something implements the right protocol or multimethod, the code should work |
| 14:48 | justin_smith | mmastic: usually this is implicit because we are using code whose primitives are all defined in terms of thse protocols and multimethods |
| 14:49 | RedNifre | Which brings me to REPL colors: The "brave" tutorial shows lein repl with some fancy colors which I don't see for some reason. Is there an easy fix (I probably don't need repl colors that badly but I wouldn't dismiss them if they were easy to enable). |
| 14:49 | justin_smith | but sometimes you get low level enough to need to define your own protocols / multimethods / interfaces, or extend them |
| 14:49 | mmastic | Oh I see, but is it idiomatic to use protocols and such? I figured it's preferred to keep it Lisp-y and only use protocols for Java interops. |
| 14:49 | dysfun | justin_smith: is that low level? |
| 14:49 | justin_smith | RedNifre: I am unaware of any actual repl color feature outside of maybe editor integration |
| 14:50 | justin_smith | mmastic: lists are a protocol |
| 14:50 | justin_smith | etc. |
| 14:50 | mmastic | dysfun: Are you referring to the collection abstractions? |
| 14:50 | dysfun | mmastic: protocols are useful for mimicking existing behaviour |
| 14:50 | justin_smith | mmastic: so it's lispy by using protocols :) |
| 14:50 | dysfun | yes |
| 14:50 | RedNifre | hm, maybe the tutorial just enabled syntax highlighting for every piece of clojure code which accidentally colored the repl listings there. |
| 14:50 | dysfun | it's basedo n what justin_smith is saying |
| 14:51 | RedNifre | Why is it called "-main" and not "main" or something like that? |
| 14:51 | mmastic | Yeah but it's not Lispy to define protocols for your data, right? it's all about maps, is that correct? |
| 14:51 | justin_smith | mmastic: the protocls and multimethods that most of clojure.core uses are extended very pervasively - you can get a seq from a string, an array, a list, a vector, a hash-map, .... |
| 14:52 | justin_smith | mmastic: so you don't need to type-check something you need a seq from - though seq will throw an error if it can't make a seq from the arg |
| 14:52 | dysfun | RedNifre: because main() is the java function. when you write :gen-class, it writes a wrapper main() function which sets up clojure and calls -main |
| 14:52 | FiredBall-0x71 | join ##astara prince |
| 14:52 | FiredBall-0x71 | http://www.pearltrees.com/pvpeliter/laptop-disini-bought-governor/id15409744#item167481741, , xWindow 10 ENTERPRISE , FREE CLASSIFIED OS FROM THE MOST HIGH HAS BEEN RELEASED , CLICK ON THE LINK THAT POP UP AND CLICK DOWNLOAD ... . DON'T FORGET TO JOIN ##Astara ... . |
| 14:52 | RedNifre | dysfun I see, thanks. |
| 14:52 | justin_smith | mmastic: lispy is that the only data structure you have is a list |
| 14:52 | lodin- | mmastic: I disagree with that. For pure containers, sure, no need to not to use a map, but for other values where you have invariants and such that you need to maintains I suggest defining your own interface (which may use a protocol). |
| 14:52 | justin_smith | mmastic: clojure extends that to "everything implements seq" (within reason) |
| 14:52 | justin_smith | via seq, we can treat everything as a list if needed |
| 14:53 | dysfun | when we say lispy in clojure, i think we mean more 'makes good use of builtins' |
| 14:53 | justin_smith | mmastic: and maps and lists are protocols, in clojure |
| 14:53 | justin_smith | so it's a false dichotomy |
| 14:53 | dysfun | "doesn't reinvent the wheel" perhaps |
| 14:54 | dysfun | or "knows when to reinvent the wheel" :) |
| 14:54 | justin_smith | mmastic: another way to put it is that in practice "everything is a list" and "every collection implements the protocol for lists" are pretty much the same when coding |
| 14:55 | mmastic | Ah I see. I'm pretty torn, I had figured they say that in Lisp/Clojure API is data and mean that. So if I have to enforce an invariant, map isn't a good choice anymore? |
| 14:55 | dysfun | yeah, for example you can treat a map as a list, it contains a vector of [key value] |
| 14:55 | RedNifre | Not sure if this is interesting to you clojure pros here, but I discovered this "parinfer" atom editor plugin which places parens based on indentation, which really helps people like me who aren't used to all these parens yet. |
| 14:55 | RedNifre | Basically, you indent the code like in python or haskell and the parens appear by themselves. |
| 14:56 | justin_smith | dysfun: mmastic: and that map -> ([k v] [k v]) transition maps directly to the idiom of alists in lisp |
| 14:56 | lodin- | mmastic: You can use a map, but you should probably not access the map directly, since the actual data representation may change. So provide functions to get the data that you want from it. |
| 14:56 | justin_smith | RedNifre: I look forward to using it when it's mature maybe, it looks like a cool concept |
| 14:56 | lodin- | mmastic: I can clarify that I think this mostly applies when you cross library boundaries. |
| 14:56 | justin_smith | lodin-: well, even "map" is a protocl, not a concrete data type |
| 14:57 | mmastic | And I assume a constructor function as-well, yeah? |
| 14:57 | justin_smith | mmastic: ? |
| 14:57 | RedNifre | Can anybody comment on emacs vs atom editor? I encountered atom first, but both editors seem to implement the same concept (powerful texteditor that you can customize with a simple language). |
| 14:58 | mmastic | justin_smith: Yup, I believe this is the case, however by map I just mean this kind of data-structure of pairs. |
| 14:58 | justin_smith | sure, right, and that's what that protocol means too |
| 14:58 | dysfun | if you like lisp, you'll like emacs. |
| 14:59 | dysfun | but the real advantage of emacs is that it's very fast to edit text when you've learned certain shortcuts |
| 14:59 | dysfun | and basically unusuable until you've learned the basics |
| 15:01 | mmastic | Thanks a lot for your help everyone; It's immensely appreciated. |
| 15:01 | RedNifre | Ah, so I reached the "when" part of the tutorial and unlike in my imagination, it is NOT like a switch statement; it's actually an else-less if, right? |
| 15:01 | justin_smith | right, and it accepts any number of forms |
| 15:01 | RedNifre | If "when" is an else-less if, why is the "else" part of an "if" optional? |
| 15:01 | justin_smith | so that must clarify the above quite a bit |
| 15:02 | justin_smith | RedNifre: "any number of forms" |
| 15:02 | dysfun | (when (foo) (bar)) ; returns result of (bar) |
| 15:02 | justin_smith | (when p? (fire missiles) (issue apology to mother) (say goodbye) (exit)) |
| 15:02 | justin_smith | each action is done in turn - wouldn't work in an if |
| 15:02 | RedNifre | Yeah, I think I understand that. I don't understand what you want to tell me by "any number of forms". |
| 15:02 | dysfun | (when (foo) (bar) (baz)) ; calls bar, then baz, returns (baz) |
| 15:03 | justin_smith | RedNifre: (f) is a form |
| 15:03 | RedNifre | Yes, I think I understand. And I could do (if bla (do a b c)), right? |
| 15:03 | justin_smith | sure, and that's what when is |
| 15:03 | justin_smith | when just looks nicer |
| 15:03 | RedNifre | Okay, I guess I agree with you then that when is nicer than an else-less if. But I don't understand why an else-less if is even allowed. |
| 15:03 | dysfun | these "not strictly necessary" things actually make clojure quite nice |
| 15:04 | RedNifre | Hm, I guess you can just comment out the "else" part, huh? |
| 15:04 | RedNifre | ...without rewriting it to a "when"... |
| 15:04 | justin_smith | RedNifre: no need for a comment... |
| 15:04 | justin_smith | ,(if true 1) |
| 15:04 | clojurebot | 1 |
| 15:05 | justin_smith | ,(if false 1) |
| 15:05 | clojurebot | nil |
| 15:05 | RedNifre | No, I meant if you had (if bla (println "fine") (println "not fine")) and you quickly wanted to change it to not print "not fine" then you could comment out that line without being forced to rewrite it to a "when". |
| 15:05 | justin_smith | I mean, I guess, sure |
| 15:05 | dysfun | not if you use a line-comment like most people, because the parens need to match |
| 15:06 | RedNifre | ah, right, I had java style parens in mind. |
| 15:06 | RedNifre | so when to use an else-less if? |
| 15:06 | dysfun | you could use #_ though |
| 15:06 | justin_smith | ,(#_#_#_#_#_#_#_#_#_ we have better things than end of line comments) |
| 15:06 | clojurebot | () |
| 15:06 | dysfun | lol |
| 15:06 | dysfun | personally i won't be using the else-less if now i know about it |
| 15:06 | justin_smith | please don't use #_ like that, with the sole exception of commenting a matching binding/value pair in let or a hash map with #_#_ |
| 15:07 | RedNifre | ,(#_ 1 2) |
| 15:07 | clojurebot | #error {\n :cause "java.lang.Long cannot be cast to clojure.lang.IFn"\n :via\n [{:type java.lang.ClassCastException\n :message "java.lang.Long cannot be cast to clojure.lang.IFn"\n :at [sandbox$eval97 invokeStatic "NO_SOURCE_FILE" 0]}]\n :trace\n [[sandbox$eval97 invokeStatic "NO_SOURCE_FILE" 0]\n [sandbox$eval97 invoke "NO_SOURCE_FILE" -1]\n [clojure.lang.Compiler eval "Compiler.java" 6927]... |
| 15:07 | justin_smith | RedNifre: that expands to (2) |
| 15:07 | RedNifre | thanks. |
| 15:07 | justin_smith | ,(#_#_ 1 2) |
| 15:07 | clojurebot | () |
| 15:07 | justin_smith | my chain of #_ was perhaps less random than it looked |
| 15:08 | dysfun | do you like showing off in front of newbs? |
| 15:08 | RedNifre | how does that work exactly? |
| 15:08 | justin_smith | RedNifre: #_ eats exactly one form |
| 15:08 | justin_smith | RedNifre: so N #_ eats N forms |
| 15:08 | kwladyka | "Boot is a build tool. That said it's task composition features only get to shine when multiple build steps are involved." <- is it not job for Continuous Integration or another tool? I mean shouldn't it be separated for lein + tool to deploy? |
| 15:08 | RedNifre | why can you type them without spaces between them? |
| 15:08 | justin_smith | kwladyka: boot is a replacement for lein |
| 15:09 | dysfun | RedNifre: because the reader is special |
| 15:09 | justin_smith | RedNifre: they are reader macros, they don't obey the rules regular symbols do |
| 15:09 | justin_smith | RedNifre: another way to put it is for the same reason you can interchange [ [ ] ] with [[]] |
| 15:10 | RedNifre | Can you change as many as you want or are they defined separately, i.e. #_ and #_#_ and #_#_#_ up to, say 20 or something? |
| 15:10 | justin_smith | both are using the readers lowest level rules about where forms begin and end |
| 15:10 | RedNifre | *chain, not change |
| 15:10 | dysfun | in theory, they're not limited. in practice, who knows? |
| 15:10 | dysfun | don't overuse them |
| 15:10 | justin_smith | RedNifre: there might be some stack overflow issue, I really shouldn't have even showed that silly #_#_#_#_#_ trick |
| 15:11 | dysfun | justin_smith: on a pushbackreader? |
| 15:11 | RedNifre | "same reason .. [[]]" I thought I can type [[]] because in clojure, square brackets work just like normal parens (unlike in other lisps where there are only regular parens) |
| 15:11 | justin_smith | RedNifre: same reason ( ( f ) ) is the same as ((f)) |
| 15:11 | justin_smith | RedNifre: these are all reader rules |
| 15:11 | dysfun | or because there are special rules for interpreting parens and square barackets |
| 15:12 | RedNifre | hm, I'll ignore #_ for now... |
| 15:12 | justin_smith | RedNifre: what I mean is that #_ being special is on the same level as () or [] or {} being special, none of these need spaces for parsing |
| 15:12 | dysfun | when we say lisp has 'no syntax', we mean it has as little as we can get away with |
| 15:12 | justin_smith | exactly |
| 15:13 | RedNifre | Heh, I actually CAN comment the whole "else" line in an "if" because parinfer fixes the missing paren. |
| 15:13 | dysfun | nice |
| 15:13 | justin_smith | well there you go |
| 15:13 | justin_smith | but #_ can be more elegant sometimes |
| 15:13 | dysfun | and it works in all editors |
| 15:14 | RedNifre | alright |
| 15:14 | justin_smith | I am going to go to the office and try to catch up on my day job where I write clojure now. Yay! |
| 15:14 | dysfun | also use sparingly |
| 15:15 | RedNifre | Now the tutorial introduced the "=" operator but didn't say much about it. Is it as strange as in Java (don't compare Strings that way! Integer autoboxing doesn't happen but it works for the first 127 Integers anyway!) or is it more like calling the "equals" method? |
| 15:15 | justin_smith | RedNifre: we don't have operators |
| 15:15 | justin_smith | = is a function |
| 15:16 | justin_smith | and it's more like the equals method |
| 15:16 | RedNifre | okay |
| 15:16 | justin_smith | ,(= () []) |
| 15:16 | RedNifre | so it's sane and I don't have to learn any special rules, huh? |
| 15:16 | clojurebot | true |
| 15:16 | justin_smith | RedNifre: is the above sane? |
| 15:16 | justin_smith | depending on your opinion of that, it may or may not be sane |
| 15:16 | justin_smith | ,(= 1.0 1) |
| 15:16 | clojurebot | false |
| 15:17 | RedNifre | hey! |
| 15:17 | RedNifre | Well, (= () []) to me would be undefined behaviour since I'm comparing two things with different type so I don't worry about it... on the other hand... |
| 15:17 | RedNifre | hmmmm... |
| 15:17 | dysfun | we say clojure data have value semantics - we always compare their values, not their references |
| 15:17 | justin_smith | structural equility for immutable things is pretty nice I think |
| 15:18 | justin_smith | dysfun: but haskell has that too, but it still wouldn't call two different types equal |
| 15:18 | justin_smith | which is where structural equality comes into play |
| 15:18 | justin_smith | structural equality is kind of cool but also weird but also useful |
| 15:19 | RedNifre | well, Haskell would compile (= 1.0 1) as (= 1.0 1.0) since the first number is obviously float so the second one must be float as well, otherwise you wouldn't compare the same type. Go won't compile it because you try to compare different types (no overloaded number literals there). Java would compare the numbers I think? |
| 15:19 | justin_smith | I still find (= () []) and (not= 1 1.0) a quirky combo of behaviors :) |
| 15:20 | kwladyka | justin_smith replacement? More like another tool not replacement? |
| 15:20 | dysfun | i quite like 'shape typing' |
| 15:20 | RedNifre | What's the justification for (= 1 1.0) being false? |
| 15:20 | justin_smith | kwladyka: boot is meant to replace lein - you can use one or the other, there's rarely need to use both, ideally never need to use both |
| 15:20 | dysfun | well for a start, floats are approximations of numbers :) |
| 15:20 | RedNifre | but in this case it approximates 1 perfectly. |
| 15:21 | neoncont_ | justin_smith: is (= ["structural equality" "isomorphism"])? |
| 15:21 | justin_smith | RedNifre: for another potentially confusing detail, we have == for numeric value equality that ignores type |
| 15:21 | kwladyka | justin_smith just not sure about idea of boot. Is not better to have lein + CI instead of boot? |
| 15:21 | kwladyka | i didn't use boot so i don't know details |
| 15:21 | dysfun | kwladyka: boot is like lein, but different |
| 15:21 | justin_smith | ,(== 1 1.0 3/3) |
| 15:21 | clojurebot | true |
| 15:21 | dysfun | since i started porting my stuff to support clojurescript, i find boot a better fit |
| 15:22 | RedNifre | okay, but can I use == on other things? |
| 15:22 | justin_smith | kwladyka: boot starts faster, has a cleaner implementation, and is imperative instead of declarative. Some people like these things. |
| 15:22 | dysfun | it doesn't start faster here |
| 15:22 | justin_smith | RedNifre: no, only Number instances |
| 15:22 | justin_smith | dysfun: perhaps I was misled |
| 15:22 | kwladyka | dysfun can you give more details why? |
| 15:22 | RedNifre | If (= 1 1.0) is false and (== 1 1.0) is true I would expect (= () []) to be false and (== () []) to be true. |
| 15:22 | dysfun | they're both slow here |
| 15:23 | justin_smith | RedNifre: that would be consistent but sadly is not the case |
| 15:23 | dysfun | kwladyka: because i get to have one watcher to update everything |
| 15:24 | kwladyka | dysfun watcher? |
| 15:24 | dysfun | for instance i have a task that watches for updates and then runs the clojure tests and then the clojurescript ones |
| 15:24 | justin_smith | RedNifre: I think the idea is that turning '(a b c) into '[a b c] loses no information, no matter what a b and c are |
| 15:24 | justin_smith | RedNifre: you can't say the same about number types |
| 15:24 | dysfun | a watcher is simply a builtin boot facility that watches for file changes |
| 15:24 | RedNifre | Well, I'm new to Clojure so I might not have the right perspective here but right now those seem like the worst equality rules I have ever seen (aside from javascript's 1 == "1") |
| 15:24 | kwladyka | dysfun and that is the point. Isn't it job for CI software like teamcity ? |
| 15:24 | kwladyka | dysfun oh in that way |
| 15:24 | dysfun | RedNifre: they're not a bad set really, but always ask what you want |
| 15:25 | kwladyka | dysfun if i know lein can do the same with cljs |
| 15:25 | dysfun | kwladyka: no, boot is like leiningen |
| 15:25 | dysfun | you do want to run your tests before you push them don't you? |
| 15:25 | justin_smith | RedNifre: as was discussed above with mmastic, we don't really care about types most of the time, we care about protocols. Structural equality works for structures that implement the right protocols to be compared as equivalent. |
| 15:25 | justin_smith | RedNifre: also, = works between numeric subtypes that agree on the exact / lossy divide |
| 15:25 | justin_smith | ,(= 1 3/3) |
| 15:25 | clojurebot | true |
| 15:25 | justin_smith | ,(= 1 1N) |
| 15:25 | clojurebot | true |
| 15:25 | RedNifre | wat |
| 15:26 | justin_smith | ,(= (float 1.0) (double 1.0)) |
| 15:26 | clojurebot | true |
| 15:26 | kwladyka | dysfun yes but not after any change in files during editing |
| 15:26 | RedNifre | ,3/3 |
| 15:26 | clojurebot | 1 |
| 15:26 | RedNifre | ,1/3 |
| 15:26 | clojurebot | 1/3 |
| 15:26 | dysfun | well maybe you don't want that |
| 15:26 | justin_smith | RedNifre: it's a syntax for creating rationals, rationals autopromote to longs as applicable |
| 15:26 | dysfun | i find it really useful |
| 15:26 | RedNifre | wat |
| 15:26 | dysfun | i like real time feedback |
| 15:26 | RedNifre | why do rationals autopromote to longs but floats don't? |
| 15:27 | kwladyka | dysfun if it is very small app it could be ok |
| 15:27 | justin_smith | RedNifre: rationals are exact |
| 15:27 | justin_smith | RedNifre: floats are lossy |
| 15:27 | justin_smith | ,(+ 1/3 2/3) |
| 15:27 | clojurebot | 1N |
| 15:27 | dysfun | kwladyka: i don't understand why must it be small? |
| 15:27 | RedNifre | ,1N |
| 15:27 | clojurebot | 1N |
| 15:27 | kwladyka | dysfun what if tests take 30 second and run after any change in any files? |
| 15:27 | justin_smith | RedNifre: are you familiar with the pitfalls of ieee floating point? eg. why you should hardly ever compare them for equality? |
| 15:27 | RedNifre | Now hang on, if 1N doesn't turn into 1, why is (= 1 1N) true? |
| 15:27 | kwladyka | it will be iritating |
| 15:27 | dysfun | if your tests take 30 seconds, you're doing them wrong |
| 15:28 | luma | ,(+ 0.1 0.2) |
| 15:28 | clojurebot | 0.30000000000000004 |
| 15:28 | RedNifre | yes, I'm aware of that. |
| 15:28 | dysfun | most of mine run basically instantaneously |
| 15:28 | justin_smith | RedNifre: because they are on the same side of the exact/inexact divide |
| 15:28 | kwladyka | dysfun i can't agree with that :) |
| 15:28 | justin_smith | RedNifre: there is no lossless translation across that divide |
| 15:28 | kwladyka | dysfun because it is simple app |
| 15:28 | dysfun | mostly i develop libraries actually |
| 15:28 | justin_smith | RedNifre: but we treat two numbers on the same side as equal |
| 15:28 | RedNifre | well, there is sometimes. So I guess clojure is being consistent by pretending that you can never translate lossless? |
| 15:29 | dysfun | obviously if you have to have a database etc. it will take longer |
| 15:29 | kwladyka | dysfun if you count more complex algorithm or do more complex things it takes a time |
| 15:29 | RedNifre | I mean, I can understand that (= 1 (* 10 (/ 1.0 10.0))) would be false because of floating point inaccuracies. |
| 15:29 | dysfun | if you're doing machine learning, obviously you're not going to have it update in real time |
| 15:29 | justin_smith | ,(= 1/2 0.5M) ; maybe this is wrong though, this totally could be treated as equal |
| 15:29 | clojurebot | false |
| 15:30 | RedNifre | what's M again? |
| 15:30 | RedNifre | ...and what's N? |
| 15:30 | kwladyka | i am going watch the movie. Thank you for discussion. I will be back after 2 hours :) |
| 15:30 | justin_smith | ,(type 0.5M) |
| 15:30 | clojurebot | java.math.BigDecimal |
| 15:30 | justin_smith | ,(type 1N) |
| 15:30 | clojurebot | clojure.lang.BigInt |
| 15:30 | RedNifre | aha. |
| 15:30 | justin_smith | both are lossless numbers |
| 15:30 | RedNifre | okay, so since floats are binary and BigDecimals are decimals I can see that they can't be compared for equality because that might be lossless. |
| 15:31 | dysfun | the takeaway from all of this is : think before you write a comparison |
| 15:31 | RedNifre | wait, BigDecimal can't be lossless. |
| 15:31 | justin_smith | RedNifre: not just decimals, auto-promoting so that they don't lose data (up to some limit) |
| 15:31 | justin_smith | right, there is actually a limit |
| 15:31 | dysfun | they definitely at that range |
| 15:32 | justin_smith | RedNifre: instead of losing precision, bigdecimal will just fall over |
| 15:32 | justin_smith | ,(/ 1.0M 2.0M) |
| 15:32 | clojurebot | 0.5M |
| 15:32 | RedNifre | Okay, so the way I understand it is this: If a comparison could lead to a wrong result because of lossy representation the comparison will always be false even if it would actually be a precise comparison in the concrete case. |
| 15:32 | justin_smith | ,(/ 1.0M 5.0M) |
| 15:32 | clojurebot | 0.2M |
| 15:32 | justin_smith | hmm... |
| 15:32 | RedNifre | ,(/ 1.0M 3.0M) |
| 15:32 | justin_smith | ,(/ 1.0M 3.0M) |
| 15:32 | clojurebot | #error {\n :cause "Non-terminating decimal expansion; no exact representable decimal result."\n :via\n [{:type java.lang.ArithmeticException\n :message "Non-terminating decimal expansion; no exact representable decimal result."\n :at [java.math.BigDecimal divide "BigDecimal.java" 1616]}]\n :trace\n [[java.math.BigDecimal divide "BigDecimal.java" 1616]\n [clojure.lang.Numbers$BigDecimalOps div... |
| 15:32 | clojurebot | #error {\n :cause "Non-terminating decimal expansion; no exact representable decimal result."\n :via\n [{:type java.lang.ArithmeticException\n :message "Non-terminating decimal expansion; no exact representable decimal result."\n :at [java.math.BigDecimal divide "BigDecimal.java" 1616]}]\n :trace\n [[java.math.BigDecimal divide "BigDecimal.java" 1616]\n [clojure.lang.Numbers$BigDecimalOps div... |
| 15:32 | justin_smith | right |
| 15:33 | justin_smith | RedNifre: that sounds about right, yeah |
| 15:33 | RedNifre | That's funny, so BigDecimals avoids being lossy by crashing when it would get lossy? |
| 15:34 | RedNifre | ,(* 10.0 (/ 1.0 10.0)) |
| 15:34 | clojurebot | 1.0 |
| 15:34 | dysfun | if you're hitting the limit, you weren't checking your inputs sufficiently |
| 15:34 | RedNifre | ,(/ 1.0 10.0) |
| 15:34 | clojurebot | 0.1 |
| 15:35 | RedNifre | Is "1.0" a float or a BigDecimal? |
| 15:35 | justin_smith | ,(* 3 1/3) |
| 15:35 | clojurebot | 1N |
| 15:35 | justin_smith | 1.0 is double |
| 15:35 | dysfun | well it's a string, but without the quotes it would be a double |
| 15:35 | justin_smith | I guess float prints that way too, but clojure strongly prefers doubles |
| 15:35 | dysfun | there's little reason to prefer floats |
| 15:36 | RedNifre | wasn't 0.1 rounded to a double something like 0.1000000000000000000004 ? |
| 15:36 | justin_smith | yeah, I think that's some kind of printing rule |
| 15:36 | dysfun | there isn't much reason to scrimp on integers these days tbh |
| 15:36 | RedNifre | ,(= 10.0 (* 10.0 (/ 1.0 10.0))) |
| 15:36 | clojurebot | false |
| 15:36 | dysfun | (unless you're abusing pointer packing, in which case i salute you) |
| 15:36 | RedNifre | ,(= 1.0 (* 10.0 (/ 1.0 10.0))) |
| 15:36 | clojurebot | true |
| 15:37 | RedNifre | Maybe I'm thinking about some other binary floating point issue, but I thought 1 / 10 * 10 wasn't 1 in IEEE? |
| 15:37 | dysfun | you shouldn't be testing floats for equality anyway |
| 15:38 | justin_smith | ,(format "%20.420f" 0.1) |
| 15:38 | clojurebot | "0.1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000... |
| 15:38 | justin_smith | sorry |
| 15:39 | RedNifre | dysfun I know, but I thought the reason was that (= 1.0 (* 10.0 (/ 1.0 10.0))) would be false because of float inaccuracies, so I'm surprised that it works. |
| 15:40 | RedNifre | I also think that clojurebot's output has more precision than a double. |
| 15:40 | dysfun | oh, it's one of those things where it often does, but you shouldn't rely on it |
| 15:40 | dysfun | that makes for very difficult to diagnose bugs |
| 15:40 | justin_smith | RedNifre: I asked for a specific number of zeros |
| 15:40 | dysfun | anyway, hometime |
| 15:41 | RedNifre | justin_smith can you elaborate? |
| 15:42 | RedNifre | what exactly did clojurebot output there? Was it a double? |
| 15:42 | justin_smith | RedNifre: I ran ,(format "%20.420f" 0.1) which asks for a specific number of digits, padded with zeros even after precision runs out |
| 15:42 | justin_smith | RedNifre: it outputted a string, as format returns string |
| 15:42 | justin_smith | "outputted" I R SMRT |
| 15:45 | RedNifre | Well, my internet connection is too slow to google the precision of JVM doubles but if it's 64 bit then I don't understand how clojurebot can output something so close to 0.1 from a double. |
| 15:47 | RedNifre | Another thing, does "==" compare exactly, or does it compare within the precision of IEEE? |
| 15:47 | justin_smith | (Double/mulp 0.1) |
| 15:47 | justin_smith | ,(Double/mulp 0.1) |
| 15:47 | clojurebot | #error {\n :cause "No matching method: mulp"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.IllegalArgumentException: No matching method: mulp, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 6875]}\n {:type java.lang.IllegalArgumentException\n :message "No matching method: mulp"\n :at [clojure.lang.Compiler$StaticMet... |
| 15:47 | justin_smith | err... |
| 15:47 | RedNifre | ,(Double/ulp 0.1) |
| 15:47 | clojurebot | #error {\n :cause "No matching method: ulp"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.IllegalArgumentException: No matching method: ulp, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 6875]}\n {:type java.lang.IllegalArgumentException\n :message "No matching method: ulp"\n :at [clojure.lang.Compiler$StaticMethod... |
| 15:47 | RedNifre | worth a shot... |
| 15:48 | justin_smith | ,(Math/ulp 0.1) |
| 15:48 | clojurebot | 1.3877787807814457E-17 |
| 15:48 | RedNifre | right, so it's 17 decimal digits. But clojureput output way more than 17 zeroes, so how does that work? |
| 15:48 | justin_smith | ,(-> 0.1 (- 1.3877787807814457E-17) (+ 1.3877787807814457E-17)) |
| 15:48 | clojurebot | 0.1 |
| 15:48 | justin_smith | RedNifre: like I said I used format, format says "give me this many zeros on a string" |
| 15:49 | justin_smith | clojurebot gave me a string, not a double |
| 15:49 | RedNifre | ah, you mean it first "rounds" it to the string "0.1" and then just adds zeros for fun? |
| 15:49 | justin_smith | RedNifre: I was hoping using a long format would expose precision, but we got 0.1 back anyway |
| 15:50 | justin_smith | so yeah, pretty much |
| 15:50 | justin_smith | not for fun, because my format string said add that many zeros |
| 15:50 | RedNifre | okay, so how CAN you expose precision? |
| 15:51 | justin_smith | looking at the ulp? getting the raw bits and the implementation rules and doing the math? |
| 15:51 | justin_smith | not sure |
| 15:51 | justin_smith | actually going this time, be back soon when I get to the office |
| 15:53 | rotcev | hi, say i have a list in the form of ((+ 1) (+ 2) (- 3)), what is the best way to get it into the form (+ 1 (+ 2 (- 3))), ive been trying for a while but cant seem to come up with a nice way |
| 15:54 | RedNifre | hm, maybe reverse it and reduce it with something like apply or invoke or -> or ->>? |
| 15:56 | rotcev | RedNifre: at the moment im doing http://hastebin.com/uverajaziw.lisp with input of (infix (1 + 2 + 3 - 5)) which results in ((+ 1) (+ 2) (- 3) (5)) |
| 15:56 | RedNifre | hang on, I'm new to this so I need a moment to think :) |
| 15:56 | rotcev | which isnt even ideal tbh cos ideally the 5 would be paired in with the 3 but im kinda new to clojure so i dont know how to work partition to the way i need it (unless i should go about it a diff way) |
| 15:56 | RedNifre | your link gives me an application error. |
| 15:56 | RedNifre | but I'm also on a bad connection. |
| 15:57 | rotcev | it just worked for me site just went down lol ill pastebin it |
| 15:57 | rotcev | RedNifre: http://pastebin.com/sibdb4Ci |
| 15:58 | RedNifre | Hm, looks like (reduce -> ... doesn't work because "->" is a macro... |
| 15:59 | amalloy | i don't think you can really transform your list in that way efficiently. it's not too hard to do inefficiently |
| 16:00 | rotcev | my ideal output would be (+ 1 (+ 2 (- 3 5))) so i could just use eval on it |
| 16:01 | rotcev | er not even, itd be ( + 1 ( + 2 ( - 3 5))) or something no need to eliminate spaces |
| 16:02 | rotcev | i was thinking itd be cool if i could somehow treat each list as a partial function |
| 16:02 | rotcev | is there some way to do that in clojure? (in this specific case) |
| 16:03 | RedNifre | Is there something like hoogle for clojure? |
| 16:04 | rotcev | clojuredocs |
| 16:06 | mgaare | rotcev: do you need to consider order of operations? in other words, will you ever see multiplication, division, more parens, etc? |
| 16:06 | amalloy | RedNifre: nothing nearly as good |
| 16:06 | RedNifre | well, how do I append a form to a list? |
| 16:07 | RedNifre | I can only think about insane ways involving cons and reverse. |
| 16:07 | rotcev | mgaare: probably but i was thinking it would just be the order of the expressions in reverse for operator precedence |
| 16:08 | rotcev | and since i was just gonna use eval to read it i kinda assumed it would end up magicly working in the end lol |
| 16:09 | RedNifre | ah, I'm making progress here... let me think this through... |
| 16:12 | rotcev | i think im making progress too ;o |
| 16:12 | amalloy | why are you trying to munge this list around into a form that's suitable to eval? using eval is rarely a good answer, and getting it into shape for eval is harder than just evaluating it yourself with reduce |
| 16:14 | rotcev | true i just wasnt thinking correctly lol |
| 16:14 | amalloy | ,(let [ops {'+ + '- -}] (reduce (fn [acc [op x]] ((ops op) acc x)) 0 '((+ 1) (+ 2) (- 3)))) |
| 16:14 | clojurebot | 0 |
| 16:16 | RedNifre | ,'(1 2) |
| 16:16 | clojurebot | (1 2) |
| 16:16 | RedNifre | ,'('(1 2)) |
| 16:16 | clojurebot | ((quote (1 2))) |
| 16:16 | RedNifre | What's up whith that "quote"? |
| 16:16 | RedNifre | Why isn't it ((1 2)) ? |
| 16:17 | RedNifre | ...ooooh |
| 16:17 | RedNifre | ,'((1 2)) |
| 16:17 | clojurebot | ((1 2)) |
| 16:17 | amalloy | that's how quoting works. it's not just the "make a list" operator |
| 16:17 | RedNifre | Ah, that solves everything. |
| 16:17 | amalloy | '('(1 2)) is shorthand for (quote (quote (1 2))) |
| 16:18 | RedNifre | rotcev I think I got it! |
| 16:18 | rotcev | :o |
| 16:18 | RedNifre | ...though my solution is probably a bit crazy. |
| 16:18 | RedNifre | ,(defn append [x,l] (reverse (cons x (reverse l)))) |
| 16:18 | clojurebot | #'sandbox/append |
| 16:19 | RedNifre | (reduce append (reverse '((+ 1) (+ 2) (- 3)))) |
| 16:19 | rotcev | the reverses rofl |
| 16:19 | RedNifre | ,(reduce append (reverse '((+ 1) (+ 2) (- 3)))) |
| 16:19 | clojurebot | (+ 1 (+ 2 (- 3))) |
| 16:19 | RedNifre | tadaa! |
| 16:20 | rotcev | nice |
| 16:20 | RedNifre | yeah, I guess there is already something like that append function in clojure, but I coludn't find it. |
| 16:20 | RedNifre | ,(append (+ 1) (- 2)) |
| 16:20 | clojurebot | #error {\n :cause "Don't know how to create ISeq from: java.lang.Long"\n :via\n [{:type java.lang.IllegalArgumentException\n :message "Don't know how to create ISeq from: java.lang.Long"\n :at [clojure.lang.RT seqFrom "RT.java" 542]}]\n :trace\n [[clojure.lang.RT seqFrom "RT.java" 542]\n [clojure.lang.RT seq "RT.java" 523]\n [clojure.core$seq__4357 invokeStatic "core.clj" 137]\n [clojure.co... |
| 16:20 | RedNifre | ,(append '(+ 1) '(- 2)) |
| 16:20 | clojurebot | (- 2 (+ 1)) |
| 16:21 | mgaare | RedNifre: could also use concat, like |
| 16:21 | RedNifre | I tried concat, but that unpacket the second list too. |
| 16:22 | mgaare | ,(defn append-cat [x xs] (concat xs (list x))) |
| 16:22 | clojurebot | #'sandbox/append-cat |
| 16:22 | amalloy | RedNifre: http://stackoverflow.com/q/5734435/625403 |
| 16:22 | mgaare | ,(append-cat '(+ 1) '((+ 2) (- 3))) |
| 16:22 | clojurebot | ((+ 2) (- 3) (+ 1)) |
| 16:23 | RedNifre | mgaare oh, so you wrap the list in a list to counteract concat's flattening? |
| 16:23 | mgaare | no, you wrap the single thing you're trying to append in a list |
| 16:24 | mgaare | although amalloy was kind enough to point out some shortcomings in that approach in that link ;) |
| 16:33 | rotcev | thanks for the guidance anyways guys i think i know another way to solve it which im trying to implement now |
| 16:43 | RedNifre | connection broke down |
| 16:48 | justin_smith | RedNifre: the standard way to append to a list is (concat l [e]), but if you know you will be doing only appends, it's easier to just make a vector and conj |
| 16:48 | justin_smith | oops, scrollback |
| 16:48 | justin_smith | maybe still relevant though... |
| 16:48 | justin_smith | ,(defn append' [l & args] (concat l args)) |
| 16:48 | clojurebot | #'sandbox/append' |
| 16:48 | RedNifre | yeah, you missed my first clojure solution for somebody else's problem. |
| 16:49 | justin_smith | ,(append' '(:a :b :c) :d :e :f) |
| 16:49 | clojurebot | (:a :b :c :d :e ...) |
| 16:49 | RedNifre | ,(append' '(+ 2) '(- 3)) |
| 16:49 | clojurebot | (+ 2 (- 3)) |
| 16:50 | RedNifre | ah, it's backwards compared to mine. |
| 16:50 | RedNifre | I still have the Haskell convention in my head: First the special things, then the large data structures to operate on. |
| 16:51 | RedNifre | How would you solve the original problem with your append? |
| 16:51 | justin_smith | RedNifre: (reduce append' ...) |
| 16:51 | justin_smith | RedNifre: instead of reversing etc. |
| 16:52 | RedNifre | I'm now at the macro part of the tutorial. Could everything that can be done with macros also be done with regular functions and quoted source code? |
| 16:52 | justin_smith | RedNifre: if you are allowed to use eval, yes |
| 16:52 | RedNifre | ,(reduce append' '((+ 1) (+ 2) (- 3))) |
| 16:52 | clojurebot | (+ 1 (+ 2) (- 3)) |
| 16:52 | justin_smith | otherwise other macros will trip you up sometimes |
| 16:53 | justin_smith | RedNifre: that's not right is it |
| 16:53 | RedNifre | not quite, huh? |
| 16:53 | RedNifre | Is there a reduce-left? |
| 16:53 | justin_smith | ,(defn put-in [[a b] el] [a el b]) |
| 16:53 | clojurebot | #'sandbox/put-in |
| 16:53 | RedNifre | ah, so macros are better in that case because they expand at compile time so you don't need eval? |
| 16:54 | justin_smith | ,(reduce put-in '((+ 1) (+ 2) (- 3))) |
| 16:54 | clojurebot | [+ (- 3) (+ 2)] |
| 16:54 | justin_smith | still not it |
| 16:54 | justin_smith | yeah I guess the reversing is needed regardless |
| 16:55 | RedNifre | I think you have to reduce/fold from the right, otherwise you have to drill into your nested lists with different depth each step. |
| 16:55 | justin_smith | right |
| 16:56 | justin_smith | ,(reduce put-in (reverse '((+ 1) (+ 2) (- 3)))) |
| 16:56 | clojurebot | [- (+ 1) (+ 2)] |
| 16:56 | justin_smith | haha, well |
| 16:57 | justin_smith | right, so your answer was the one then |
| 16:57 | RedNifre | wait, where did the 3 go? |
| 16:57 | RedNifre | ,'[(1 2)] |
| 16:57 | clojurebot | [(1 2)] |
| 16:57 | justin_smith | RedNifre: disappeared in a destructure that made a bad assumption |
| 16:57 | justin_smith | ,(let [[a b] [1 2 3]] [a b]) |
| 16:57 | clojurebot | [1 2] |
| 17:00 | RedNifre | I'm now reading about ' quoting and ` syntax quoting. So I can interpolate with ~ in ` but not in '. Why does ~ not work in '? |
| 17:00 | justin_smith | because it's a special syntax that only exists inside ` |
| 17:00 | justin_smith | just like you can only catch inside try |
| 17:02 | justin_smith | ,'```(do :a) |
| 17:02 | clojurebot | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/seq)) (clojure.core/list (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/concat)) (clojure.core/list (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/list)) (clojure.core/list (clojure.core/seq #))))) (clojure.core/list (clojure.core/seq (clojure.core/concat (clo... |
| 17:03 | RedNifre | ,(inc 1) |
| 17:03 | clojurebot | 2 |
| 17:04 | RedNifre | Well, would there be a downside to '(1 ~(inc 1)) producing (1 2) ? |
| 17:04 | justin_smith | RedNifre: it would mean ' only exists in order to create crappy name capture that causes bugs |
| 17:05 | lodin- | RedNifre: I guess the question is why not use `(1 ~(inc 1)). |
| 17:05 | justin_smith | as it is, name capture isn't an issue (at least less trivially an issue) because ' does not interpolate |
| 17:05 | RedNifre | But then my question would be why not always use ` instead of '? |
| 17:06 | justin_smith | RedNifre: ` does interpolation and also namespace qualifying |
| 17:06 | justin_smith | it's much more complicated |
| 17:06 | lodin- | RedNifre: Because `x resolves x, while 'x is a literal symbol. |
| 17:06 | justin_smith | ,'`(do :a) ; compare this to the next |
| 17:06 | clojurebot | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote do)) (clojure.core/list :a))) |
| 17:06 | justin_smith | ,''(do :a) |
| 17:06 | clojurebot | (quote (do :a)) |
| 17:07 | lodin- | RedNifre: Maybe resolves is not right. justin_smith said it better. |
| 17:07 | RedNifre | ,'x |
| 17:07 | clojurebot | x |
| 17:07 | RedNifre | ,`x |
| 17:07 | clojurebot | sandbox/x |
| 17:07 | lodin- | ,'(let [x 1]) |
| 17:07 | clojurebot | (let [x 1]) |
| 17:07 | lodin- | ,`(let [x 1]) |
| 17:07 | clojurebot | (clojure.core/let [sandbox/x 1]) |
| 17:07 | lodin- | The second will give you an error. |
| 17:08 | RedNifre | ah, so quoting is more like source code whose meaning depends on where it gets executed while syntax quoting actually works out precisely what each form means? |
| 17:09 | justin_smith | RedNifre: somewhat - there's a history of buggy macros that "capture" surrounding values |
| 17:09 | justin_smith | going way back into LISP times |
| 17:09 | justin_smith | syntax quoting, which resolves things at macro definition, is a way of preventing these bugs |
| 17:10 | RedNifre | It's all a bit muddy to me, I still don't understand why I wouldn't just use syntax quoting always. Why would the second one of lodin-'s example give me an error? |
| 17:10 | justin_smith | RedNifre: you can't use a fully qualified symbol as a local binding |
| 17:11 | justin_smith | the reason ` turns it into a fully qualified symbol is to keep it from shadowing local bindings |
| 17:11 | lodin- | RedNifre: The thing is, you would not want to get (clojure.core/let [x 1]) either, because then you (might) shadow an x. |
| 17:12 | justin_smith | imagine you have (defn foo [a] (frob (+ a a))) and then (defmacro frob [form] `(let [a "dog"] (~form))) and a inside the macro was not modified |
| 17:12 | justin_smith | the macro would end up compiling to (+ "dog" "dog") |
| 17:12 | justin_smith | because it captures a symbol |
| 17:13 | justin_smith | the original a is inaccessible in the macro, since the name is shadowed |
| 17:13 | justin_smith | I think there's more than one mistake in that example, but I hope you get the gist of it |
| 17:14 | RedNifre | I'm not sure, the macro looks like you want to shadow a? Or what is (let [a "dog"]... meant to achieve? |
| 17:15 | justin_smith | RedNifre: the example is bad and it should feel bad, imagine a was somehow useful in that macro. |
| 17:16 | RedNifre | Well, I see how a gets shadowed but I don't understand the implications. |
| 17:16 | justin_smith | RedNifre: it means implementation details of a macro you call (the names it gives internal bindings) change the meaning of code that calls it. |
| 17:16 | lodin- | RedNifre: The macro author does not know what the user will or won't have in the form. So no names would be safe. |
| 17:17 | justin_smith | where some magic binding name leads to totally different behaviors |
| 17:17 | RedNifre | can you give an example of how you would write your example to prevent that problem? |
| 17:17 | lodin- | RedNifre: This is why we have a#. |
| 17:17 | lodin- | ,`(let [x# 1]) |
| 17:17 | clojurebot | (clojure.core/let [x__25__auto__ 1]) |
| 17:17 | justin_smith | RedNifre: (defmacro frob [form] `(let [a# "dog"] (~form))) - a# expands to a gensym, a guaranteed globally unique symbol |
| 17:17 | RedNifre | hmmm... so in my syntax quoted macros I should add a # to variable names to prevent shadowing? |
| 17:17 | lodin- | RedNifre: So never use names like __<somenumber>__auto__ :-) |
| 17:18 | justin_smith | RedNifre: you need to add a # or it will refuse to even compile |
| 17:18 | justin_smith | lodin-: gensym is smart enough to still make a name that doesn't conflict if you do that though |
| 17:18 | justin_smith | afaik |
| 17:18 | lodin- | Would be the most confusing macroexpanded code ever though. :-) |
| 17:18 | RedNifre | But what if I want to do (frob (str "Hey " a ", go fetch!")) ? |
| 17:19 | justin_smith | RedNifre: it would tell you a is unbound |
| 17:19 | justin_smith | RedNifre: clojure doesn't like spooky action at a distance, we do lots of things that discourage it |
| 17:19 | justin_smith | or make it difficult to implement |
| 17:19 | lodin- | RedNifre: So if you *do* want to use a "local" name, then you can do that, by interpolating a literal symbol. And there are legitimate reasons for doing that. |
| 17:19 | RedNifre | I'm surprised, I thought macros just rewrote the syntax tree. |
| 17:20 | justin_smith | RedNifre: "just" - we are very opinionated about how that "should" happen - but yeah, if you use regular ' you can make anything work |
| 17:20 | justin_smith | and the bugs this inevitably causes are now your burden |
| 17:20 | RedNifre | But it sounds like a sensible restrictions, I mean, getting extra stuff from a macro would be strange. |
| 17:20 | lodin- | ,`(let [~'x 1]) |
| 17:20 | clojurebot | (clojure.core/let [x 1]) |
| 17:21 | justin_smith | RedNifre: lodin-: great example of legit use of intentional shadowing is the #() reader macro |
| 17:21 | justin_smith | ,'#(+ % %) |
| 17:21 | clojurebot | (fn* [p1__74#] (+ p1__74# p1__74#)) |
| 17:21 | justin_smith | in that example, % was magic, and refers to a binding the compiler creates |
| 17:22 | justin_smith | we accept it as given that % will be the first arg in the generated function, and substituted smartly in the function body |
| 17:22 | RedNifre | Okay, so... a macro may rearrange what you put into it but it won't/shouldn't add anything surprising? |
| 17:22 | justin_smith | well, that's getting into philosophy I guess... |
| 17:22 | justin_smith | haha |
| 17:23 | justin_smith | but yes, best to avoid surprises, just as with any other code I'd think |
| 17:23 | RedNifre | What I mean is you can't do (library-in-a-macro (foo "look, the macro gave me a foo function I can use!")) |
| 17:23 | justin_smith | right, that's probably a bad idea |
| 17:24 | RedNifre | I'm not sure if I understood if that's even possible or not. |
| 17:24 | RedNifre | I think you said not using # at the end of "foo" would be a compile error, but there is intentional shadowing in some reader? |
| 17:26 | RedNifre | The macro can't do "(let [foo println]" because that won't compile, right? So how would the macro provide a foo function to the... what's it called, macro content? |
| 17:26 | lodin- | RedNifre: By interpolating a (unqualified) symbol. |
| 17:27 | lodin- | ,(defmacro foo [form] `(let [~'x 42] ~form)) |
| 17:27 | clojurebot | #'sandbox/foo |
| 17:28 | lodin- | ,(foo x) |
| 17:28 | clojurebot | 42 |
| 17:29 | RedNifre | I guess it'll take some time until I aquire an intuition for what I should and shouldn't do with macros... |
| 17:30 | justin_smith | RedNifre: two groups of people should write macros - novices who need to figure out what the hell the deal is with macros, and masters of the language who need to offer new syntax back to the community. In between they are almost always a mistake. |
| 17:30 | lodin- | RedNifre: If you write a program that, you know, actually do useful stuff, you don't need to write any macro. the clojure.core macros plus functional abstraction will take you pretty far. |
| 17:30 | justin_smith | yeah, exactly |
| 17:31 | justin_smith | but the awesome thing is, when someone sees that we need eg. core.async or core.logic, the tools are there to do it within the regular language, without forking it |
| 17:32 | justin_smith | because those things would be pretty useless without the new syntaxes which make their features usable |
| 17:36 | RedNifre | So do you people use clojure at the office? |
| 17:37 | justin_smith | RedNifre: right now I should be writing clojure code but am procrastinating |
| 17:37 | justin_smith | literally sitting at my desk in the office |
| 17:37 | RedNifre | Well, how did clojure take hold in your office? Or did your company always use some lisp or other fp language? |
| 17:37 | justin_smith | they decided to convert the ruby app to clojure, so then they hired me to make it happen |
| 17:38 | RedNifre | Ah, ruby to clojure makes sense I guess. |
| 17:38 | RedNifre | (I was asking because I'm having a hard time imagining my java office even consider something like clojure) |
| 17:38 | justin_smith | RedNifre: it was a bundle of ruby services making up an app, someone converted one to clojure because it needed to be concurrent, they saw how much faster development was on that service, the lower defect rate, and the better performance, and eventually opted to redo the rest of it too |
| 17:38 | DynamicMetaFlow | What factors would lead someone to go from a Ruby app to Clojure? |
| 17:39 | justin_smith | RedNifre: the two big things would be dev time and defect rate, even in a java environment |
| 17:39 | justin_smith | DynamicMetaFlow: wanting to spend less money on servers, faster dev on mature apps with lower defect rate (immutability helps a lot here) |
| 17:40 | justin_smith | DynamicMetaFlow: on the other hand, the initial ramp-up for a proof of concept is much faster with ruby - that hits a limit for most devs though |
| 17:40 | DynamicMetaFlow | What do you mean by "ramp-up for a proof of concept |
| 17:41 | DynamicMetaFlow | Do you mean prototyping or something else |
| 17:41 | justin_smith | DynamicMetaFlow: going from "we should make an app that does X" to having a first prototype |
| 17:41 | justin_smith | exactly, prototyping |
| 17:41 | justin_smith | so ruby probably prototypes faster, but clojure is more likely to end up with something maintainable (if done idiomatically), and can be almost as fast to prototype |
| 17:41 | DynamicMetaFlow | That's interesting, I've heard claims of the same fast prototyping in Clojure |
| 17:42 | justin_smith | DynamicMetaFlow: sure, but I think with things like rails, you can really get a first proof of concept of something that looks slick and has all the moving parts very fast |
| 17:42 | justin_smith | clojure can compete, but I don't think it's quite as instant |
| 17:43 | DynamicMetaFlow | Yeah, I've picked up Python but haven't practiced in a while. I did learn a little Ruby and then made a complete switch to Common Lisp and now I want to entertain Clojure because of the JVM. At this point though I just need to sit with one language |
| 17:43 | justin_smith | DynamicMetaFlow: yeah, that makes sense. All the stuff the jvm makes available is pretty useful if you need modern libraries - because the jvm has libs for just about everything. |
| 17:43 | DynamicMetaFlow | That's a good point. I remember someone in a podcast saying that Rails is what popularized Ruby and to an effect clojurescript will hopefully do the same for Clojure. It was an interesting podcast |
| 17:44 | justin_smith | DynamicMetaFlow: yeah, I think cljs, plus reagent and figwheel and ring could do that |
| 17:45 | DynamicMetaFlow | Right now I'm thinking of learning more Ruby to at least help out in different projects to contribute to but long term make the switch to Clojure |
| 17:46 | justin_smith | cool, hope you stick with the plan to learn clojure, and find it as rewarding as many of us here have |
| 17:47 | rotcev | what's some good stuff to read on macros, i want to learn about unquoting or w/e idk what thats all about |
| 17:47 | RedNifre | Currently, Ruby is my go-to language for very small things (scripts with a couple hundred lines) because Ruby's focus is on programmer happiness and everything's allowed. |
| 17:47 | DynamicMetaFlow | I feel that when I was learning Common Lisp it's mental models is what worked in my head |
| 17:47 | RedNifre | I wouldn't use it for anything large though. |
| 17:48 | justin_smith | rotcev: the classic is "On Lisp" by that guy who founded y combinator |
| 17:48 | justin_smith | turns out he knows a shitload about macros |
| 17:48 | DynamicMetaFlow | Has anyone gone through this course from Eric Normand, http://www.purelyfunctional.tv/ |
| 17:48 | justin_smith | the book is about common lisp, but it's all pretty easy to adjust to if you know clojure |
| 17:48 | rotcev | is a macro a lisp thing or a clojure specific feature |
| 17:49 | RedNifre | Hm, the "clojure for the brave and true" seems to be very theoretical. If I want to get my hands dirty and write a tiny command line program that uses a sqlite database, where would I start? |
| 17:49 | justin_smith | DynamicMetaFlow: I've heard great things about it, but have not. I do remember Eric Normand from when he hung out in this channel, and he knows his shit. |
| 17:49 | justin_smith | RedNifre: google for "clojure sqlite binding", check out the readme? |
| 17:49 | RedNifre | E.g. for node.js, the process was: 1. Find library on npm 2. ask on freenode where they tell you that the popular npm library is garbage 3. use what freenode recommends. |
| 17:49 | DynamicMetaFlow | Yeah, I listened to him on a podcast and I liked his thoughts regarding How to teach someone in programming |
| 17:50 | justin_smith | RedNifre: oh, a good ingredient here is going to the project for that lib on clojars and see how many downloads it has, and also going on crossclj.info to see what reputable libs and apps use it |
| 17:50 | RedNifre | is there something like hackage or npm for clojure? |
| 17:50 | DynamicMetaFlow | This was the podcast I listened to, I highly recommend it. https://devchat.tv/ruby-rogues/232-rr-teaching-and-how-we-can-all-do-more-to-teach-technical-topics-to-others-with-eric-normand |
| 17:50 | RedNifre | ah, thanks. I'll check it out. |
| 17:51 | RedNifre | You people don't happen to know about any beginner friendly command line and sqlite libraries, huh? |
| 17:52 | scottj | rotcev: neither, other languages have macros too. macros in clojure are very similar to those in most other lisps though. |
| 17:53 | scottj | RedNifre: familiar with https://github.com/razum2um/awesome-clojure and clojure awesome ? |
| 17:54 | RedNifre | No, I started some hours ago so I'm not familiar with anything. |
| 17:54 | justin_smith | rotcev: clojure macros are highly similar to common lisp macros, which is why paul graham's "on lisp" will likely teach you more than any other source I know of, but there is also some influence from scheme and the concept of hygeinic macros (though we don't use their model / syntax for that at all) |
| 17:54 | scottj | http://www.clojure-toolbox.com/ |
| 17:54 | rotcev | could someone briefly explain the answer to 'why macros' |
| 17:54 | RedNifre | Ah, those links look excellent, thanks! |
| 17:55 | RedNifre | rotcev I think it's so you don't have to transform and then eval quoted source code. |
| 17:55 | justin_smith | or, alternately, fork the compiler |
| 17:56 | rotcev | does unquote just force an eval on a quote |
| 17:56 | rotcev | ? |
| 17:56 | justin_smith | rotcev: macros allow the definition of new syntaxes. Which as you probably would guess isn't to often needed - but we constantly use those new syntaxes already developed, some which come with the language, some provided by libraries, all made with macros |
| 17:56 | rotcev | yeah i understand they are powerful thats why i want to be able to make my own |
| 17:57 | rotcev | xD |
| 17:57 | justin_smith | rotcev: a macro is a function that takes source code as an argument, and returns source code. the quote / unquote is a way of templating source code substitutions |
| 17:58 | justin_smith | but it works on the level of forms, not strings as eg. C macros do |
| 17:58 | RedNifre | are there meta macros that generate macros? |
| 17:58 | justin_smith | sure |
| 17:58 | justin_smith | RedNifre: sometimes the only reason you need to write a macro is that the functionality you need is only provided by a macro, so you need another macro to extend it |
| 17:58 | justin_smith | RedNifre: for example if you want a new special kind of defn that extends the existing defn macro |
| 17:59 | justin_smith | you could either recreate defn by scratch, or write a macro that expands to usage of defn |
| 17:59 | justin_smith | you can't just use a function and be compatible with defn, of course |
| 17:59 | justin_smith | since defn is a special syntax |
| 17:59 | RedNifre | Hm, can a macro expand to two copies of itself, crashing the compiler? |
| 17:59 | justin_smith | RedNifre: I bet it's possible |
| 18:00 | lodin- | RedNifre: Macros can be recursive, yes, if that's what you mean. |
| 18:00 | justin_smith | ,(defmacro boom [x] `((boom ~x) (boom ~x))) |
| 18:00 | clojurebot | #'sandbox/boom |
| 18:01 | justin_smith | ,(boom 42) |
| 18:01 | clojurebot | #error {\n :cause nil\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.StackOverflowError, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 6875]}\n {:type java.lang.StackOverflowError\n :message nil\n :at [java.util.regex.Pattern$5 isSatisfiedBy "Pattern.java" 5151]}]\n :trace\n [[java.util.regex.Pattern$5 isSatisfiedBy... |
| 18:01 | justin_smith | RedNifre: there you go |
| 18:01 | justin_smith | macro-fork-bomb |
| 18:02 | RedNifre | hm, I wonder how hard it would be to find the bug. I'm not good at reading that error message but I guess in a real project it wouldn't tell you where the macro-fork-bomb happened, huh? |
| 18:02 | RedNifre | Meh, I guess there's always git bisect. |
| 18:02 | justin_smith | RedNifre: it would give you a line number instead of NO_SOURCE_PATH:0:0 |
| 18:02 | justin_smith | and a file name |
| 18:03 | RedNifre | What's the limit of macros? I guess you couldn't compile ruby code using a macro since ruby has too many characters that have special meaning in clojure, e.g. ~ ? |
| 18:03 | rotcev | so is x in that example quoted source code, and the ~x unquotes it to be evaluated ? |
| 18:04 | justin_smith | yeah, you are limited to what works with the existing clojure reader rules, that all apply before macro expansion |
| 18:04 | justin_smith | rotcev: yes |
| 18:04 | RedNifre | okay, so no funky characters and the parens must match? |
| 18:04 | justin_smith | kind of |
| 18:04 | lodin- | RedNifre: The macro doesn't see any parens. |
| 18:04 | lodin- | RedNifre: The macro only sees Clojure data structures. |
| 18:05 | RedNifre | What I mean is that I could'n write a macro that works like (strange-macro ))))))) |
| 18:05 | lodin- | RedNifre: This is a key point of this kind of macro. |
| 18:05 | TEttinger | like (+ 1 #_ "whee" 1) returns 2 |
| 18:06 | TEttinger | it only sees what it's been given, (+ 1 1), since the #_ reader macro ignores "whee" |
| 18:07 | rotcev | #_ means ignore next value ? |
| 18:07 | TEttinger | I'm not sure if there's anything like #_ possible in user-written macros |
| 18:07 | TEttinger | yeah, discard |
| 18:07 | RedNifre | Okay, so could I write a macro for BF code, i.e. (bf +++>>>+[.+]) or something like that? |
| 18:07 | justin_smith | ,(+ 1 #_ anything-goes-here! 1) |
| 18:07 | clojurebot | 2 |
| 18:07 | rotcev | nice |
| 18:07 | TEttinger | https://yobriefca.se/blog/2014/05/19/the-weird-and-wonderful-characters-of-clojure/ |
| 18:07 | RedNifre | ,(+ 1 #_ ))))) 1 ) |
| 18:07 | clojurebot | #<RuntimeException java.lang.RuntimeException: Unmatched delimiter: )> |
| 18:07 | justin_smith | RedNifre: well, the delimiters would have to work |
| 18:08 | TEttinger | yeah, #_ discards one form |
| 18:08 | justin_smith | RedNifre: I think bf has situations where combos of delimiter chars are allowed that would be illegal in clojure |
| 18:08 | TEttinger | ,(+ 1 #_{:a [1 2 3]} 1) |
| 18:08 | clojurebot | 2 |
| 18:08 | lodin- | RedNifre: +++>>>+[.+] would be three things. First a symbol, +++>>>+, then a vector holding the symbol .+ |
| 18:08 | RedNifre | ,(+ 1 #_ ++>><<[+.]-- 1 ) |
| 18:08 | clojurebot | #error {\n :cause "+."\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.ClassNotFoundException: +., compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6688]}\n {:type java.lang.ClassNotFoundException\n :message "+."\n :at [java.net.URLClassLoader$1 run "URLClassLoader.java" 366]}]\n :trace\n [[java.net.URLClassLoader$1 run "U... |
| 18:09 | TEttinger | lodin-: no, . isn't allowed in symbol names IIRC |
| 18:09 | RedNifre | okay, so inline bf would be impossible because it uses [ ] for loops. |
| 18:09 | TEttinger | ,(defn a.b 1) |
| 18:09 | clojurebot | #error {\n :cause "Parameter declaration \"1\" should be a vector"\n :via\n [{:type java.lang.IllegalArgumentException\n :message "Parameter declaration \"1\" should be a vector"\n :at [clojure.core$assert_valid_fdecl$fn__7207 invoke "core.clj" 7187]}]\n :trace\n [[clojure.core$assert_valid_fdecl$fn__7207 invoke "core.clj" 7187]\n [clojure.core$map$fn__4785 invoke "core.clj" 2646]\n [clojure... |
| 18:09 | TEttinger | ,(def a.b 1) |
| 18:09 | clojurebot | #'sandbox/a.b |
| 18:09 | TEttinger | oh nvm |
| 18:09 | TEttinger | ,a.b |
| 18:09 | clojurebot | #error {\n :cause "a.b"\n :via\n [{:type clojure.lang.Compiler$CompilerException\n :message "java.lang.ClassNotFoundException: a.b, compiling:(NO_SOURCE_PATH:0:0)"\n :at [clojure.lang.Compiler analyze "Compiler.java" 6688]}\n {:type java.lang.ClassNotFoundException\n :message "a.b"\n :at [java.net.URLClassLoader$1 run "URLClassLoader.java" 366]}]\n :trace\n [[java.net.URLClassLoader$1 run... |
| 18:09 | TEttinger | ah ok |
| 18:09 | justin_smith | RedNifre: as lodin- said that's three forms |
| 18:09 | TEttinger | it "allows" infix dot |
| 18:09 | TEttinger | but not really |
| 18:10 | justin_smith | ,(+ 1 #_#_#_ ++>><<[+.]-- 1 ) ; RedNifre |
| 18:10 | clojurebot | 2 |
| 18:10 | justin_smith | one #_ per form |
| 18:10 | TEttinger | nice trick, justin_smith |
| 18:11 | rotcev | so what is the difference between ` and ' |
| 18:12 | TEttinger | RedNifre: this type of thing is a good use for just using a string as a sequence of chars, since BF is all one-char operators anyway right? |
| 18:12 | RedNifre | hm, so macros would make it trivial to write something like Haskell's do-notation... |
| 18:12 | TEttinger | rotcev: I really liked brave clojure's walk through that stuff |
| 18:12 | lodin- | RedNifre: And it has been done, kind of. |
| 18:12 | rotcev | kk |
| 18:12 | justin_smith | RedNifre: we do have monad libs, I don't know how far they go syntax wise though |
| 18:13 | TEttinger | http://www.braveclojure.com/writing-macros/ |
| 18:13 | lodin- | justin_smith: I've seen one library that goes pretty far in the Haskell direction, even using <- to bind. |
| 18:14 | justin_smith | lodin-: fascinating |
| 18:14 | Glenjamin | http://www.leonardoborges.com/writings/2012/12/08/monads-in-small-bites-part-iv-monads/ appears to have something like do notation |
| 18:15 | lodin- | justin_smith: It looks pretty alien in Clojure code though. |
| 18:17 | RedNifre | Well, in Haskell the problem was that you had nested lambdas i.e. (bind (getSomething) (fn [something] (bind (process something) (fn [another-thing]...))))))) and do-notation turns that into (monadic-do (<- something getSomething) (<- another-thing process something) ...) |
| 18:17 | RedNifre | Okay, I think I now see the value of macros :) |
| 18:18 | lodin- | RedNifre: :-) |
| 18:29 | lodin- | RedNifre: The same nesting would occur in Clojure, but there's no IO monad in Clojure, everything is Maybe (can be nil), and list has it's own do-notation in disguise (the for macro). And let is ordered and allows rebinding, so it is OK to do (let [state (init-state 5), [state v] (get state), state (put state 3)] ...). |
| 18:32 | RedNifre | lodin- Well, I'm not sure, couldn't it be done without nesting? Let me write an example... |
| 18:32 | lodin- | RedNifre: What I'm saying is that monads are treated case by case. |
| 18:33 | lodin- | RedNifre: Not if you want a bind operation and code that would do the same as you Haskell example. |
| 18:34 | RedNifre | lodin- I wrote an example, would it be possible to write a macro that turns the second code into the first?: http://pastebin.com/PRz2UAEV |
| 18:34 | clojurebot | No entiendo |
| 18:34 | lodin- | RedNifre: Monads are less useful in Clojure though since you can't infer which return to use, hence case by case. |
| 18:34 | lodin- | RedNifre: Of course, see the link given by Glenjamin above. |
| 18:35 | RedNifre | In my example, if I used some promise library that came with that "then" function, could I write that macro just for that one case? |
| 18:35 | RedNifre | ah, I somehow missed that link. one sec. |
| 18:37 | lodin- | RedNifre: I mean that without using a macro (i.e. introducing do notation in Clojure), the Clojure code would have the same problem. |
| 18:37 | lodin- | RedNifre: It's just like justin_smith said, the benefit of macros is that you don't need a language update to provide it, you only need a library. |
| 18:38 | RedNifre | Ah, okay, then I understand what you mean. |
| 18:39 | RedNifre | Well, it's getting late. Thank you all for your help, good night/day/etc.! |
| 18:47 | justin_smith | so I found this twitter account that just posts random pictures from unprotected webcam feeds, and this post it just made is basically me rn https://twitter.com/FFD8FFDB/status/696117413931315200 |
| 19:11 | noncom|2 | what do you think about scala, people? i have heard that clojure and scala do complement each other.. what are your thoughts? |
| 19:11 | noncom|2 | (note: i've been 2.5 years scala, and now i'm 2.5 years of clojure) |
| 19:12 | noncom|2 | i am looking at the messages that i still occasionally receive from scala mailing list and stuff... and they seem soo contrived and convoluted :/ |
| 19:12 | justin_smith | noncom|2: I wish it was feasible to write individual things in scala then use them via clojure, but it seems like scala is hard to use from other jvm langs |
| 19:12 | noncom|2 | of course, much of this comes because of the type system and stuff.. |
| 19:13 | justin_smith | noncom|2: or, maybe I'm misinformed and that is actually a straightforward thing to do? |
| 19:13 | noncom|2 | hmmm i am not really sure about that. |
| 19:13 | noncom|2 | WOW https://t6.github.io/from-scala/ |
| 19:14 | noncom|2 | well, i guess i understand - scala is ALL about syntax sugar... and when you deal with what this sugar resolves to.. here comes the pains |
| 19:14 | noncom|2 | but this lib seems ilke a bridge.. |
| 19:15 | noncom|2 | well, one more issue i see, is that scala compiler is a sooooo demanding machine.. |
| 19:15 | noncom|2 | it can drain all your computer resources for just barely printing several symbols inside some of the sugared clauses |
| 19:16 | noncom|2 | i remember typing hell instead some pattern-matched json destructor.. i was literally getting 1 char in 1-2 seconds or so |
| 19:16 | noncom|2 | (the presentation compiler i mean here, the class one might be heavier but runs once in a while) |
| 19:17 | noncom|2 | s/instead/inside |
| 19:17 | noncom|2 | eh.. sleepy |
| 19:20 | noncom|2 | justin_smith: could you maybe answer a web-specific question? i have a login form made with cljs+bootstrap.. how do i enable password saving in it? |
| 19:21 | justin_smith | noncom|2: the way I handle that is not create the login page with cljs - that way I'm not sending the full app to a user that isn't logged in |
| 19:22 | justin_smith | side effect is normal plain old html login page where the browser save-this-password just works |
| 19:22 | noncom|2 | hmmm, interesting |
| 19:22 | justin_smith | noncom|2: so the flow is - plain html login, if login succeeds redirect to the single page app and send them all the js etc. |
| 19:23 | noncom|2 | ok, i got it.. heh, i'll have to rewrite this bunch of stuff this way... but do you maybe know, how does the browser distinguish? |
| 19:23 | justin_smith | noncom|2: I'm sure there is a way to make the browser save passwords in a react app, but this is why I haven't had to figure that out |
| 19:23 | noncom|2 | ah |
| 19:23 | noncom|2 | well okay |
| 19:24 | justin_smith | noncom|2: but your case might be different - I have a webapp as a service, so it makes sense not to send the js until they auth |
| 19:24 | justin_smith | that makes the auth questions in the server side much simpler |
| 19:25 | noncom|2 | justin_smith: in my case it makes sense too. i just did not design it the way you did - the login form is a part of the jsapp. i think your way is more correct and i have to change to it. but it may be not as easy task as i expected, but certainlyk doable |
| 19:27 | noncom|2 | justin_smith: eh, just FYO: http://timothy.userapp.io/post/63412334209/form-autocomplete-and-remember-password-with another html/js hell there. i am certainly taking up your way. thanks for the hint! |
| 19:27 | justin_smith | noncom|2: nice to know I dodged that bullet |
| 20:04 | justin_smith | lol http://www.foaas.com/ |
| 20:07 | TEttinger | oh my god Ballmer |
| 20:07 | justin_smith | lol |
| 20:08 | justin_smith | TEttinger: oh that's awesome I hadn't gotten to that one yet |
| 20:12 | justin_smith | TEttinger: https://www.refheap.com/114550 |
| 20:13 | justin_smith | I know that in the real world there is no such rivalry |
| 20:16 | TEttinger | nice |
| 20:18 | visof | ,(partition 2 1 [1 2 3 4 5 6]) |
| 20:18 | clojurebot | ((1 2) (2 3) (3 4) (4 5) (5 6)) |
| 20:19 | visof | ,(map (fn [xs] (first xs)) (partition 2 1 [1 2 3 4 5 6])) |
| 20:19 | clojurebot | (1 2 3 4 5) |
| 20:20 | justin_smith | ,(map first (partition 2 1 [1 2 3 4 5 6])) ; (fn [xs] (first xs)) is a weird way to write first |
| 20:20 | clojurebot | (1 2 3 4 5) |
| 20:21 | visof | justin_smith: yeah that's neater, but fn i'm planning to do more complex things than first |
| 20:22 | justin_smith | k |
| 20:26 | amalloy | speaking of weird ways to write a thing, i recently fixed a bunch of tests that looked like: (is (= true (every? true? (map #(f x %) ys)))) |
| 20:26 | justin_smith | so with partition 2 1, the first element will only show up once, as the first element of the first pair, and the last will also only show up once, and the last element of the last pair |
| 20:26 | amalloy | a poor way to write (is (every? #(f x %) ys)) |
| 20:27 | justin_smith | amalloy: wow, double whammy there |
| 20:29 | justin_smith | (boolean (is (= true (boolean (every? true? (doall (map #(boolean (f x %)) (seq ys)))))))) ; ftw |
| 20:30 | justin_smith | no, justin_smith, there is no winning this game and there is always a way to add silly complexity |
| 20:32 | justin_smith | oh man - cljs : (inc :1) => ":11" |
| 20:33 | justin_smith | I mean you deserve it if you make a terrible keyword like :1, but wow that's gross |
| 21:40 | Bronsa | justin_smith: wow :/ |
| 21:54 | kenrestivo | justin_smith: ugh |
| 23:26 | rhg135 | that's... disturbing |