Fork me on GitHub
#clojure
<
2017-01-21
>
raspasov00:01:19

hey everyone, not sure if this is the best channel for this but

raspasov00:01:05

do you know if there’s going to be any discount for the ClojureWest hotel? or should I just go ahead and book a room directly with the hotel

mkeathley00:01:28

I read that there's a group rate

raspasov00:01:37

ah damn Twitter OK

raspasov00:01:47

I got a response, on twitter, but never got notified

mkeathley00:01:07

Just to make it official 😛

ag00:01:30

how to conj to vector only if it doesn’t have that element?

bostonaholic00:01:07

use a sorted set?

bostonaholic00:01:01

that came out really fast. I have no idea what that will do 😜

ag00:01:15

bad idea I think. sets do not guarantee order, vectors do

andy.fingerhut01:01:48

@ag Two ways I can think of -- in addition to the vector, also maintain a set of the vector's elements, using the set for the check whether an element is new, adding to both set and vector only if it is new.

andy.fingerhut01:01:22

@ag Second way is to use an ordered-set from flatland library: https://github.com/amalloy/ordered

andy.fingerhut01:01:23

Oh, and of course if the O(n) search time through the vector doesn't bother you, you can always search the vector for an element before adding yourself

ag01:01:05

eeish… I thought maybe there’s simple elegant solution. so often after writing “something very smart” I realize there’s an implementation in .core

sophiago01:01:58

is there a way to get printf to not append nil to its output?

sophiago02:01:12

ugh wait. with-out-str outputs a string whereas i was using printf to convert hex strings to numbers

bcbradley02:01:50

i'm getting a strange error when i try to use https://github.com/cch1/http.async.client

(ns discord.core
  (:require
    [clojure.data.json :as json]
    [http.async.client :as http]))
