This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-03-27
Channels
- # announcements (2)
- # aws (31)
- # babashka (81)
- # beginners (82)
- # calva (38)
- # clj-kondo (41)
- # cljdoc (4)
- # cljs-dev (6)
- # clojure (101)
- # clojure-belgium (1)
- # clojure-europe (30)
- # clojure-germany (1)
- # clojure-italy (7)
- # clojure-nl (4)
- # clojure-norway (1)
- # clojure-spec (1)
- # clojure-uk (19)
- # clojurescript (16)
- # clojutre (1)
- # community-development (26)
- # core-logic (2)
- # data-science (26)
- # datomic (71)
- # events (3)
- # fulcro (55)
- # graalvm (2)
- # graphql (3)
- # joker (2)
- # kaocha (19)
- # luminus (2)
- # malli (6)
- # meander (3)
- # off-topic (6)
- # pathom (34)
- # random (1)
- # re-frame (2)
- # robots (1)
- # shadow-cljs (37)
- # sql (30)
- # tools-deps (21)
- # xtdb (4)
- # yada (25)
ClojureD talks are out! 🎉 https://www.youtube.com/playlist?list=PLaSn8eiZ631lrDFmUTBH9LFGzAxc3tvU4
#graalvm ?
I was thinking how, given that (a-map :a-keyword)
is same as (:a-keyword a-map)
it’s weird how we picked (sub-thing big-thing)
form as canonical instead of (big-thing sub-thing)
especially when you consider that map and function are synonyms
I'd never do (k m)
I'd always use (get m k)
in that case. If I knew the map, not the k, I'd do ({:foo 1} k)
.
good stuff
For Macros. I always hesitate to jump to them if possible. My question is, what is the opinion on resorting to a macro just to reduce boilerplate code? e.g instead of having to write the following over and over:
; try several-line-of-logic
; catch
; log-something
; do-something-else
we encapsulate in a macro?you're not resorting, that's what macros are for
Follow up Q: what are the things we should look out for when using macros? I read a lot about abuse of macros.
How much boilerplate would it save you? Doesn't seem like much from the example you gave
pretend that several-line-of-logic
, log-something
and do-something-else
are 2x more each.
Another reason in favor of using a macro for something like that is if you believe you will have dozens of occurrences of it, and you believe you are likely to want to change the behavior of all of them in a consistent way, via changing the macro definition later.
Look out for using patterns that break the IDE (cursive). For it to work with Cursive, it usually needs to be similar to an existing built in form, like def, defn, let, etc
I will keep an eye out for that, isak.
I don’t show phantomjs
but the general idea is outlined here: https://betweentwoparens.com/clojurescript-test-setup#headless-browser-testing - maybe it will guide a little.
What is the idiomatic way to make into
safe for both lists and vectors/sets?
(let [xs '(1 2 3 4)]
(into (empty xs) xs)) ; => (4 3 2 1)
(let [xs [1 2 3 4]]
(into (empty xs) xs)) ; => [1 2 3 4]
don't use empty?
That is closer to what I’m doing, are you saying there’s a way to use into
without empty
or that I should be avoiding the need for lists to use it at all?
just use [] ?
why are you trying to retain the type of xs
are you really using sets?
in general, I would be suspicious of that function above - it's way better to just say (map my-transform xs) at the place you need it
In my case I’m modifying some value at a user provided path but the function applies the path operation to all items in collections like lists/vectors/sets if it’s encountered. IE I don’t have control of the data and the structure should be retained in the return
Here is the function with the specifics removed:
(defn upper-case-path
[m [k & rest-paths]]
(if-some [v (get m k)]
(cond
(and (empty? rest-paths)
(string? v))
(assoc m k (clojure.string/upper-case v))
(sequential? v)
(->> (map #(upper-case-path % rest-paths) v)
(into (empty v))
(assoc m k))
(map? v)
(assoc m k (upper-case-path v rest-paths))
:else m)
m))
(upper-case-path {:foo {:bar [{:baz "hello"}]}}
[:foo :bar :baz])
; => {:foo {:bar [{:baz "HELLO"}]}}
if you really need that level of control, then you might have to write custom per-coll type code. in general, I find anything like that to smell kind of fishy
I guess in this case my sequential?
check wouldn’t even catch sets, so I could just always put lists into vectors and handle sets in a separate cond
case
@U0CLLU3QT checkout #specter
(sp/transform sp/ALL inc coll)
will do (0 1) => (1 2)
, [0 1] => [1 2]
and #{0 1} => #{1 2}
as you expect
(defn upper-case-path
[coll & paths]
(sp/transform (sp/multi-path
(for [path paths]
(interleave path (repeat (sp/if-path coll? sp/ALL sp/STAY)))))
string/upper-case
coll))
@U2J4FRT2T haven’t used specter before but that snippet is making me want to
after #specter, upper-case-path
is the kind of function that I don't write
I just write (sp/transform [:the :path :i :want] the-function-i-want the-data-i-know)
It solve some problems when you can't control your data
Once you get in control of your data, this kind of function that "fixes your data inplace" dosen't make sense anymore.
Why would you transform :app.user/name
in upper case? Just create a :app.user/name-in-uppercase
That’s just the example I’m using, in the real usecase they’re transforms applied before being stored somewhere, so the keys matter
I'm trying to implement a record that will have multiple fields and a context map that consumers can use as a regular map. Something like (defrecord A [private-field1 private-field2 context])
and I want all (assoc record :field val)
to assoc to context
field.
I figured that it should be easy to do a deftype and implement IPersistentMap, but can't find a reference of all the methods required for IPersistentMap. Can someone point me into the right direction or how to implement a thing in the first paragraph with records? Thanks
I don't care about private fields, it is just I don't know all the fields in the record in advance
so I do something like (defrecord A []) ?
I'll have a couple of fields that will be always present and a bunch of helper methods to modify them. I thought that I will put them in an interface and implement those using a record. I want to make sure that consumers can't mess up with those fields in any way.
I can leave them as namespaced keywords, but then it will be hard to consume these records with "filter" or any other methods, since you will always need to keep those extra fields in mind
when doing require, is there a subtractive form for :refer :all? for example '[prelude.core :refer :all-but [something-not-needed]]
:exclude
, I think?
Yeah -
(require '[clojure.string :refer :all :exclude [lower-case]])
WARNING: reverse already refers to: #'clojure.core/reverse in namespace: user, being replaced by: #'clojure.string/reverse
WARNING: replace already refers to: #'clojure.core/replace in namespace: user, being replaced by: #'clojure.string/replace
=> nil
(lower-case "ABC")
Syntax error compiling at (/private/var/folders/lb/q65kb_ys1g71l4n2fj7m8jvh0000gn/T/form-init7403812543420763679.clj:1:1).
Unable to resolve symbol: lower-case in this context
(require '[clojure.string :refer :all])
=> nil
(lower-case "ABC")
=> "abc"
Is there a way to add a namespace to fields in a record?
you can use any key for fields, but only un-namespaced keywords for basis fields
@bear-z, I don't have a lot of experience with trying to do what you described further above, but I have to say that it seems unnatural in Clojure to use an interface to enforce an API. It seems more natural to use a regular map with namespaced fields for the required fields, and use spec to enforce the API if needed. I'm not sure what you meant about difficulties consuming the records with filtering.
@UBRMX7MT7 Yeah, I don't like the resulting code at all. It feels complicated and unnecessary. I guess the alternative is just to have a bunch of methods to work on a specific map.
(ins)user=> (defrecord Foo [a b])
user.Foo
(ins)user=> (map->Foo {:a 0 :b 1 :some.important/key "OK" [1 2 3] true})
#user.Foo{:a 0, :b 1, :some.important/key "OK", [1 2 3] true}
if you need validation, one option is to add a validator to the container holding the data
(ins)user=> (def foo 1)
#'user/foo
(ins)user=> (set-validator! #'foo number?)
nil
(ins)user=> (def foo 2)
#'user/foo
(ins)user=> foo
2
(ins)user=> (def foo :b)
Execution error (IllegalStateException) at clojure.main/main (main.java:40).
Invalid reference state
(ins)user=> foo
2
this also works on agents/refs/atoms
but importantly this isn't a property of the data object (that's immutable) but a property of the container enforcing the data objects it can contain
Thanks @noisesmith. That should work.
what is the proper way to get rid of reflection here?
(let [fs (java.nio.file.FileSystems/newFileSystem (.toPath (io/file "bb.zip")) nil)
to-extract (.getPath fs "bb" (into-array String []))]
(java.nio.file.Files/copy to-extract (.toPath (io/file "bb"))
^"[Ljava.nio.file.CopyOption;"
(into-array java.nio.file.CopyOption [])))
The double mention of java.nio.file.CopyOption feels verbose, but removing either results in a reflection warning or ClassCastExceptionone would think into-array with an explicit class would not need an annotation
into-array is just a function, so it isn't like the compiler determines functions return types from Class object's you pass in to the function
no idea, just that when trying to use java libs, it sort of removes all the beauty of the language
it gets a little bit uglier than in Java itself because of the into-array stuff for passing varargs
Would make-array
work a little better here? or no?
(make-array java.nio.file.CopyOption 0)
=> #object["[Ljava.nio.file.CopyOption;" 0x3fd7ca93 "[Ljava.nio.file.CopyOption;@3fd7ca93"]
Wonder if that’ll still generate the reflection warning…
What I tend to do in situations like this is hide the creation off in a function somewhere and type hint the function return. That at least makes the code using the function result clean.
https://github.com/seancorfield/next-jdbc/blob/master/src/next/jdbc/prepare.clj#L78-L80 (I'm open to discussion on whether using class
and :tag
here is more readable than `^"L...;")
It's useful to express the return type of the function in terms of the arguments to the function.
they a means to communicate type information to the compiler, and since generics don't exist, the compiler doesn't need them
I don't need generics. I need either a into-array for all possible arrays, or a way for into array to produce compiler information based on arguments.
a macro would cover some subset of into-array uses, because into-array can be passed a non-class literal, e.g. (let [a String] (into-array a []))
which a macro is going to have trouble with
it would be an interesting use case for a reader macro - create the array at a set size with hints to the compiler, eg.
#array-of[String 12]
it would not make much sense to privide literal contents though(?)