This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-19
Channels
- # announcements (9)
- # babashka (11)
- # beginners (157)
- # calva (10)
- # cider (18)
- # clara (4)
- # clj-kondo (40)
- # cljsrn (8)
- # clojure (29)
- # clojure-europe (11)
- # clojure-italy (1)
- # clojure-nl (2)
- # clojure-spec (4)
- # clojure-sweden (1)
- # clojure-uk (39)
- # clojurescript (32)
- # conjure (1)
- # core-async (2)
- # cursive (20)
- # datomic (7)
- # duct (9)
- # emacs (1)
- # figwheel-main (1)
- # fulcro (24)
- # helix (1)
- # hoplon (20)
- # hugsql (3)
- # jackdaw (5)
- # jobs-discuss (7)
- # lambdaisland (1)
- # malli (5)
- # music (4)
- # off-topic (54)
- # parinfer (2)
- # pedestal (13)
- # re-frame (12)
- # reagent (22)
- # reitit (9)
- # shadow-cljs (89)
- # spacemacs (2)
- # xtdb (21)
Hi, is concat
on lists in Clojure constant time? And is there a better function then concat
if I don't need laziness?
you can use the cat
transducer or mapcat
or for
to concatenate as part of a data transform
you can use into
to concatenate some seqable onto the end of a vector
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)
(unless I'm thinking about this wrong...)
if you need fast concatenation, check out finger trees https://github.com/clojure/data.finger-tree
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
NB - don't mix eager code that combines collections with repeated nested calls to cat https://stuartsierra.com/2015/04/26/clojure-donts-concat
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.
(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)
something like that?
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
lazy-seqs are linked lists, they just aren't mutable
if you mean doubly-linked, that's what finger-trees offer (linked above) iirc
of course you can use a mutable java doubly linked list and dump it into a clojure collection on return
if nobody saw you mutate, you're pure :D
is list
structure is linked list too?
concat reuses the structure of all its inputs, I should have been more clear about that
very little copying is done by clojure collection ops, you don't need to copy as much when the data is immutable
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.
internally, concat creates a generator that walks the first input, and then starts walking the next input when (if) it hits the end
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
Yeah, I'll have some recursion, but it shouldn't be too deep. I don't think I come anywhere close to limit.
How do you regex on a paren? I tried double escaping it from some googling but that's not matching either.
(ins)user=> (re-seq #"\(" "((((()))))")
("(" "(" "(" "(" "(")
regex literals use different escaping rules than regular clojure strings, and they use bare "()" for grouping
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!
that is, "\(" isn't a valid string, but #"\(" is the regex you get when you re-compile "\\("
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
Use [
.. ]
for a vector.
(
.. )
is a list, which will be treated as a function call here (so it will attempt to call value1
passing value2
as its argument)
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])
What about that last code example did not seem to work for you?
Oh, Jack and Queen are symbols that will be evaluated, unless they are quoted to prevent their evaluation
So you could quote them, like ‘Jack
Or quote the whole vector like ‘[ ... ]
Or use keywords like :Jack instead of a symbol, because keywords evaluate to themselves
Thanks!
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$Unboundi'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=>
It seems I needed to reload the REPL for some reason
I'm using cursive with nREPL
ok. do you know the keybinding to send top level form to the repl and send form before cursor to repl?
I just found it, alt-shift-p
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
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
(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...
(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 thingthis only preserves one suit for each rank
And I put this statement into a function?
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
Ah, this is super helpful
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
Just ran into a bug
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?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
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!
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
Oh nice, it probably will let me try it 😁
I remembered it being a Ring thing and did some quick googling for "ring session middleware"
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!
http kit websockets are a different story though
not documented well enough, but the source code is documented
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 |
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
but what you want can easily be done as a one-liner with doseq
oh you want right-alignment, that makes it harder
short answer: nothing built in
I always assume you can do anything with cl-format
that's true, but cl-format is a turing complete programming language
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 |
+-----------+---------------------------------------------------------------+
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
that lib is smarter for sure (see what happened with the newline in the string)
you could pre-process the v
in the lambda to make it better behaved (give a max length, trim newlines...)
now it works with cljs 🙂 https://github.com/cldwalker/table/pull/10
(println (cl-format nil "~{~<~%~1,20:;~A~> ~}" word-wrap))
... that's really mind-blowing ..
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])))
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
@noisesmith pointed this out in a thread
right, you don't want a map at all, probably a vector or set
i'm not sure what representation you want but perhaps a set would work. in which case you can turn the {}
into #{}
or just the raw output of for
also I think one of your :spades should be :hearts
At least you tried, thats what counts the most :D
Ah, I hadn't seen his reply
Hey, 🙂
Do you guys know why this import fails core is not defined
? I am not sure what I am doing wrong. Thank you!
if you require :as core
, then core.foo
is wrong, you need core/foo
also, as someone doing maint. work on a project with multiple core.clj
files, there's never a reason to name anything core
Thank you 🙂 That did it 😄
I've been thinking of (defn someFunc [...] (reify (SomeProtocol ...))) as implementing a protocol. But is that the right way to think about reify?
no, and it's not really good to do that
SomeProtocol here is an interface generated as a side effect of the implementation of protocols
relying on tapping into that is kind of going through the window instead of the door a bit (and not portable to cljs afaik)
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
or actually making a defrecord/deftype that extends it
Can you help me understand reify? Is thee any simple or plain language way to think about it?
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
the verbs for protocols and interfaces are different, you can implement an interface, and you can satisfy a protocol
and you can satisfy a protocol by implementing the interface defining the protocol generated
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
What does reify do?
Reify makes something abstract concrete. It makes an anonymous object which can implement some interfaces and satisfy some protocols
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
(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)
the ambiguity of the name referring to the protocol or the interface can arise with using defrecord or deftype as well
What about the example above indicated it was using the generated interface rather than the protocol?
reify can't do protocols per se can it?
oh I thought it just did the interface
reify, defrecord, and deftype all handle "inline" protocol satisfaction in the same way
when given a protocol, they lookup the interface behind the protocol, and implement that interface, which in turns causes them to satisfy the protocol
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
which is why I started with "I am not sure if there is some other context to the question I am missing"
sorry, I've clearly lost my mind :) I recant!
I read it as the interface
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?
Following this tutorial: https://medium.com/swlh/building-a-rest-api-in-clojure-3a1e1ae096e
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.
There are ways to add dependencies to a running REPL but they're not really "beginner" level friendly 😐
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?
Is that clj-kondo?
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...
Yes, I think it's related to this https://github.com/borkdude/clj-kondo/issues/342.
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.
[compojure.core :refer [defroutes GET POST]]
is better and will satisfy the linter... well, once you are using POST
in your code.