(defn request
  "Perform an http request through discord's official api. "
  [token-kind token method path query body]
  {:pre [(or (nil? token-kind) (keyword? token-kind))
         (if (nil? token-kind) true (token-kind #{:bot :bearer}))
         (or (nil? token) (string? token))
         (keyword? method)
         (method #{:get :put :patch :delete :post})
         (string? path)
         (or (nil? query) (map? query))
         (or (nil? body) (map? body) (vector? body))]}
  (with-open [client (http/create-client :user-agent "DiscordBot (, 0.0.0)")]
    (apply
      (method {:get http/GET :put http/PUT :patch http/PATCH :delete http/DELETE :post http/POST})
      (str "" path)
      (concat
        (if (nil? body) [] [:body (json/write-str body)])
        (if (nil? query) [] [:query query])
        (if (or (nil? token-kind) (nil? token)) [] [:headers {"Authorization" (str (clojure.string/capitalize (name token-kind)) " " token)}])))))

bcbradley02:01:30

The error says

java.lang.IllegalArgumentException: No value supplied for key: {"Authorization" "Bot DISCORD_API_KEY"}, compiling:(discord/core.clj:560:1)

bcbradley02:01:28

I'm invoking it from here:

(defn get-channel
  "Get a channel by ID. "
  [token-kind token channel-id]
  {:pre [(string? channel-id)]}
  (request token-kind token :get (str "/channels/" channel-id) nil nil))
(get-channel :bot "DISCORD_API_KEY" "269507447671095298")

bcbradley02:01:36

What do you think it could be?

bcbradley02:01:39

If I require

org.httpkit.client
and use this definition of request, it operates succesfully:
(defn request
  "Perform an http request through discord's official api. "
  [token-kind token method path query body]
  {:pre [(or (nil? token-kind) (keyword? token-kind))
         (if (nil? token-kind) true (token-kind #{:bot :bearer}))
         (or (nil? token) (string? token))
         (keyword? method)
         (method #{:get :put :patch :delete :post})
         (string? path)
         (or (nil? query) (map? query))
         (or (nil? body) (map? body) (vector? body))]}
  (get
    (update
      @((method {:get client/get :put client/put :patch client/patch :delete client/delete :post client/post})
        (str "" path)
        (merge
          {:user-agent "DiscordBot (, 0.0.0)" :as :text}
          (if (nil? body) {} {:body (json/write-str body)})
          (if (nil? query) {} {:query-params query})
          (if (or (nil? token-kind) (nil? token)) {} {:headers {"Authorization" (str (clojure.string/capitalize (name token-kind)) " " token)}})))
      :body
      json/read-str
      :key-fn
      (comp
        keyword
        clojure.string/lower-case
        #(clojure.string/replace % \_ \-)))
    :body))

bcbradley02:01:26

The logic is virtually the same, the only difference is the second implementation of request does some extra stuff. I can't see how the second implementation can work, while the first one doesn't.

seancorfield02:01:49

@bcbradley That error indicates illegal syntax on line 560 of discord/core.clj — is the code on GitHub where we can see the source?

bcbradley02:01:32

no, i haven't put anything on github yet

bcbradley02:01:41

i can send you a zip if you want to take a look at it

seancorfield02:01:37

No. Put it up on GitHub where anyone can see it and help you.

notid02:01:58

clojure.spec question here… from the docs:

(s/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z])
However, would you expect the generator to generate ::secret, ::user, and ::pwd?
(gen/generate (s/gen (s/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z])))
#:spec-game.core{:x 2447, :y 0, :secret -25791379, :user 190093, :pwd -3668447, :z -36153036}

notid02:01:39

seems like the generator should use the same or/and logic rules

seancorfield02:01:00

It only states what is required — additional keys are always acceptable.

notid03:01:45

Sure, however it never generates anything with any unspecified key. It seems like it would never generate a map that’s missing a secret, or pair of user/pwd, which seems like it’s likely to cause issues

notid03:01:32

further, it means you have to use a custom generator if the rule is: secret or user/pwd, but never both.

seancorfield03:01:56

But a map with both still conforms to the spec.

seancorfield03:01:12

In your case {:x 1 :y 1 :user “fred”} would fail conformance but {:x 1 :y 1 :user “fred” :secret “hush”} would pass I believe.

notid03:01:12

I agree it conforms to the spec, however I think the problem is that there is information provided that is disregarded in generating sample data. I realize this is an extreme example, but it’s as if the implementation of the int? generator was (constantly 0). Technically conforms, yes, but not really in the spirit of “generating” data

Alex Miller (Clojure team)03:01:03

There's actually a ticket with a patch that overhauls all this stuff

Alex Miller (Clojure team)03:01:15

David Chelimsky worked on it with Rich

notid03:01:36

Ah, nice. I did a search in JIRA but couldn’t find it

notid03:01:44

was going to do some digging if not. 🙂

seancorfield03:01:37

It will definitely be nicer if the generator for s/keys respects or in the intuitive way.

seancorfield03:01:19

You still need to bear in mind that something could pass you {:x 1 :y 1 :user "fred" :secret “hush”} and that does conform.

seancorfield03:01:34

The same as {:x 1 :y 1 :username "fred" :secret “hush”} would conform.

notid03:01:36

@seancorfield absolutely yes. Both the case I’m presenting, and yours, are relevant in the spirit of generating sample data that might hit edge cases you didn’t consider

Alex Miller (Clojure team)03:01:40

Those changes better handle or options in the gen but won't add nonexistent keys

Alex Miller (Clojure team)03:01:31

If you want that, then create a spec that merges your spec with another one and override with that generator

notid03:01:35

@seancorfield looks like that ticket is written as you’d like: >(s/keys :req [(or ::x ::y)]) always generates maps with both ::x and ::y but it should also generate maps with either ::x or ::y.

notid03:01:10

ah ok. Thanks for the info!!

cch104:01:41

@bcbradley args to http.async.client fns are keyword args, not a map, IIRC. (apply ,,, (mapcat identity options))

bcbradley04:01:44

@cch1 but what about the header option?

bcbradley04:01:07

... :headers {...}) right?

cch104:01:59

The header option is one of the options. Here’s an example with one option, @bcbradley :

cch104:01:13

(with-open [c (http.async.client/create-client)]
                     (apply http.async.client/GET
                            c
                            ""
                            (mapcat identity {:timeout 1000})))

seancorfield06:01:12

Seems like (apply ... (concat ...)) would be correct there?

seancorfield06:01:45

The error message suggestions a syntax error in the source file, not a runtime failure to destructure arguments.

max-tweddell06:01:15

Does anyone now what the +site template does on luminus?

beppu07:01:41

@max-tweddell According to the github page, +site seems to be a combination of +h2 and +cljs. Strange name for such an option, though. https://github.com/luminus-framework/luminus-template

max-tweddell07:01:23

yeah, I saw that but I wasn't sure if that was all of it, or if there was something else to it

bcbradley14:01:52

Also, @cch1 your advice about flattening the headers map isn't right. It expects a :headers argument key followed by a map that represents the contents of the header

cch114:01:58

That’s what I was trying to demonstrate with another key (:timeout). the GET method wants flattened arg pairs. In the case of header, the val is a map and of course should not be recursively flattened.

bcbradley14:01:13

ah i misunderstood you then

cch114:01:33

Yeah, and I think I misunderstood your question being specific to the headers arg

moxaj14:01:29

@seancorfield @hiredman I think I solved my issue from yesterday: definterface expects type hints to be fully qualified (unless they are primitive or from the java.lang package). Too bad this is not documented for definterface, only for gen-interface.

bcbradley15:01:53

how can make a promise in clojure that depends on another promise? I basically want a promise that is delivered as soon as the promise it depends on is delivered, and the value it returns when derefenced should be the output of a function applied to the value of dereferencing the original promise.

bcbradley15:01:30

nvm i figured it out, i can use a future to deliver the promise

miikka17:01:20

Has anyone heard of Clojure(Script) codebases that are significantly larger than 100k lines? Like 500k or something?

tolitius17:01:45

OMF began with more than 1,700 requirements from Boeing and took two and a half years to develop. Built using more than 34,000 lines of Clojure code, it is one of the largest Clojure code bases to date

miikka17:01:57

Yeah. I've heard several reports of codebases of 100-150k lines and have personally worked on one, but I'm wondering if there's anything bigger out there.

mpenet17:01:07

I think we re at least in the same ballpark

Alex Miller (Clojure team)18:01:19

while I have worked on 1MLOC Java code bases, it seems hard for me to believe that many projects would ever require 1MLOC Clojure code bases

mpenet18:01:36

To be fair, it s not all in 1 product tho

Alex Miller (Clojure team)18:01:14

Clojure allows you to create abstractions that reduce code size (unlike Java where abstractions typically increase code size), so my experience has been largely that size is dependent on the amount of time you dedicate to refactoring

Alex Miller (Clojure team)18:01:06

and if you can spend more time refactoring, your code can almost always be smaller

Alex Miller (Clojure team)18:01:18

but it’s not always worth doing so

bbloom18:01:31

“I didn't have time to write a short letter, so I wrote a long one instead.” ― Mark Twain

tbaldridge18:01:55

@miikka IMO, once you get close to 100k lines of CLJ code, projects tend to look for more leverage to keep the LOC down. For example, you could encode your business logic as CLJ code, or you could write a rules engine and store your rules in a database of some sort.

tbaldridge18:01:35

So once you figure in macros, rules systems, logic engines (datalog, etc.), I find it hard to believe that someone would need a project of 1mil lines of CLJ.

nooga20:01:34

sometimes, I get a feeling that almost everything can be built in less than 100kLOC of app code in clj

nooga20:01:20

actually, chunkiest parts of code in my company are usually queries or parts related to GUI

seancorfield21:01:28

@miikka: @tolitius World Singles just passed 50kloc, including about 13kloc tests.

nooga21:01:24

@seancorfield can you break this number down roughly?

tolitius22:01:14

@seancorfield thanks for the numbers, I thought you mentioned you have quite a large codebase some months ago, but I was not sure what it was. do you use https://github.com/clojure-expectations/expectations for all 13kloc of tests? Is there a particular way you tend to structure your tests to minimize the time to refactor?

tolitius22:01:13

I also heard that circleci codebase is quite large in lines. Boeing is the only large one I actually saw the numbers for. Most of the Clojure codebases I work(ed) with tend to split into smaller libs or different projects once they reach something that is hard to load into one's brain in the morning