Fork me on GitHub
#beginners
<
2020-10-15
>
zackteo05:10:56

Hello, could someone explain to me what happens when I do ...

( #(if string?
     (clojure.string/join (reverse %))
     (reverse %)) [:foo :bar :foo])

zackteo05:10:15

I understand what I want is with do

( #(if string?
     (do clojure.string/join (reverse %))
     (do reverse %)) [:foo :bar :foo])

mavbozo06:10:29

do here executes the expressions inside one-by-one then returns the result of the last expression.

mavbozo06:10:15

so, (do clojure.string/join (reverse %)) executes clojure.string/join then executes and return the value from`(reverse %)`

zackteo06:10:28

But I would like to just understand the how first ends up going to the true branch to produce ":foo:bar:foo"

mavbozo06:10:00

maybe you want this in the first code

(#(if (string? %)
    (clojure.string/join (reverse %))
    (reverse %)) [:foo :bar :foo])

mavbozo06:10:55

as for the first version, everything in clojure is true except nil or false

zackteo06:10:29

what's the part that get evaluated as true there

mavbozo06:10:35

(if string? true false) => true

mavbozo06:10:48

string? is considered as true

mavbozo06:10:17

it's called nil-punning

zackteo06:10:00

wait i heard that before but could you summarise what that is exactly ?

zackteo06:10:27

like that all values other than nil and false are true?

mavbozo06:10:42

yes, that's what I understand

zackteo06:10:09

so this is a case of nil-punning? - > string? is considered as `true`

zackteo06:10:59

But yes thanks 😄

mavbozo06:10:58

on second thoughts, i'm not sure your case string? can be considered as nil-punning`

mavbozo06:10:24

your string? case just shows the truthiness concept in clojure

zackteo06:10:14

okay! let me read up on it

dehli20:10:42

