Fork me on GitHub
#beginners
<
2019-02-27
>
mitchell_clojure02:02:17

I see a lot of posts here about http-kit but looking at github it seems like the project is not being maintained

mitchell_clojure02:02:50

Is there another library I should be using if I am just getting started? Or is it just stable

seancorfield03:02:21

@mitchell_clojure A lot of Clojure libraries are very stable and can look "unmaintained".

seancorfield03:02:20

It's common for a Clojure library to be very focused on a specific problem-to-solve so it's possible for a Clojure library to be "done".

seancorfield03:02:01

That said, http-kit looks pretty well-maintained to me, with 2.4.0 Alpha 3 coming out about a month ago. We use it heavily at work, if that helps you feel more comfortable about using it? 🙂

mitchell_clojure03:02:40

Thanks Sean, your input is always appreciated. Looks like you are right about that, I just think I put a little too much stake into this blurb on the github: "http-kit's author (@shenfeng) unfortunately hasn't had much time to maintain http-kit recently. To help out I'll be doing basic issue triage, accepting minor/obvious PRs, etc."

dawran603:02:03

Would anyone help me to understanding the parameter form inside the fn? (The code snippet comes from: https://github.com/chrishowejones/blog-film-ratings/blob/master/src/film_ratings/handler/index.clj

(defmethod ig/init-key :film-ratings.handler/index [_ _]
  (fn [{[_] :ataraxy/result}]
    [::response/ok (views.index/list-options)]))

dawran604:02:43

I feel like it’s doing some kind of parameter destruction but I can’t make sense of the map: {[_] :ataraxy/result}

seancorfield04:02:39

@dawran6 That is destructuring that will match {:ataraxy/result [...]} with zero or more elements where ... is

seancorfield04:02:14

user=> (let [{[x] :ataraxy/result} {:a 1 :ataraxy/result [42]}] x)
42
user=> (let [{[x] :ataraxy/result} {:a 1 :ataraxy/result []}] x)
nil
user=> (let [{[x] :ataraxy/result} {:a 1 :ataraxy/result nil}] x)
nil
user=> (let [{[x] :ataraxy/result} {:a 1 :ataraxy/result :stuff}] x)
Execution error (UnsupportedOperationException) at user/eval158 (REPL:1).
nth not supported on this type: Keyword
user=>      

seancorfield04:02:42

See how the value of that key can be nil or have zero or more elements -- but cannot be certain types of values. In this case tho', the _ is just a placeholder so this function is really ignoring the values anyway and so it's just "documentation" really...

seancorfield04:02:25

In particular, the element doesn't have to exist at all

user=> (let [{[x] :ataraxy/result} {:a 1}] x)
nil
user=> 

dawran604:02:10

Thanks @mitchell_clojure and @seancorfield. I wasn’t aware of this way of destructuring. Those were great explanations

mitchell_clojure04:02:49

It's actually a combination of both sequential and associative destructuring, explained more thoroughly here: https://clojure.org/guides/destructuring . It doesn't smell idiomatic to me to try to hint at the structure of your data in this way, though, but I /am/ just a beginner

seancorfield04:02:04

https://clojure.org/guides/destructuring#_associative_destructuring shows the {x :k} style first, and then the {:keys [k]} form -- the former can bind values to symbols that don't match the keys, whereas the latter requires your names match your keys.

seancorfield04:02:55

The last example in this section shows everything together https://clojure.org/guides/destructuring#_where_to_destructure -- and includes an example very much like the function above that you asked about.

dawran604:02:04

I was also looking at the links you two mentioned. I think I am more used to seeing the {:keys [k]} form than the associative destructuring. Now I learn something new.

seancorfield04:02:10

Yeah, the :keys form is by far the most common. Good to know about :syms and :strs as well.

mitchell_clojure04:02:55

What I don't really get is the purpose of destructuring in this specific example if you are not going to use the contents of said collection anyways. Is it really just trying to be more "clear" about what we want the argument to be?

dawran604:02:04

Perhaps there’s no strong purpose in this example but I found another one actually uses the destructured data: https://github.com/chrishowejones/blog-film-ratings/blob/master/src/film_ratings/handler/film.clj#L13

mitchell_clojure04:02:17

Nice this looks like a good resource for me

seancorfield04:02:05

@dawran6 Yeah, and that function has :as request but then doesn't use request so that's also a bit odd.

dawran604:02:38

Indeed that’s a bit more oddity there again 😛

vale15:02:51

@seancorfield i reflexively add :as req to my handlers when i start writing them and often forget to delete it later

alexmiller15:02:28

I find those :as can serve as useful docs even if they aren't used

noisesmith16:02:54

yeah, put readable names on all the things (see also (fn foo [] ...) on anonymous functions - it even makes stack traces more readable)

vale16:02:22

i started unanonimizing my functions when i saw babel doing it in js and realized >stacktrace

seancorfield17:02:12

@vale Yes, I can definitely see value in it -- if it is done consistently (which it is not in that tutorial, unfortunately).

lady3janepl17:02:46

o/ silly question, I’m trying to figure out why something I wrote is slow

lady3janepl17:02:04

I’m looking at docs online, and there’s this:

;; Now lets parse the XML into a Document.  It is not strictly
;; necessary to memoize, but this provides a cache of the parsed
;; document which will make operations on the document faster.
(def xmldoc
     (memoize (fn [] (xml->doc (hackernews-rss-xml)))))

lady3janepl17:02:02

why is there a memoize? wouldn’t (def xmldoc (xml->doc "contents of the url")) do the same thing?

lady3janepl17:02:24

(provided I’m happy to load the contents into memory at the time of execution rather than delay)

noisesmith17:02:36

yeah, pretty much

dpsutton17:02:38

memoizing an arity 0 function seems very strange to me

hiredman17:02:50

it should be a delay

hiredman17:02:57

it is to avoid side effects at code load

noisesmith17:02:04

I've always been confused by the preference for a memoized 0-arg function over using delay which is made for specifically this purpose...

dpsutton17:02:23

ah. i see. thanks

noisesmith17:02:53

(even at work, if I use delay people ask "what's that I don't get it", then they jump through hoops with memoize and forcing things to be 0 arg functions for the same semantics...)

noisesmith17:02:30

helpful trick: force on any object that isn't a delay acts as identity, on a delay it accesses the value of the realized delay - it makes it straightforward to make a function that accepts a delay or a regular value

lady3janepl17:02:33

so would that normally be written (def xmldoc (delay (xml->doc "contents of the url"))) and then resolved with @xmldoc, right?

noisesmith17:02:02

user=> (force (delay 42))
42
user=> (force 42)
42

hiredman17:02:05

a lot of stuff in the code you linked is weird junk

noisesmith17:02:12

(force xmldoc) would also work

lady3janepl17:02:21

how would you debug why your code is slow?

hiredman17:02:30

my guess it is written by someone also just learning clojure, which is fine, but hardly an example to follow

noisesmith17:02:02

@lady3janepl I would use a profiler, sadly there's a learning curve to translate the info from a profiler to useful improvements to clojure code

hiredman17:02:04

lots of weird forcing seqs to be vectors, before treating them as seqs again

lady3janepl17:02:10

(tdlr: I have an xml, I use this lib to parse it, I then map a function over a collection of nodes and turn them into nil-or-map)

lady3janepl17:02:36

…turns out that compiling an expression beforehand helps. Thanks 😄

lady3janepl17:02:33

another q with regards to delays: if I create a delay, and then want to map over it (but also stored as a delay), do I have to do this: (def further (delay (map f @original))) or is it sufficient to: (def further (map f original))

lady3janepl17:02:00

(how smart is map?)

noisesmith17:02:44

map is lazy, but you would need to call force or deref on original

noisesmith17:02:32

also, if xml->doc were lazy, you could leverage that instead of using a delay

noisesmith17:02:11

also, there's often good reason to use lazy values inside a limited scope rather than the top level, because that way you avoid holding the whole thing in memory (might not be a concern here)

lady3janepl17:02:51

yeah, I’d normally go with something like that, but I have a file that fits in memory and I’m essentially writing a script to clean/extract data and dump it back into json

lady3janepl17:02:58

so it needs to be processed in one go anyway

lady3janepl18:02:14

I’m asking questions so that I know how to use it in future, basically 😄

overde18:02:47

People of Clojure Land, have ever anyone here deployed an Uberwar and connected to the REPL?

hiredman18:02:25

what "the repl"?

overde18:02:00

may be the nRepl I guess ?

hiredman18:02:08

are you running an nrepl server?

overde18:02:00

I did a deploy of my pedestal Uberwar, then connected to the repl port, my guess it's nRepl

overde18:02:17

now, the first strange thing, the Clojure's version at the console is different from my lein's projec.clj version

overde18:02:39

it shows 1.7.0 and over my project.clj is 1.10.0

hiredman18:02:26

you should check what jars are in your war, and check lein :version

overde18:02:50

is it expected to not be able to enter the name spaces?

masta18:02:27

can we sort maps by keywords alphabetically?

noisesmith18:02:58

this is what sorted-map and sorted-map-by are for, as long as all keys are sortable with one another

noisesmith18:02:28

(for a normal clojure map, the types of the keys are unrestricted, and you can't sort many combinations of types...)

chase-lambert21:02:43

Understanding destructurings seems to be one of the sticking points for me. In something like this:

(defn receive-treasure-location
  [{:keys [lat lng] :as treasure-location}]
  (println (str "Treasure lat: " lat))
  (println (str "Treasure lng: " lng))
So the map notation tells me it's going to be destructuring a map right? You use the same notation as the collection you expect? Then :keys means pull out all the keys and associate them with what? Is this automatically going to make lat the first key and lng the second key? But maps are unordered? And then :as means the whole map will still be bound as treasure-location?

lennart.buit21:02:06

You are saying here that you expect a map consisting of keys lat and long and you wish to call this map treasure-location

chase-lambert21:02:32

so you expect the map will only have two keys?

noisesmith21:02:42

you are right, the order doesn't matter, the :keys destructure is based on the keys themselves

hiredman21:02:43

it isn't ordered at all

lennart.buit21:02:10

there is no expectency, if there is a key :lat in the map, it will be bound to lat in your function, otherwise its nil

hiredman21:02:20

{:keys [lat lng]} binds (get ... :lat) to lat and (get ... :lng) to lng

chase-lambert21:02:28

but the keywords in the map used for that example are :lng and :lat not lng and lat

hiredman21:02:44

there is also :syms and :strs for other types of keys

lennart.buit21:02:39

oh thats really cool, didn’t know

chase-lambert21:02:08

what does :syms stand for?

lennart.buit21:02:15

symbols I’d assume

hiredman21:02:29

and :keys/:syms/:strs are just sugar on the general destructuring syntax, where the key can be any kind of literal

noisesmith21:02:19

user=> (let [{:strs [c d] :keys [a b] :syms [e f]} {:a 1 :b 2 :c 3 :d 5 :e 6 :f 7 "a" 8 "b" 9 "c" 10 "d" 11 "e" 12 "f" 13 'a 14 'b 15 'c 16 'd 17 'e 18 'f 19}] [a b c d e f])
[1 2 10 11 18 19]

hiredman21:02:25

it might even be easier to understand starting from the general destructuring form and understanding that, then looking at :keys as sugar on top of that, instead of trying to understand :keys in isolation

chase-lambert21:02:38

so in your destructuring part you don't use the : notation for the actual keys you just leave that out?

hiredman21:02:00

it is implicit

chase-lambert21:02:28

ok, ok. seems obvious i guess but I don't think a single tutorial has told my dumb butt that

hiredman21:02:50

it is implicit, the destructuring code understands that :keys [a b] means you want to lookup the keys :a and :b in the map and bind the values to the locals a and b

hiredman21:02:27

where as :syms [a b] means look up the symbols a and b in the map and bind the values to the locals a and b

hiredman21:02:44

the general destructuring syntax for maps is kind of the map inverted

hiredman21:02:03

{a :a} means look up the keys :a and bind the value found to the local a

lennart.buit21:02:42

Yeah, you can be explicit: (let [{my-a-key :a} {:a 12}] ...), is useful for renaming

hiredman21:02:04

user=> ((fn [{a #inst "2008"}] a) {#inst "2008" 1})
1
user=> 

hiredman21:02:42

:keys, :syms, and :strs are special cases (I think these days there is some additional stuff for namespaced keys which I haven't used yet)

lennart.buit21:02:12

Yeah there is, you can use ::keys for the current namespace or require/as a namespace and then use ::alias/keys

lennart.buit21:02:50

May I ask for my understading, these binding forms, are they “fixed”, or is it possible to define a new “shorthand” like :keys, :syms or :strs, for example :lowercase-strs or something strange like that.

lennart.buit21:02:11

(Not that I want to, but for complete understanding!)

lennart.buit21:02:06

Cool, so thats a no

chase-lambert21:02:01

thanks, folks. unrelated question: When reading other people's code I see idx used a lot. What idiom is that pertaining too?

lennart.buit21:02:41

sounds like index?

chase-lambert21:02:44

seems right. of course now looking at my notes for questions to eventually ask i didn't write an example so can't remember how it was used. i'm sure the context should have told me.

chase-lambert21:02:28

like when I see acc I'm starting to learn that means they are using this let binding for their accumulator

noisesmith22:02:19

more often it would be a function arg in reduce, or a loop binding