Fork me on GitHub
#beginners
<
2019-07-15
>
Schmoho12:07:12

Sorry if this is a silly question, but I'm really irritated by this. Why doesn't this overflow the stack? (reduce (fn [a b] (inc a)) 0 (partition 1 (range 10000000))) I have been noticing that several essential seq-functions are implemented as recursive functions returning lazy-seqs. This seemed weird to me, because I assumed they'd overflow the stack - but they don't. So I figure there must be something going on with how lazy-seqs are implemented, or what their semantics actually are, but I really don't get it.

Alex Miller (Clojure team)12:07:27

lazy-seq is not a recursive function, it is a macro

Schmoho12:07:55

partition is recursive though

Alex Miller (Clojure team)12:07:39

lazy-seq is a macro expansion - it does not happen in the runtime call stack

Alex Miller (Clojure team)12:07:18

macro expansion happens at compile time, not at runtime

Schmoho12:07:55

Okay I understand that. One step back though, artition features this line (cons p (partition n step pad (nthrest s step))) - why is this not recursive?

Alex Miller (Clojure team)12:07:05

because it's in the body of lazy-seq

Alex Miller (Clojure team)12:07:20

a simpler example to look at would be and or or

FiVo12:07:41

Is there a way to do stdio interactively with ProcessBuilder and http://clojure.java.io/reader and http://clojure.java.io/writer with a subprocess? Or what is the correct way of doing this?

FiVo12:07:20

I have something like this

Alex Miller (Clojure team)12:07:05

specifically clojure.java.shell/sh ?

FiVo12:07:21

(let [pbuilder (ProcessBuilder. (into-array String ["..."]))
      process (.start pbuilder)]
  (with-open [reader ( (.getInputStream process))
              writer ( (.getOutputStream process))]
    (let [line1 (.readLine ^java.io.BufferedReader reader)]
      (println line1)
      (.write ^java.io.BufferedWriter writer "test\n")
      (let [line2 (.readLine ^java.io.BufferedReader reader)]
        (println line2)))))

Alex Miller (Clojure team)12:07:36

or for a lib with a lot more functionality, https://github.com/Raynes/conch

FiVo12:07:31

@alexmiller ah thanks, the liberary seems to solve the buffering issues

Alex Miller (Clojure team)12:07:41

yeah, properly buffering and draining the process streams is tedious

Alex Miller (Clojure team)12:07:49

also, fyi the http://clojure.java.io/reader and http://clojure.java.io/writer can help with making buffered readers and writers

seancorfield17:07:26

@finn.volkel Conch is part of clj-commons now, which is where maintenance and new releases happen https://github.com/clj-commons/conch /cc @alexmiller

clj 8
Parker18:07:59

Hi guys! I'm running some tests though cider-test-run-* and I'm having a real hard time finding out if outputs I log in log/info are output anywhere during these tests

Parker18:07:22

well then, looks like it outputs in the linked cider REPL. No clue how I missed that. Nothing like coming back from the weekend to solve your problems 🙂

cider 4
John23:07:19

In C++ we can access maps/hashmaps with any kind of value (as long as there is a comparison function, or a hashing function), but in Clojure we can just interact with them with keywords, how can I achieve the same things if I can't use any kind of data? what is the Clojure-way of solving this?

hiredman23:07:27

You can use any object as a key

hiredman23:07:48

Keywords just happen to be very convenient

noisesmith23:07:38

Please don't use mutable objects as keys - it makes bad things happen. But clojure won't stop you.

noisesmith23:07:57

in fact there are built in functions in clojure.core that would be nearly useless if we didn't allow arbitrary keys

user=> (group-by count ["hello" "goodbye" "OK" "yes" "no" "stop" "start"])
{5 ["hello" "start"], 7 ["goodbye"], 2 ["OK" "no"], 3 ["yes"], 4 ["stop"]}

John23:07:30

Oh, great, from what I have read it seemed that you weren't supposed to use nothing more than keywords as keys. So as long as I use immutable objects as keys there should be no problem, right?

noisesmith23:07:51

right - strings, numbers, immutable data structures, symbols - all work great as keys

👍 4
noisesmith23:07:33

keywords are common for implementing named fields, but you can mix in whatever makes sense for your domain

noisesmith23:07:02

eg. I'd rather have "json key" as a key than attempt to turn that into a keyword

andy.fingerhut23:07:21

You can use maps containing maps containing maps containing sets of vectors as a key of a map, if you feel so inclined.

noisesmith23:07:47

I can almost picture a use case for that

chrisulloa23:07:24

sometimes can be useful to make composite keys [primary-id secondary-id] with vectors, though rarely use anything but keywords or strings as keys

andy.fingerhut23:07:44

If you don't care if the map is sorted or not, then no comparison function is needed, the built-in clojure.core/hash function is used and works fine. If you want sorting, then the built-in clojure.core/compare function doesn't do anything useful for you to compare sets or maps to other things, and you may or may not want its default behavior for comparing sequential things to each other.

noisesmith23:07:47

with group-by I end up using all sorts of keys

hiredman23:07:22

clojure.set/index builds maps of maps to sets

hiredman23:07:28

super useful

noisesmith23:07:11

also, if you use memoize, you are implicitly using a map from List to Object

andy.fingerhut23:07:12

There are guide articles on writing custom comparator functions: https://clojure.org/guides/comparators and on a few corner cases of equality/hash for non-sorted sets you should avoid: https://clojure.org/guides/equality

noisesmith23:07:23

in the C++ case you probably have to implement a comparator / hash, with Clojure you rarely have to do this on your own (I haven't had to at all)