basically string? is a function which is always truthy (which is what your if is evaluating for. in order to invoke it you need to wrap it in () which is why (string? %) behaves like you’d expect (it returns a true of false depending on whether or not % is a string)

michele mendel06:10:24

This is straightforward

((partial reverse) [3 2 1])
But why does this produce the same result?
((partial reverse [3 2 1]))

zackteo06:10:14

@michelemendel I believe thatt that includes [3 2 1] into reverse to make a new function. So when you call that function it will always execute reverse [3 2 1] - like a function with zero arguments

zackteo06:10:19

in the first case, (partial reverse) produces a new function that literally does the same thing as reverse (since you don't add in anymore arguments

michele mendel06:10:54

So in the second case, the reverse has already been done when it reaches partial?

mavbozo06:10:57

in second case, your ((partial reverse [3 2 1])) becomes

((fn []
   ([] (reverse [3 2 1]))
   ([x] (reverse [3 2 1] x))
   ([x y] (reverse [3 2 1] x y))
   ([x y z] (reverse [3 2 1] x y z))
   ([x y z & args] (apply reverse [3 2 1] x y z args))))

mavbozo06:10:21

there's 0 arg there so, (reverse [3 2 1) executed

michele mendel06:10:06

I see. This is from the definition of partial, right?

mavbozo06:10:23

yes, that's from partial source code

zackteo06:10:03

yeap the function you effectively produce is kinda like

(fn []
     reverse [3 2 1])

michele mendel06:10:53

Yes, because this actually works

(partial 3) ;=> 3

zackteo06:10:40

Not sure when you would use that but is a similar idea to (partial +) which produces the + function again I guess

michele mendel06:10:23

I think this to make it possible to combine it.

michele mendel06:10:05

This is where some understanding of category theory is useful.

zackteo06:10:32

hmmm, i guess edge cases and combining of higher order functions perhaps 😮

michele mendel06:10:18

Yes, so in these cases, it look strange taken at face value

Jim Newton06:10:48

cans someone remind how to force a lazy sequence to be non-lazy. It is really difficult to debug a (for ...) loop with too many lazy sequences.

Cameron06:10:23

doall

✔️ 6
Jim Newton06:10:53

its not clear from the documentation whether doall returns the sequence.

elarouss08:10:40

Hello, how can i set the value of a static field in an abstract java class? i want to set the api key for Stripe: Stripe.apiKey = "…"

delaguardo08:10:56

`(set! (. Classname-symbol staticFieldName-symbol) expr)` https://clojure.org/reference/java_interop#_alternative_macro_syntax

elarouss08:10:10

thank you, it worked 🙏

delaguardo09:10:33

there is also a builder for stripe’s request with method setApiKey might be a little bit more idiomatic solution

Jim Newton08:10:30

Is there an equivalent of Common Lisp maplisthttp://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm`mapcon` in clojure? I.e., maping functions which iterate over cons cells rather than the contents of cons cells? this is very useful when you need to compute something based on an element but also based on elements you'll see later. E.g. checking whether a list is in order, or removing successive duplicates, or checking the list of vertices of a polygon to detect right turns vs left turns. Unfortunately the function only make sense for lists, not for generalized sequences.

Jim Newton08:10:42

The reason I need such a function now is I want to scan a list with a given binary predicate to determine whether any two consecutive elements of the sequence satisfy the predicate. E.g., given (1 2 3 4 16 5 6 7) find two concecutive elements (a,b) such that b = a^2 , and if so replace (4 16) with some new merged value computed by a given function.

delaguardo09:10:00

you can craft such function youself

(defn foo [f xs]
  (let [xs' (partition 2 1 xs)]
    (map (fn [pair]
           (f pair))
         xs')))

Jim Newton09:10:06

yes in lisp it is pretty easy to write such special purpose functions. but in Common Lisp these function are as friendly as mapcar (CL version of map) which take multiple arity functions such as (map f l1 l2 l3 l4) which expects f to be 4-ary simply because i've given 4 arglists. BTW this is a cool feature of clojure which is pretty difficult to do in Scala.

Jim Newton09:10:52

For the case in point, I need to remove duplicate elements from a list. but not all duplicates, just the ones of a special form. I.e., duplicate elements x such that (f x) is true for some given f

Jim Newton09:10:44

the second case is I need to replace a succession of (a b) with either a or b or leave it as (a b) depending on another predicate function. This is part of a symbolic algebraic reduction phase.

Jim Newton09:10:42

in CL I'd use this cons cell iterator, and in each case look at the first and second elements of the list. and emmit (in a mapcat fashion the elements of the resulting list.

Jim Newton09:10:09

I can understand why these functions might not be in clojure, because they are very list specific, they don't apply to other non-list sequences.

Jim Newton09:10:31

but you're right that in lisp you can very often write the function you need as if it were built into the language.

Ben Sless12:10:59

To remove duplicates by predicate you can write something like:

(defn distinct-by
  ([f]
   (fn [rf]
     (let [seen (volatile! #{})]
       (fn
         ([] (rf))
         ([result] (rf result))
         ([result input]
          (let [k (f input)]
            (if (contains? @seen k)
              result
              (do (vswap! seen conj k)
                  (rf result input)))))))))
  ([f coll]
   (sequence (distinct-by f) coll)))

(distinct-by #(mod % 3) [1 5 3 4 2 6])
Which is just a modified version of distinct

Ben Sless12:10:05

to map over pairs of the original sequence, perhaps a transducer will lend itself well

Ben Sless12:10:28

(transduce
 (comp
  (partition-all 2)
  (map select-a-or-b))
 conj
 []
 xs)

Ben Sless12:10:12

The CL implementation is very tied to the cons cell abstraction, but Clojure is more generic sequence oriented and has a very large collection of sequence manipulation functions which compose well

3
Jim Newton11:10:20

is thrown supposed to work screwy like this? The compiler claims thrown? cannot be used within (not ...)

(deftest t-invalid-type
  (testing "for invalid type within rte"
    (with-compile-env ()
      (is (thrown? Exception (canonicalize-pattern '(not (:or String Number)))) "test 0")
      (is (thrown? Exception (canonicalize-pattern '(not (or String Number)
                                                         (or Long Number)))) "test 1")
      (is (thrown? Exception (canonicalize-pattern '(and (:or String Number)
                                                         (:or :sigma)))) "test 2")
      (is (thrown? Exception (canonicalize-pattern '(or (:and String Number)))) "test 3")
      (is (not (thrown? Exception (canonicalize-pattern '(:or :epsilon (:cat Integer (:* Integer))))))
          "test 4")
)))

delaguardo11:10:07

thrown? is not a “normal” function it is a form of assert-expr https://github.com/clojure/clojure/blob/28efe345d5e995dc152a0286fb0be81443a0d9ac/src/clj/clojure/test.clj#L504-L516 and it is trivial to create custom not-thrown? extension as a method of assert-expr

delaguardo11:10:11

(defmethod clojure.test/assert-expr 'not-thrown? [msg form]
  ;; (is (not-thrown? c expr))
  ;; Asserts that evaluating expr not throws an exception of class c.
  (let [klass (second form)
        body (nthnext form 2)]
    `(try ~@body
          (do-report {:type :pass, :message ~msg,
                      :expected '~form, :actual nil})
          (catch ~klass e#
            (do-report {:type :fail, :message ~msg,
                        :expected '~form, :actual e#})
            e#))))
didn’t test it though

Jim Newton12:10:07

is that the normal way I'm supposed to test whether a certain condition is not thrown?

Jim Newton12:10:10

I suggested some time ago that clojure test needs an is-not which is like is but logically reverses the expectation.

delaguardo12:10:11

ask around, i’m not claiming this to be “normal” way)

Jim Newton12:10:11

thus. (is-not (thrown? Exception....) and (is-not (my-own-predicate ...))

Jim Newton12:10:19

It could be that I know enough clojure now to implement is-not and submit it as a patch.

delaguardo12:10:04

ask core members about it first. unlikely this patch will be accepted

delaguardo12:10:14

clojure.test tend to be as small and simple as possible. is-not looks like syntactic sugar for me

delaguardo12:10:41

can be implemented outside of framework

(defmacro is-not [form]
  `(is (not ~form)))

Alex Miller (Clojure team)12:10:43

You’re making this too hard. You don’t need to assert not thrown at all (an uncaught throw will fail the test). Either just invoke, or assert the return.

Jim Newton13:10:20

I believe if you have an uncaught exception the test flow will stop.

Alex Miller (Clojure team)13:10:41

If you don’t want that, break it into a separate deftest

Jim Newton13:10:40

there seems to be a piece missing from the testing flow. an is-not test or a not-thrown predicate. yes its not the end of the world if the testing flow fails, just not very elegant.

delaguardo13:10:21

how you define “flow”?

(deftest flow
  (testing "flow"
    (is (do (prn "1") (= 1 1)))
    (is (throw (ex-info "exception" {})))
    (is (do (prn "2") (= 2 2)))))
like this ^? if so then in case of unhandled exception it will not stop.

delaguardo13:10:01

I think your snippet can be rewritten like this:

(deftest t-invalid-type
  (testing "for invalid type within rte"
    (with-compile-env ()
      (is (thrown? Exception (canonicalize-pattern '(not (:or String Number)))) "test 0")
      (is (thrown? Exception (canonicalize-pattern '(not (or String Number) (or Long Number)))) "test 1")
      (is (thrown? Exception (canonicalize-pattern '(and (:or String Number) (:or :sigma)))) "test 2")
      (is (thrown? Exception (canonicalize-pattern '(or (:and String Number)))) "test 3")
      (is (canonicalize-pattern '(:or :epsilon (:cat Integer (:* Integer)))) "test 4"))))
so “test 4” will fail if exception thrown

delaguardo13:10:06

and it will be much more elegant if you add assertion into it (is (= some-data (canonicalize-pattern …

Jim Newton12:10:28

Is there a way to use a simply 2-thread model to evaluate an expression, or call a 0-ary function, and return its return value, however, in a second thread, wait some timeout, and print a message that the expression is taking a long time. I.e., if expr1 takes more than 10 seconds to finish, print a message, and when it finally finishes print a second message indicating the total time.... If the expression finishes before the timeout, then the 2nd thread should print nothing, but should be killed, as I intend to launch 1000s of these in succession.

Ben Sless13:10:03

(deref (future (expr ,,,)) timout-ms timeout-val)

Ben Sless13:10:05

For example,

(deref (future (Thread/sleep 1000) (println 'finished)) 100 :timouet)
will return after 100 ms and print finished after 1000 ms

Jim Newton13:10:18

hmm. not sure how to make this do what I need. I don't want anything printed if it took less than the threshold to run. Maybe I'm thinking about it wrong. But if I tried to do it, I'd have 2 threads. #1 runs the client function #2 sleeps until the timeout If #1 finishes it kills #2, so I don't get 1000s of sleeps in parallel, plugging up the available threads If #2 finishes, then it prints a message, and waits for #1 to finish (if ever), then reports the total time.

delaguardo14:10:17

I can recommend read throw this chapter — https://www.braveclojure.com/core-async/

Jim Newton12:10:43

I'm generating this as randomized input to some functions during testing. occasionally some test take a long long long time. I'd like to recognize this and find the kind of input which causes it to never return. I can't really use a simple timer, because if it never finishes, the simple timer method wont really work.

Jim Newton13:10:43

would it be possible (from the clojure implementation perspective) to re-engineer the memoize function so that when the java heap starts filling up, the cache gets flushed. Of course permission to do so would need to be an option in the call to memoize . When I run huge numbers of randomly generated test cases the java heap fills up on what seems to be simple computation. I suspect it is the fact that several of my functions are memoized.

Alex Miller (Clojure team)14:10:35

it is technically possible using the weaker java ref types (in fact, the interned keyword cache in Clojure is similar). hard to say whether that's a change we would make in core

Alex Miller (Clojure team)14:10:13

the diversity of options is why the core.memoize lib exists

Jim Newton15:10:51

@alexmiller, what's the idea with the core.memoize library. Does it mean I'll have to implement a caching algorithm for my application, or does it mean I can chose from an existing one?

William Skinner16:10:53

I'm looking for a critique of my implemenation. It's the basic procedure for a script I'm writing and does a lot of impure/stateful stuff. Would you write it different?

(defn process-oldest-unprocessed [{integration-id :integration/id pattern :integration/file_pattern ftp-info :integration/sftp-info}]
  "Find oldest unprocessed file on sftp for integration.  Copy it to Minio.  Create entry in stage_candidates table."
  (let [processed (->> (db/all-candidates integration-id) (map :stage_candidate/file_name))]
    (sftp/with-sftp-channel ftp-info
                            (fn [channel]
                              (let [sftp-file-info (sftp/oldest-matching-excluding channel pattern processed)]
                                (minio/create (sftp/get-input-stream channel sftp-file-info))
                                (db/create-candidate sftp-file-info))))))

ghadi16:10:42

@skinner89 just off the top 1) db/create-candidate doesn't take a db argument - are you using some sort of global state for db? make it explicit, you'll thank me later 2) docstring indicates this should be three separate routines, with a fourth to orchestrate together. Is this the orchestration function? 3) what happens when either copy or db create don't work? 4) what does this function return? consider some sort of explicit record of what happened minor: put the argslist on the next line destructuring is verbose, making needless extra names, leads to cognitive overhead. Consider {:integration/keys [id file_pattern sftp-info]}. Don't worry about the - vs _ idioms. More important to have fewer names.

hiredman16:10:06

pretty sure that docstring is in the wrong place too

ghadi16:10:05

(defn name "docstring" [arglist ...] .....

borkdude16:10:13

;)

❤️ 6
💕 9
William Skinner16:10:20

Thanks! Hadn't considered the db arg @ghadi.

William Skinner16:10:27

To answer 3) I would need to cleanup the minio work if db/create fails.

J Crick18:10:34

I've tried looking online, and did a quick search in this forum, but didn't find an answer. I'm trying to find a learning path to proficiency in modern, idiomatic, production Clojure--something that will tell me what I should learn (in what order, too?), so that I can have a solid sense of direction for my learning (I know that might be complex, as I'm guessing the field is huge, and there are a lot of paths---maybe that's why this doesn't exist, though I've seen things like this for Front End dev). I know about Brave Clojure, and a few other online resources that cover the basics of the language, but, naturally, there's so much more. http://Purelyfunctional.tv is great, but there doesn't seem to be any rhyme or reason to what's there. It feels like a big jumble of videos. I find myself getting stuck because I'm lacking direction. Does anyone here have recommendations for a learning path (or can they point me to something they know that already exists)?

dharrigan18:10:19

two examples

dharrigan18:10:51

John (who runs Practicalli) has a lot of youtube videos up too on the channel

phronmophobic19:10:28

What kind of projects are you interested in? • games • web servers • web ui • graphics • scripting • data processing • networking • procedural music generation • something else?

seancorfield20:10:08

Clojure Applied is a reasonably good book for "modern, idiomatic, production Clojure" (although the author cautions that when they do a next edition, they will place less emphasis on records and keep to plain hash maps a lot more).

J Crick06:10:35

Just looking over Clojure Applied. That seems to be right in line what I was looking for. Thank you!

ryan echternacht18:10:09

Welcome to learning clojure! Unfortunately, I think you're experience is a common one. For my own learning, I found a thing I wanted to build, then figured out how to build it, piece by piece. That got me enough skill to land a job in clojure, and that gave me the support to keep going

ryan echternacht18:10:39

For me, I focused on building an API first, while building the site in something i knew (Vue). Most of what I needed was just familiarity with writing functional code and clojure idioms, and you should be able to learn this no matter what thing you're working on

ryan echternacht18:10:13

Hope that helps and YMMV. Best of luck! And I've found this community (and this message board specifically) to be quite helpful

practicalli-johnny20:10:47

@alibris.lumos as Clojure is a general purpose language, there is not a single learning path except for that you make for yourself. Regardless of content used to learn Clojure, the only way you will make sense of things is identifying specific goals to achieve or projects to build. Without this, anyone learning any programming language will most likely end up lost and frustrated. Learning to write good idiomatic code does contribute to writing production code, however, that is just part of the engineering required to write a production quality system (what ever the context of that is). I suggest to anyone learning Clojure (or any general purpose language) to experiment at first and feel comfortable in understanding how to try things out. Getting comfortable using the Clojure REPL will give you a good foundation, along with learning some of the Clojure core functions (http://www.4clojure.com/ is excellent for this). Once you have a goal or a project in mind, you can start asking specific questions in that context and you should feel like you are making progress. If you can, find someone who will mentor you or someone to learn with. Or just ask lots of specific questions here, its what we are here for.

William Skinner20:10:13

Is there an idiom for saving a value in the middle of a thread pipeline during RDD?

seancorfield21:10:26

(doto tap>) if you are using something that has tap watchers (such as Reveal or REBL or Portal).

Lennart Buit21:10:19

This is such a life saver!

seancorfield22:10:12

Nice! My vote would be to ignore tap> failure because if you really want to detect that and do something different, you can always do (doto (-> tap> assert)) or similar...

seancorfield22:10:43

(since most folks are probably using (doto tap>) right now, they're ignoring the result/failure anyway)

seancorfield21:10:37

Another option would be (as-> v (def saved-value v)) -- one of the few "good" uses of def not at the top-level: this would create a global saved-value containing the value from the pipeline.

seancorfield21:10:16

I highly recommend https://github.com/vlaaad/reveal/ as an option here. I use it day-in, day-out for all my RDD @skinner89

seancorfield21:10:32

(and for anyone who wants to try Reveal, there's a #reveal channel if you get stuck or have in-depth questions about it)