Fork me on GitHub
#beginners
<
2020-06-19
>
sergey.shvets00:06:18

Hi, is concat on lists in Clojure constant time? And is there a better function then concat if I don't need laziness?

noisesmith00:06:14

you can use the cat transducer or mapcat or for to concatenate as part of a data transform

noisesmith00:06:32

you can use into to concatenate some seqable onto the end of a vector

noisesmith00:06:37

concat can't be constant time as the inputs can be lazy and it creates a new list, so it can't do better than O(n)

noisesmith00:06:00

(unless I'm thinking about this wrong...)

noisesmith00:06:48

if you need fast concatenation, check out finger trees https://github.com/clojure/data.finger-tree

noisesmith00:06:13

come to think of it, concat returns instantly and does almost 0 work regardless of input size, it's just that it delays some calculations to be done later, once you actually access elements in the collection

noisesmith00:06:49

NB - don't mix eager code that combines collections with repeated nested calls to cat https://stuartsierra.com/2015/04/26/clojure-donts-concat

sergey.shvets00:06:38

Thanks! I just read this article by Stuart. I need to insert some elements in the middle of a list (I'm merging two lists where some nodes may be added, removed or modified). My idea was to split list at the node where I need to insert, use conj and concat and I somehow can't find the good way to solve it.

noisesmith00:06:58

(let [input [0 1 2 3 4 5]
      position 2
      [pre post] (split-at position input)]
  (concat pre [:inserted] post))

=> (0 1 :inserted 2 3 4 5)

noisesmith00:06:00

something like that?

sergey.shvets00:06:10

yes, but I need to be mindful of performance since lists may be quite big and have children that needs to be merged too. That's why I was looking towards linked lists

noisesmith00:06:39

lazy-seqs are linked lists, they just aren't mutable

noisesmith00:06:09

if you mean doubly-linked, that's what finger-trees offer (linked above) iirc

noisesmith00:06:31

of course you can use a mutable java doubly linked list and dump it into a clojure collection on return

noisesmith00:06:48

if nobody saw you mutate, you're pure :D

sergey.shvets00:06:19

is list structure is linked list too?

noisesmith00:06:46

concat reuses the structure of all its inputs, I should have been more clear about that

noisesmith00:06:06

very little copying is done by clojure collection ops, you don't need to copy as much when the data is immutable

sergey.shvets00:06:59

Thanks, that's very helpful. I'll give it a try and see how it works. Unless, I'm missing something I should be fine with just linked lists. No need for finger trees.

noisesmith00:06:06

internally, concat creates a generator that walks the first input, and then starts walking the next input when (if) it hits the end

noisesmith00:06:03

the "concat bomb" that the blog mentions is a danger when you call (concat (concat (concat ....))) and all the nested wrapping generators blow up the stack

sergey.shvets01:06:17

Yeah, I'll have some recursion, but it shouldn't be too deep. I don't think I come anywhere close to limit.

Chase01:06:22

How do you regex on a paren? I tried double escaping it from some googling but that's not matching either.

noisesmith01:06:13

(ins)user=> (re-seq #"\(" "((((()))))")
("(" "(" "(" "(" "(")

noisesmith01:06:16

regex literals use different escaping rules than regular clojure strings, and they use bare "()" for grouping

Chase01:06:45

lol, oops. I had a typo in another section of the regex. Yeah, I think it's time to call it a night. Thank you!

noisesmith01:06:17

that is, "\(" isn't a valid string, but #"\(" is the regex you get when you re-compile "\\("

Mark-James M.01:06:15

How do I create a binding for a vector? I've tried using (def var-name (value1 value 2)) but that doesn't seem to work

seancorfield01:06:12

Use [ .. ] for a vector.

seancorfield01:06:51

( .. ) is a list, which will be treated as a function call here (so it will attempt to call value1 passing value2 as its argument)

Mark-James M.01:06:59

I initially tried that but it didn't seem to work either

(def suits
  [1 2 3 4 5 6 7 8 9 10 Jack Queen])

andy.fingerhut01:06:18

What about that last code example did not seem to work for you?

andy.fingerhut01:06:53

Oh, Jack and Queen are symbols that will be evaluated, unless they are quoted to prevent their evaluation

Chase01:06:04

It's not going to know what Jack and Queen are.

andy.fingerhut01:06:36

So you could quote them, like ‘Jack

andy.fingerhut01:06:57

Or quote the whole vector like ‘[ ... ]

Chase01:06:07

You would want "Jack" or "Queen" or probably :Jack and :Queen I think

andy.fingerhut01:06:27

Or use keywords like :Jack instead of a symbol, because keywords evaluate to themselves

Mark-James M.03:06:17

Is there a reason why I don't need to specify vector here:

(def rank
  [1 2 3 4 5 6 7 8 9 10 :Jack :Queen :King :Ace])
but I do here:
(def suits (vector [:Diamonds :Clubs :Spades :Hearts]))
If I try
(def suits [:Diamonds :Clubs :Spades :Hearts])
They type is listed as clojure.lang.Var$Unbound

dpsutton03:06:16

i'm not able to recreate what you're seeing

~/p/clojure ❯❯❯ clj
Clojure 1.10.1
user=> (def suits [:Diamonds :Clubs :Spades :hearts])
#'user/suits
user=> suits
[:Diamonds :Clubs :Spades :hearts]
user=>

Mark-James M.03:06:15

It seems I needed to reload the REPL for some reason

dpsutton03:06:34

what is your repl environment like? how are you evaling forms?

Mark-James M.03:06:10

I'm using cursive with nREPL

dpsutton03:06:11

ok. do you know the keybinding to send top level form to the repl and send form before cursor to repl?

Mark-James M.03:06:00

I just found it, alt-shift-p

dpsutton03:06:25

yeah use those keybindings to evaluate forms as you go

Mark-James M.03:06:27

Previously I had been reloading the repl each time I modified a function

😲 6
dpsutton03:06:46

this should be far nicer for you

👍 3
dpsutton03:06:39

cmd-\ sends your cursor to the repl if you want to type and esc will put your cursor back in the code buffer. also, use (comment ) forms to type in the repl and eval them

Mark-James M.03:06:38

What would be the equivalent to mapping using a for loop? For example, I want to take one vector, and then for every item in that vector, map it to an item from another vector. I've found zipmap which seems to create maps from vectors

dpsutton03:06:16

can you give me some example inputs and desired output?

Mark-James M.03:06:41

(def rank
  [1 2 3 4 5 6 7 8 9 10 :Jack :Queen :King :Ace])

(def suits
  [:Diamonds :Clubs :Spades :Hearts])
Desired output (map):
1:Diamonds 2:Diamonds...

dpsutton03:06:44

(into {}
      (for [rank [1 2 3 4 5 6 7 8 9 :jack :queen :king :ace]
            suit [:diamonds :clubs :spades :hearts]]
        [rank suit]))
the for here does a list comprehension over rank and suit and makes a vector or tuple of rank and suit. into {} "pours" this collection of tuples into the map, which interprets them as key/value pairs and does the obvious thing

noisesmith13:06:45

this only preserves one suit for each rank

dpsutton13:06:26

Ha. Whoops.

Mark-James M.03:06:46

And I put this statement into a function?

dpsutton03:06:48

and in cursive, put your cursor on into and for and hit ^-j (control j) to see their doc strings. and you should hopefully have the clojure docs downloaded as well. the documentation story in cursive is very nice

👍 3
Mark-James M.03:06:34

Ah, this is super helpful

dpsutton03:06:55

depends on what you want. this is making a map of the cartesian product. you could make a function called that which you use to create a map of a deck of cards. or just def the deck of cards. what ever makes the most sense

Mark-James M.03:06:43

Just ran into a bug

Mark-James M.03:06:29

Managed to fix it by enabling EAP updates

parens 3
Spaceman05:06:55

I have two responses at two different bidi routes:

{:status 200, :headers {"Content-Type" "application/edn", "Access-Control-Allow-Headers" "Content-Type", "Access-Control-Allow-Origin" "", "Access-Control-Request-Method" "GET, OPTIONS", "Access-Control-Allow-Credentials" true}, :body "{:duration-wise-cost 0.5}"}
{:status 200, :headers {"Content-Type" "application/edn", "Access-Control-Allow-Headers" "Content-Type", "Access-Control-Allow-Origin" "", "Access-Control-Request-Method" "GET, OPTIONS", "Access-Control-Allow-Credentials" true}, :body "{:paid true}"}
In my browser the first one doesn't give an error but the second one gives a 406 not acceptable. Why would that be?

Spaceman05:06:14

Seems like the error is happening in the client side and is related to re-frame:

GET ?..... 406 (Not Acceptable)
goog.net.XhrIo.send @ xhrio.js:631
eval @ xhrio.cljs:32
ajax$protocols$_js_ajax_request @ protocols.cljc:6
ajax$simple$raw_ajax_request @ simple.cljc:64
ajax$simple$ajax_request @ simple.cljc:67
day8$re_frame$http_fx$http_effect @ http_fx.cljs:88
re_frame$fx$do_fx_after @ fx.cljc:76
re_frame$interceptor$invoke_interceptor_fn @ interceptor.cljc:71
re_frame$interceptor$invoke_interceptors @ interceptor.cljc:109
re_frame$interceptor$execute @ interceptor.cljc:204
re_frame$events$handle @ events.cljc:65
eval @ router.cljc:179
eval @ router.cljc:198
eval @ router.cljc:146
eval @ router.cljc:169
G__67540 @ router.cljc:187
channel.port1.onmessage @ nexttick.js:218

ryan09:06:57

A 406 would suggest the endpoint is not happy with Accept or similar headers value

Michael J Dorian11:06:33

Hello! Can anyone provide information on how to store data in a browser session from http-kit routes (routing with compojure)? I have a working api going and I'd like to add some really minimal authentication. Thanks!

Michael J Dorian11:06:05

The only reference to sessions I can find in the docs is an issue where they aren't working quite the way someone expects: https://github.com/http-kit/http-kit/issues/378

Michael J Dorian11:06:58

Oh nice, it probably will let me try it 😁

manutter5111:06:13

I remembered it being a Ring thing and did some quick googling for "ring session middleware"

Michael J Dorian11:06:01

Yeah I figured if it wasn't documented with http-hit it must somehow be obvious, and I guess the obviousness vector was that I should look for the ring docs!

Daniel Tan12:06:13

http kit websockets are a different story though

Daniel Tan12:06:26

not documented well enough, but the source code is documented

Lucy Wang13:06:34

is there any out of the box function/lib that could print a map in column wise manner, i.e. format {:k1 1 :k2 2} to something like this?

| :k1 | 1 |
| :k2 | 2 |

Lucy Wang13:06:09

would be very handy to look at the content at one glance

noisesmith13:06:14

pprint/print-table gives rows to each key in a vector of maps

(ins)user=> (clojure.pprint/print-table [{:a 0 :b 1} {:a 12 :b 42}])

| :a | :b |
|----+----|
|  0 |  1 |
| 12 | 42 |
nil

👍 3
noisesmith13:06:32

but what you want can easily be done as a one-liner with doseq

Lucy Wang13:06:32

thx, I knew that, but want something for a single map object ...

Lucy Wang13:06:08

and what's more it's headers on the top line, which is different from what I want

Lucy Wang13:06:36

e.g. when the screen is small it'll wrap around, which is not good

noisesmith13:06:42

oh you want right-alignment, that makes it harder

noisesmith13:06:54

short answer: nothing built in

Alex Miller (Clojure team)13:06:24

I always assume you can do anything with cl-format

noisesmith13:06:50

that's true, but cl-format is a turing complete programming language

Lucy Wang13:06:12

user=> (t/table (meta #'doc))
+-----------+---------------------------------------------------------------+
| key       | value                                                         |
+-----------+---------------------------------------------------------------+
| :macro    | true                                                          |
| :ns       | clojure.repl                                                  |
| :name     | doc                                                           |
| :arglists | ([name])                                                      |
| :added    | 1.0                                                           |
| :doc      | Prints documentation for a var or special form given its name |
| :line     | 120                                                           |
| :file     | clojure/repl.clj                                              |
+-----------+---------------------------------------------------------------+

noisesmith13:06:33

user=> (clojure.pprint/print-table (map (fn [[k v]] {'key k 'value v}) (meta #'doc))))

|       key |                                                                                              value |
|-----------+----------------------------------------------------------------------------------------------------|
|    :added |                                                                                                1.0 |
|       :ns |                                                                                       clojure.repl |
|     :name |                                                                                                doc |
|     :file |                                                                                   clojure/repl.clj |
|   :column |                                                                                                  1 |
|     :line |                                                                                                132 |
|    :macro |                                                                                               true |
| :arglists |                                                                                           ([name]) |
|      :doc | Prints documentation for a var or special form given its name,
   or for a spec if given a keyword |
nil

noisesmith13:06:59

that lib is smarter for sure (see what happened with the newline in the string)

Lucy Wang13:06:23

yeah, but the lib doesn't support cljs

noisesmith13:06:19

you could pre-process the v in the lambda to make it better behaved (give a max length, trim newlines...)

Lucy Wang13:06:49

cl-format is new to me, I'll take a look, thx guys

Lucy Wang13:06:50

(println (cl-format nil "~{~<~%~1,20:;~A~> ~}" word-wrap)) ... that's really mind-blowing ..

Lucy Wang13:06:15

I guess I will not touch it 😂

Mark-James M.13:06:15

Is there a reason why this is only generating a map from the :spades keyword in suit, and not for all the suits?

(def card-deck
  (into {}
        (for [rank [:Ace 1 2 3 4 5 6 7 8 9 10 :Jack :Queen :King]
             suit [:diamonds :clubs :spades :spades]]
          [rank suit])))

dpsutton13:06:29

i was too casual when helping you. a map can only hold one value for any particular key. so when rank is used as a key, there can only be one suit available. :jack can be in the map once. and therefore only one suit can be stored in this way

dpsutton13:06:50

@noisesmith pointed this out in a thread

noisesmith13:06:54

right, you don't want a map at all, probably a vector or set

dpsutton13:06:58

i'm not sure what representation you want but perhaps a set would work. in which case you can turn the {} into #{}

noisesmith13:06:02

or just the raw output of for

noisesmith13:06:19

also I think one of your :spades should be :hearts

dpsutton13:06:59

wow i get no gold stars on that example 🙂

3
Mark-James M.14:06:20

At least you tried, thats what counts the most :D

Mark-James M.13:06:03

Ah, I hadn't seen his reply

Milan Munzar15:06:44

Hey, 🙂 Do you guys know why this import fails core is not defined ? I am not sure what I am doing wrong. Thank you!

noisesmith15:06:56

if you require :as core, then core.foo is wrong, you need core/foo

noisesmith15:06:31

also, as someone doing maint. work on a project with multiple core.clj files, there's never a reason to name anything core

👍 6
Milan Munzar15:06:10

Thank you 🙂 That did it 😄

Jim Strieter18:06:08

I've been thinking of (defn someFunc [...] (reify (SomeProtocol ...))) as implementing a protocol. But is that the right way to think about reify?

Alex Miller (Clojure team)18:06:07

no, and it's not really good to do that

Alex Miller (Clojure team)18:06:35

SomeProtocol here is an interface generated as a side effect of the implementation of protocols

Alex Miller (Clojure team)18:06:30

relying on tapping into that is kind of going through the window instead of the door a bit (and not portable to cljs afaik)

Alex Miller (Clojure team)18:06:36

depending on exactly what you're doing, extending protocols via metadata (assuming you control it and can allow that) is probably better for this usage https://clojure.org/reference/protocols#_extend_via_metadata

Alex Miller (Clojure team)18:06:50

or actually making a defrecord/deftype that extends it

Jim Strieter20:06:06

Can you help me understand reify? Is thee any simple or plain language way to think about it?

hiredman18:06:28

I am not sure if there is some other context to the question I am missing, but SomeProtocol may refer to the protocol or may refer to the interface generated by defining the protocol, and it is tricky for beginners to tell which it is referring to in a given context. reify explicitly supports both protocols and interfaces

hiredman18:06:55

the verbs for protocols and interfaces are different, you can implement an interface, and you can satisfy a protocol

hiredman18:06:18

and you can satisfy a protocol by implementing the interface defining the protocol generated

hiredman18:06:57

which is how reify's protocol support works, given a name that refers to a protocol instead of an interface, it looks up the interface behind the protocol

Jim Strieter12:06:30

What does reify do?

hiredman14:06:17

Reify makes something abstract concrete. It makes an anonymous object which can implement some interfaces and satisfy some protocols

hiredman18:06:56

but it is always preferable to use the protocol over the interface directly.

hiredman18:06:10

as a beginner that is tricky to do because the same name can refer to either, so a rule of thumb is never use import for a name created by defprotocol, and never use a name with a "/" in it somewhere when referring to the protocol outside of the namespace where it was defined

hiredman18:06:13

(because import will import the java interface, and using '/' in a name ensures it goes through namespace resolution which will get you the protocol instance instead of java class resolution which will get you the interface)

hiredman18:06:37

the ambiguity of the name referring to the protocol or the interface can arise with using defrecord or deftype as well

dpsutton18:06:52

What about the example above indicated it was using the generated interface rather than the protocol?

noisesmith18:06:03

reify can't do protocols per se can it?

hiredman18:06:14

it definitely can

noisesmith18:06:28

oh I thought it just did the interface

hiredman18:06:35

the doc string says so and the code explicitly handles them

hiredman18:06:25

reify, defrecord, and deftype all handle "inline" protocol satisfaction in the same way

hiredman19:06:04

when given a protocol, they lookup the interface behind the protocol, and implement that interface, which in turns causes them to satisfy the protocol

hiredman19:06:24

nothing about the above example indicated confusion of protocols and intefaces(but nothing in the example rules it out either), but @alexmiller's response doesn't make sense to me unless you assume the example shows that confusion

hiredman19:06:43

which is why I started with "I am not sure if there is some other context to the question I am missing"

Alex Miller (Clojure team)19:06:06

sorry, I've clearly lost my mind :) I recant!

Alex Miller (Clojure team)19:06:30

I read it as the interface

AS21:06:45

Hey guys, noob question incoming. My editing environment is the spacemacs clojure layer (Cider, etc), and am trying to write a basic REST API using http-kit/ring/compojure. When I try to send my code to the CIDER repl, it keeps coming back with the error Could not locate org/httpkit/server__init.class, org/httpkit/server.clj or org/httpkit/server.cljc on classpath. However, lein classpath shows the dependencies just fine, and my code works when I hit lein run. Are there steps I need to take to get Cider to recognize the right classpath?

AS21:06:27

(And I've run cider-ns-refresh - it also fails).

seancorfield21:06:30

Did you start your REPL before adding httpkit as a dependency? If so, just restart your REPL now that you've updated project.clj and connect back in from Spacemacs and you should be good to go.

seancorfield21:06:05

There are ways to add dependencies to a running REPL but they're not really "beginner" level friendly 😐

AS21:06:24

Well that did it and fixed a few hours of frustration. Thank you so much! Minor follow-up question, the linter is still giving me a couple of errors, e.g. unresolved symbol defroutes. I'm not sure why since when I run this in the cider repl it definitely works. Any idea what's up?

seancorfield22:06:38

Is that clj-kondo?

seancorfield22:06:56

I've no idea how it would be set up in Spacemacs (I haven't used Emacs for years) but there's a #clj-kondo channel where they should be able to help. It may just be a matter of how you're requiring the namespace defroutes is in...

seancorfield22:06:01

Ah, yeah [compojure.core :refer :all] -- that's not very good practice. It's better to use an alias (`:as`) or explicitly list the referred-in symbols.

AS22:06:21

Got it. Thank you so much for all the help!

seancorfield22:06:08

[compojure.core :refer [defroutes GET POST]] is better and will satisfy the linter... well, once you are using POST in your code.

AS22:06:41

Perfect - I'm on my way. Appreciate it!