Fork me on GitHub
#beginners
<
2020-12-14
>
Ritchie Young01:12:27

I’m having trouble understanding Java interop. I was trying to convert this expression from the Java docs into Clojure but it’s not working so I’m obviously getting it wrong:

FileSystems.getDefault().getPath("logs", "access.log");
My best effort was:
(.getPath (java.nio.file.FileSystems/getDefault) "logs" "access.log")
Which earns me:
; eval (root-form): (.getPath (java.nio.file.FileSystems/getDefault) "logs" "...
; (err) Execution error (ClassCastException) at cli.core/eval1683 (form-init9547531439919407953.clj:49).
; (err) class java.lang.String cannot be cast to class [Ljava.lang.String; (java.lang.String and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
Any help getting me unstuck would be appreciated.

Dane Filipczak02:12:00

(.getPath (java.nio.file.FileSystems/getDefault) "logs" (into-array String ["access.log"]))

Dane Filipczak02:12:12

https://clojure.org/reference/java_interop#_vararg_methods “Java vararg methods treat the trailing varargs parameter as an array. They can be invoked from Clojure by passing an explicit array in place of the vargs.” you are def not the first to get bitten by this : )

Ritchie Young02:12:41

I’m glad I asked. I would have been there forever figuring that one out. Thanks very much, @dane.email.

noisesmith02:12:53

the official guide on interop is very short and does mention this, by the way https://clojure.org/reference/java_interop#_vararg_methods

Alex Miller (Clojure team)03:12:34

There is a longer faq entry btw

👍 3
Alex Miller (Clojure team)04:12:37

Probably should link those up

Jim Newton11:12:42

I have a macro which expands to lots of concentric (let [x# ~x] ...) constructs. When I look at the macro expansion I have lots of lets with the exact same symbol such as (let [value_19667__auto__ value_19667__auto__] ...) , in this case I could check in my macro and just omit the let (let [a b] ...) where (= a b) , and eliminate the let. Is this a redundant optimization? will the clojure compiler do this optimization for me?

Ben Sless11:12:28

It looks like the compiler doesn't optimize this for you. The JIT might, later. You can write a tree walking function or use meander to perform the inlining

noisesmith16:12:46

in general, clojure's compiler doesn't tend to optimize things (with a few notable exceptions like locals clearing)

Jim Newton10:12:50

OK, thanks. in this case I don't need a general purpose code walker, because the lets are begin generated by my own macro, so I know their limited form. So a very limited optimized-let macro is completely sufficient for my needs.

Jim Newton11:12:27

certainly the expanded macro is more humanly readable, if I expand to code eliminating this useless let.

Jim Newton11:12:50

(defmacro optimized-let [[a b] c]
  (if (= a b)
    c
    `(let [~a ~b] ~c)))

Jim Newton11:12:59

sorry if that question doesn't make sense...

Jim Newton12:12:16

Is there a way to write a unit test for a function defined by (defn- ...) ? certainly it is a good idea to test internal function. right?

bronsa12:12:06

you can access private functions by var

bronsa12:12:23

instead of (ns/private-f you can do (@#'ns/private-f

3
Jim Newton12:12:38

do I have to use the real ns name, or can I use an abbreviation?

bronsa12:12:14

you can use an alias

3
Michaël Salihi17:12:37

Hi, what the best way to filter the first large maps contains in a list or vector? Do I have to use a loop and acc or filter + predicate is possible? Eg. Turn this collection of maps

(def mymap '({:name "Susan", :id "3", :age "20", :weight "120", :Cool true, :height "48"}
             {:name "Bob", :id "2", :age "23", :weight "90", :Cool false, :height "50", :parent "yes"}
             {:name "John", :id "1", :age "21", :weight "150", :Cool true, :height "45", :parent "yes"}
             {:name "Ben", :id "4", :age "20", :weight "100", :Cool true, :height "43"}))
and return {:name "Bob", :id "2", :age "23", :weight "90", :Cool false, :height "50", :parent "yes"}

dpsutton17:12:16

if you're looking for the map who's name is "Bob" you can

(filter (comp #{"Bob"} :name)
  '({:name "Susan", :id "3", :age "20", :weight "120", :Cool true, :height "48"}
    {:name "Bob", :id "2", :age "23", :weight "90", :Cool false, :height "50", :parent "yes"}
    {:name "John", :id "1", :age "21", :weight "150", :Cool true, :height "45", :parent "yes"}
    {:name "Ben", :id "4", :age "20", :weight "100", :Cool true, :height "43"}))
just filter the records and take the first

dpsutton17:12:48

or use some

(some (fn [x] (when (= "Bob" (:name x)) x))
  '({:name "Susan", :id "3", :age "20", :weight "120", :Cool true, :height "48"}
    {:name "Bob", :id "2", :age "23", :weight "90", :Cool false, :height "50", :parent "yes"}
    {:name "John", :id "1", :age "21", :weight "150", :Cool true, :height "45", :parent "yes"}
    {:name "Ben", :id "4", :age "20", :weight "100", :Cool true, :height "43"}))
{:name "Bob",
 :id "2",
 :age "23",
 :weight "90",
 :Cool false,
 :height "50",
 :parent "yes"}

noisesmith17:12:23

also, minor note, it's almost always better to use [{.. .. } ...] instead of '({... ...} ...) - we typically only use ' in clojure when we need unresolved symbols, which is not the case here

👍 6
bnstvn17:12:47

> also, minor note, it’s almost always better to use `[{.. .. } ...]` instead of `'({... ...} ...)`  - we typically only use `'` in clojure when we need unresolved symbols, which is not the case here is this always the case, even if you know beforehand you only need sequential access?

noisesmith17:12:38

both style wise and performance, there's zero advantage to using ' to create lists

👍 3
noisesmith17:12:15

it's a lispism, and I think people do it because they are used to it from other lisps? it usually isn't needed in clojure

dpsutton17:12:19

and this is just for literals just to be clear

noisesmith17:12:47

right - none of this makes any sense if you aren't talking about data literals

noisesmith17:12:54

but even when constructing a collection, if you aren't writing a macro you probably shouldn't be using lists

noisesmith17:12:39

(nb. lazy-seqs as returned by map, filter, etc. are not lists)

dpsutton17:12:30

and if you're wondering one reason, is that the quote can do way more than just what you intend ("just give me the list of these things")

foo> '(1 2 (+ 1 2))
(1 2 (+ 1 2))
foo> [1 2 (+ 1 2)]
[1 2 3]
foo> 

👍 3
Michaël Salihi17:12:34

In fact, i want to be able to search and filter the largest hashmap so by its size and not its content

dpsutton17:12:16

size being the number of key value pairs?

noisesmith17:12:30

(->> mymap (sort-by count) last ...)

Michaël Salihi18:12:46

Yes, the count of elements. Alright, thx @noisesmith!

Michaël Salihi18:12:07

Thank both for the advices.

noisesmith18:12:13

oops - that first should be last (fixing...)

Michaël Salihi18:12:23

For the eg., I use list instead vector because it's like that that the precedent function output in code. But manually, I always a vector of hashmaps.

dpsutton17:12:32

can you rephrase your question? "filter the first large maps contains in a list or vector?". You have a collection of maps and you want to filter them?

Stuart19:12:03

How can I update an element in a map that is itself a map. e.g.

(let [foo {:bar 5 :quax {:abc 6 :def 7}}]
  ; I want to update :def in foo
  )
?

noisesmith19:12:00

@qmstuart update-in is the function that does that

noisesmith19:12:01

user=> (update-in {:bar 5 :quax {:abc 6 :def 7}} [:quax :def] inc)
{:bar 5, :quax {:abc 6, :def 8}}
it takes a one arg function that updates the value at that path

seancorfield20:12:49

The function can take an arbitrary number of arguments but the value being updated is passed as the first argument. /pedant

seancorfield20:12:25

dev=> (update-in {:bar 5 :quax {:abc 6 :def 7}} [:quax :def] + 42)
{:bar 5, :quax {:abc 6, :def 49}}

👍 3
Stuart19:12:36

thank you! thats perfect

noisesmith19:12:45

if you don't always know the path, or need to update multiple leaves, then clojure.walk helps

Stuart20:12:25

So I don't have a functino for the last param, just a value.

(update-in memory [:memory location]
                (Integer/parseInt (->> (map (fn [m v] (if (= \X m) v m)) mask (pad v))
                                       (apply str)
                                       2)))
It errors because float can't be converted to a function, because the Integer/parseInt returns a number (which it evaluates eagerly?), Im guessing, and its trying to call that number as a function?

dpsutton20:12:45

read the docstrings of update-in and assoc-in

dpsutton20:12:15

but if you just use assoc-in here it will work. but understanding the differences will greatly ease your Clojure studies

tanzoniteblack20:12:05

https://github.com/plumatic/plumbing is this still the generally recommend library for creating computation graphs in clojure? I'm looking at improving the start up time of our API, which pre-loads of a bunch of metadata. A number of the metadata caches depend on each other, and while I could manually create a dependency order to parallelize the startup efforts, but a graph type approach that auto-computes the parallelization would be more flexible & maintainable.

dgb2321:12:35

This is the first time I come across this concepts, thank you for sharing!

Mark22:12:12

Hey folks, is there a simple way to implement a lazy seq where the nth term is defined as f(n-1)?

phronmophobic22:12:28

(->> (range) (map dec))

phronmophobic22:12:47

whoops. this should probably be

(->> (range) (map dec) (map f))

dpsutton22:12:48

(iterate f initial-value)

dpsutton22:12:01

> Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects

🙌 3