Fork me on GitHub
#beginners
<
2021-05-22
>
Scott Starkey13:05:43

I'm playing around with the "orc names" code from the Brave & True book: https://www.braveclojure.com/zombie-metaphysics/ but instead of 3000-length names, let's have 10-letter names and instead of a few thousand names, I'd have billions. Can I do that with a lazy sequence?

(defn random-string
  "Returns a random string of specified length"
  [length]
  (apply str (take length (repeatedly #(rand-nth letters)))))
  
(defn random-string-list
  [list-length string-length]
  (doall (take list-length (repeatedly (partial random-string string-length)))))

(def orc-names (lazy-seq (random-string-list 10 60000000000)))
It was my understanding that the whole idea of a lazy-seq was it only processed what I need. So, (in my mind) I should be able to get the (first orc-names) without computing the whole sequence, right? But when I do that, I'm getting a java.lang.OutOfMemoryError (Java heap space). Am I missing something about the nature of a lazy-seq?

noisesmith16:05:18

just a small thing - take is redundant on repeatedly - you can replace (take n (repeatedly f)) with (repeatedly n f)

noisesmith16:05:09

with those changes:

(defn random-string-list
  [list-length string-length]
  (repeatedly list-length
              (fn []
                (apply str (repeatedly string-length
                                       #(rand-nth letters))))))

NoahTheDuke14:05:44

You have a doall, which realizes the whole list

lassemaatta15:05:16

Also, I think the arguments are in the wrong order (list len & string len)

Scott Starkey15:05:06

ahh, yes. Thanks.

Ivan Koz16:05:40

@yekrats take in random-string-list already returns lazy-seq, second call in def orc-names is redundant

Erik B Good16:05:37

Hello, I was wondering if there was any way of differentiating these two expressions ? (= (first [nil]) (second [1]))

dpsutton16:05:24

(nth [nil] 1 ::not-found). will return your value for value not present versus the value there

Ivan Koz16:05:17

@dpsutton aren't you off by one in nth?

dpsutton16:05:59

The question was how to distinguish nil from a collection that didn’t have an element there versus one that had an element there but the value was nil

dpsutton16:05:46

So I combined both into that. Changing to 0 will return the nil in the collection. Asking for the next element will return the sentinel value

Juλian (he/him)17:05:11

is it preferred to use {:foo/bar some-val :foo/baz some-other-val} or #:foo{:bar some-val :baz some-other-val} - putting namespace in front of each keyword of a map or putting namespace in front of map when all keywords of a map use same namespace? which is more readable/understandable?

dpsutton17:05:17

totally personal opinion. do what looks nicer to you or what your team has settled on

Juλian (he/him)17:05:33

I think I'll put the namespace in front of each keyword, since it's more explicit and I can't confuse myself when destructuring or using get/assoc/etc...

dpsutton17:05:16

you can read (doc *print-namespace-maps*) and (set! *print-namespace-maps* true|false) to adjust how the repl's printer prints maps as well

Juλian (he/him)17:05:33

thanks! it even adjusts the output of pr-str 🙂

Michael Lan18:05:09

I am confused as to what the idiomatic way of working with date and times is. I have a long representing milliseconds as a Unix timestamp and I want to get a nicely formatted date.

dpsutton18:05:45

Use interop with Java time

Michael Lan18:05:27

would that be java.time or java.util.Date?

dpsutton18:05:01

Definitely worth the time to read these javadocs for 15 minutes. Stay with interop only (no wrapper libs) while you learn the basic types and ways to manipulate them. Once comfortable, feel free to use a wrapper if you like but understanding the core concepts of the package will benefit you immensely

Michael Lan18:05:53

thank you for the advice

gibi19:05:13

hi, I'm a newbie and trying to use ring coercion to transform the type of a path parameter to int. I've written this code https://pastebin.com/J7LGiqgs following an example from https://cljdoc.org/d/metosin/reitit-ring/0.2.13/doc/ring/pluggable-coercion#full-example but I still can't see the value as int.

Juλian (he/him)19:05:15

I think you got the handler function wrong. Following the docs, it should look like this:

:handler (fn [{:keys [parameters]}]
           (let [id (-> parameters :path :id)]
             {:status 200 ... etc ...

Juλian (he/him)19:05:38

btw, your link is for the docs of a relatively old version

indy19:05:37

Hello, why do some operations that are mostly meant to be applied on lists take the list as the last argument (e.g. cons, take, drop..) while most other operations take a collection as the first argument (e.g. conj, nthnext, select-keys, subvec..)?

indy19:05:04

Is it some kind of idiom that for functions that return lazy seqs, the coll be the last arg?

drewverlee19:05:24

Good question. I think your intuition is good, likely lazyness.

indy07:05:00

Thanks Sean and Rakyi, I read Rich’s explanation and couldn’t quite follow the train of thought. How does “sequences being read from left and being fed from right” along with the "sequence functions like map, filter and having variadic args" influence the choice of having the sequence as the last arg?

indy08:05:50

Particularly why does “sequences being read from left and being fed from right” influence (map f sequence) , (filter pred sequence) not being (map sequence f) , (filter sequence pred). We could still chain them the same way we do it for other collections (-> sequence (map f) (filter f)) .

indy08:05:07

Oh okay maybe it is this bit "partial allows for direct parameterization as above" that majorly affected this decision. But I rarely do something like this ((partial map inc) '(1 2)). Can't think of many sequence functions that have more than one arg before the source.

seancorfield16:05:17

@UMPJRJU9E A lot of those sequence functions have transducer-producing arities: (map f), (filter pred) etc.

seancorfield16:05:10

As you get more familiar with Clojure, this argument ordering will start to make more sense and you’ll come to understand what Rich means.

indy16:05:20

Okay Sean. But transducers were introduced much later. Will probably have to reread the motive a few times.

seancorfield16:05:49

Those transducer arities were only possible because of the argument ordering conventions.

indy16:05:07

Yeah, very interesting. Can’t imagine transducers composing easily without this arg ordering of sequence functions. Not sure if it was extreme foresight or if we just got lucky. :)

indy16:05:24

Or more like a good design decision allowing the easy evolution and birth of transducers

👍 1
noisesmith16:05:05

my hunch (or vague recollection of the actual explanation) is that things that act like entities coming as first arg works with message passing / OO conventions from smalltalk (as later adapted by java, c++ and friends) (message thing arg) and things that act like collections coming last (action parameter things) works with data processing idioms coming from lisp (map, filter, etc. etc.) - for me personally the flip in order helps me categorize them as they are different idioms of programming

1
noisesmith16:05:59

as the RH quote above describes, both idioms existed in common lisp with the introduction of CLOS (the OO system for common lisp), and clojure just follows along

1
noisesmith16:05:43

fun trivia, many don't realize that common lisp was one of the first few languages with a full featured object system

Lukas21:05:37

Hey, is it possible to bind a value in an or clause with core.match? e.g. something like this

(let [x 1 y 2]
  (match [x y]
    [1 (:or 1 2 :bind-as b)] b
   :else nil))

Juλian (he/him)22:05:05

you could use something like this:

(let [x 1 y 2]
  (match [x y]
    [1 (b :guard #{1 2})] b
   :else nil))

Lukas06:05:55

Thanks a lot