Fork me on GitHub
#beginners
<
2021-04-05
>
grazfather00:04:38

what’s the proper way to check if a value is a sequence? seq? doesn’t work on vectors

seancorfield00:04:24

seq? tests whether something is a seq (not a sequence). And a vector is not a seq. It depends on exactly what types of data you're trying to select for, but I suspect sequential? is probably what you want @grazfather

seancorfield00:04:14

FYI:

user=> (doc seq?)
-------------------------
clojure.core/seq?
([x])
  Return true if x implements ISeq
nil
user=> (doc sequential?)
-------------------------
clojure.core/sequential?
([coll])
  Returns true if coll implements Sequential
nil
user=> (doc seqable?)
-------------------------
clojure.core/seqable?
([x])
  Return true if the seq function is supported for x
nil

grazfather00:04:25

that works. Why are lists seqs but vectors are not?

grazfather00:04:35

I thought seq just meant an ordered collection

grazfather00:04:01

i guess not. How is ISeq different from Sequential, then?

seancorfield00:04:37

Sequential is a "marker" interface -- it has no methods.

grazfather00:04:52

Right, I guess I just thought that vector would implement that interface

seancorfield00:04:39

user=> (->> [1 2 3] type ancestors (sort-by str))
(clojure.lang.AFn clojure.lang.APersistentVector java.lang.Object clojure.lang.Associative clojure.lang.Counted clojure.lang.IEditableCollection clojure.lang.IFn clojure.lang.IHashEq clojure.lang.IKVReduce clojure.lang.ILookup clojure.lang.IMeta clojure.lang.IObj clojure.lang.IPersistentCollection clojure.lang.IPersistentStack clojure.lang.IPersistentVector clojure.lang.IReduce clojure.lang.IReduceInit clojure.lang.Indexed clojure.lang.Reversible 
clojure.lang.Seqable ; vector is seqable (you can call seq on it)
clojure.lang.Sequential ; vector is sequential
java.io.Serializable java.lang.Comparable java.lang.Iterable java.lang.Runnable java.util.Collection java.util.List java.util.RandomAccess java.util.concurrent.Callable)

seancorfield00:04:43

Clojure tries to avoid providing inefficient ways to access data. Vectors are efficient for conj at the end, lists are efficient for conj at the start, which is what cons does.

grazfather00:04:13

cool. thank you!

grazfather00:04:18

and til ancestors

seancorfield00:04:36

So vector has peek and pop instead of first and next:

user=> (peek [1 2 3])
3
user=> (pop [1 2 3])
[1 2]

seancorfield00:04:50

(and they operate on the end of the vector, not the start)

grazfather00:04:10

first works on vectors

seancorfield00:04:36

Right, but it calls seq first and so you are coercing the vector into a sequence.

seancorfield00:04:55

map etc also work on vector by coercing it to a sequence first.

grazfather00:04:28

alright. Thank you. I see seq? used a lot to test if something is a sequence, I was pretty suprised to see it not work on vectors. What’s the proper way to test that a value is iterable?)

phronmophobic00:04:57

I haven't seen seq? used very often. (seq xs) is common idiom to check if there are items in xs

seancorfield00:04:24

It depends what you mean by "iterable". Vectors implement java.lang.Iterable but I suspect that's not what you mean?

grazfather00:04:36

I mean an ordered collection

seancorfield00:04:26

Sounds like Sequential then.

grazfather00:04:37

works for me

phronmophobic00:04:41

Although maps and sets aren't sequential?, but they are seqable?

phronmophobic00:04:47

> (sequential? {})
false
> (sequential? #{})
false
> (seqable? {})
true
> (seqable? #{})
true

seancorfield00:04:43

user=> (for [coll [(list 1 2 3) [1 2 3] #{1 2 3} {1 2, 3 4}] f '[seq? sequential? seqable?]] [((resolve f) coll) f coll])
([true seq? (1 2 3)] [true sequential? (1 2 3)] [true seqable? (1 2 3)] [false seq? [1 2 3]] [true sequential? [1 2 3]] [true seqable? [1 2 3]] [false seq? #{1 3 2}] [false sequential? #{1 3 2}] [true seqable? #{1 3 2}] [false seq? {1 2, 3 4}] [false sequential? {1 2, 3 4}] [true seqable? {1 2, 3 4}])
user=> (clojure.pprint/pp)
([true seq? (1 2 3)]
 [true sequential? (1 2 3)]
 [true seqable? (1 2 3)]
 [false seq? [1 2 3]]
 [true sequential? [1 2 3]]
 [true seqable? [1 2 3]]
 [false seq? #{1 3 2}]
 [false sequential? #{1 3 2}]
 [true seqable? #{1 3 2}]
 [false seq? {1 2, 3 4}]
 [false sequential? {1 2, 3 4}]
 [true seqable? {1 2, 3 4}])

seancorfield00:04:35

I agree. I would say that if code is using seq? it is either doing something very specific or it is wrong 🙂

grazfather00:04:23

but seq crashes if its a different type like true

seancorfield00:04:47

There's seqable? to test for things that can be seq'd.

3
seancorfield00:04:01

user=> (seqable? true)
false

grazfather00:04:41

alright, thanks again

alexmiller01:04:01

Seqs are a logical list abstraction

alexmiller01:04:46

Vectors (and maps and sets etc) can provide a seq view (a logical list) over those collections

zackteo03:04:47

I realise I'm not too sure what functions I can use to combine 2 lists together but anyway I have a list like so ...

`((171) (0 0 0 0 0 0)
  (147) (0 0 0 0)
  (183) (0 0 0 0 0 0 0))
how do I transform the list into something like
`((171 0 0 0 0 0 0)
  (147 0 0 0 0)
  (183 0 0 0 0 0 0 0))
Like to merge pairs of elements successively

paulocuneo03:04:05

(Im not a clojure expert) maybe this way?

(->> `((171) (0 0 0 0 0 0)
       (147) (0 0 0 0)
       (183) (0 0 0 0 0 0 0))
     (partition-all 2)
     (map (fn [[left right]]
            (concat left right))))

zackteo03:04:19

Right! That's definitely 1 way 🙂

raspasov05:04:12

Transducer version (into [] (comp (partition-all 2) (map flatten)) [[1 2] [3 4] [5 6] [7 8]])

raspasov05:04:19

@UUSQHP535 as a general tip, stick to vectors rather than lists or sequences unless there’s a specific need; easier to reason about IMO

yuhan05:04:53

Note that flatten recursively flattens nested sequences - I would go with (map #(apply concat %))

👌 6
piyer06:04:56

(concat '(171) '(0 0 0 0 0 0)) ;; '(171 0 0 0 0 0 0)

Endre Bakken Stovner05:04:35

Is there a way to make Clojure always print seqs as a literal? I am copy-pasting a lot of output into the REPL and I always forget to add ' in front of seqs. (`(:hi :there)` triggers an error as you know.)

raspasov05:04:23

Prefer vectors where possible

raspasov05:04:03

mapv, vec functions come in handy

Endre Bakken Stovner05:04:47

I should make my own print that uses Specter or some such to find and turn seqs into vecs.

Endre Bakken Stovner05:04:21

But sometimes I want the colls to actually be seqs 🙂

raspasov05:04:05

Almost everything you can do with lists/seqs you can do with vectors, but you avoid the copy paste problem

teodorlu07:04:58

You might be able to simply quote everything you print. Quoted maps and vectors read back "correctly" too. Are you just printing random stuff with all the print functions? Or are you using tap?

Endre Bakken Stovner07:04:47

I am not using tap. I've just heard the term. How is it applicable here?

teodorlu06:04:41

You might be able to add your quotes with add-tap https://clojuredocs.org/clojure.core/add-tap

Endre Bakken Stovner08:04:06

I'd like to make selmer.parser/render join lists of strings before rendering, but leave strings as is:

(render "{{input}}" {:input ["a.txt" "b.txt"]}) => "a.txt b.txt"
(render "{{input.1}}" {input ["a.txt" "b.txt"]}) => "a.txt"
Currently the first would print "[&quot;a.txt&quot; &quot;b.txt&quot;]". I'd like to avoid having to call {{input|join\" \"}} or do it implicitly.

Endre Bakken Stovner08:04:14

This is the code selmer uses to render:

(defn render-template [template context-map]
  " vector of ^selmer.node.INodes and a context map."
  (let [buf (StringBuilder.)]
    (doseq [^selmer.node.INode element template]
        (if-let [value (.render-node element context-map)]
          (.append buf value)
          (.append buf (*missing-value-formatter* (:tag (meta element)) context-map))))
    (.toString buf)))
And node is
(deftype TextNode [text]
  INode
  (render-node [this context-map]
    (str text))
  (toString [_]
    (str text)))
Does this make it possible to inject my desired code or override the existing implementation? Never made or extended protocols before. https://www.braveclojure.com/multimethods-records-protocols/.

Endre Bakken Stovner10:04:08

(deftype L [l]
  clojure.lang.IPersistentCollection
  (toString [self] (str/join " " l)))

(render "all: {{lst}} one: {{lst.1}}" {:lst (L. [1 2 3])})
"all: 1 2 3 one: "
Now I need to find out how to implement whichever method is needed to pick out elements.

Endre Bakken Stovner10:04:34

I have the following deftype:

(require '[clojure.string :as str])
(deftype L [l]
  clojure.lang.IPersistentCollection
  (toString [self] (str/join " " l)))
What is the minimal code needed to support get/index lookups?
(def l (L. [1 2 3]))
(l 0) ;; class user.L cannot be cast to class clojure.lang.IFn
([1 2 3] 0) ;; 1

raspasov10:04:53

Taking a step back, and because you’re posting in #beginners - is there a specific reason you’re trying to use deftype apart from pure learning? For real-world problems, I would stay away from them, unless you have a specific reason (usually related to low-level high-performance code)

Endre Bakken Stovner10:04:31

See my previous two questions immediately above this one 🙂 I need a vec which works like this:

(.toString [1 2 3]) ;; "1 2 3"

raspasov10:04:25

Why not just:

(str [1 2 3])

raspasov10:04:33

=> “[1 2 3]”

Endre Bakken Stovner10:04:46

Because I need selmer.parser/render to behave thusly:

(render "all: {{lst}} one: {{lst.2}}" {:lst [1 2 3]}) => "all: 1 2 3 one: 3"

raspasov10:04:29

If I am understanding right, one of your goals is for:

(.toString [1 2 3])
… to output: “123” Rather than the current: “[1 2 3]”

Endre Bakken Stovner10:04:31

Of course,

[1 2 3]
should probably be a custom type of some sort. I do not want to override the toString for all vecs.

raspasov11:04:48

Yeah… that’s probably not a good idea (global override)

raspasov11:04:34

I am 95%+ certain that you’re trying to do this the hard way. There should be no issue pre-processing the input rather than trying to achieve some sort of polymorphism based on types; it is definitely possible in Clojure, but not the recommended way.

raspasov11:04:01

In 8+ years in Clojure, I’ve never felt the need to do this (and I’ve done quite some years of OOP before that)

Endre Bakken Stovner11:04:04

Okay, the alternative I can see is to write my own render-function. Which I am sure would be more work than implementing a dinky type with two methods.

raspasov11:04:19

Well, in one case you’re writing something to the effect of: 1. look for vectors in your render fn arguments 2. transform those arguments via (apply str [1 2 3]) => “123” 3. call your render fn In the other, you’re creating a brand new type, which chances are you’re not gonna use anywhere else.

raspasov11:04:11

Sorry if I’m misunderstanding something 🙂

raspasov11:04:31

From my experience in Clojure, creating a new type of vector just to have a specific custom toString function is VERY uncommon.

Endre Bakken Stovner11:04:01

If I transform the vector then ""{{input}}"" would give the correct result but not ""{{input.1}}"" (the latter would be a single char).

Endre Bakken Stovner11:04:36

Can you help me do it instead?

Endre Bakken Stovner11:04:14

Creating a dink custom type is going to be much less work than rewriting someone else's library.

raspasov11:04:04

Actually I don’t know how to do it on top of my head, it’s pretty uncommon; I’ll let you know if I figure it out 🙂

Endre Bakken Stovner11:04:58

Perhaps I should ask this in the regular Clojure channel. But then I would be cross-posting 😕

raspasov11:04:04

Just FYI, you’ll also be creating the vector in a custom way, via a new “constructor” etc

raspasov11:04:53

(but a lib like that has a reason to be a new type because it adds capabilities to a vector that it didn’t have before, rather than just customizing .toString)

Endre Bakken Stovner11:04:04

I only need an object that supports indexed lookups. That is all. Does not need to be a vector really.

yuhan11:04:09

You could define your own print method based on the :type metadata:

(defmethod print-method ::my-thing
  [x writer] (.write writer "hello"))

(print-str
  (with-meta [1 2 3]
    {:type ::my-thing}))
;; => "hello"

Endre Bakken Stovner11:04:58

How do I get the internals of selmer to use my custom print-method?

Endre Bakken Stovner11:04:49

Here is the line that calls toString: https://github.com/yogthos/Selmer/blob/master/src/selmer/parser.clj#L105 (It happens within the invocation of .render-node).

yuhan11:04:45

ah.. I didn't realize you needed to override toString tself, not printing

Endre Bakken Stovner11:04:30

No that is okay. A lot of context was hard to see :)

raspasov11:04:24

Perhaps take a look at: https://github.com/clj-commons/potemkin It does a decent job of explaining why what you’re trying to do is not so easy (I believe)… When you say “I just need something that supports indexed lookups” - that’s not a straightforward thing in Clojure; there’s a multitude of ways in Clojure to access things in an “indexed” way: nth, get, first, etc; those are all different functions which work with different protocols, interfaces, etc

raspasov11:04:59

This is for maps, but I believe relevant “A Clojure map implements the following interfaces: `clojure.lang.IPersistentCollection`, `clojure.lang.IPersistentMap`, `clojure.lang.Counted`, `clojure.lang.Seqable`, `clojure.lang.ILookup`, `clojure.lang.Associative`, `clojure.lang.IObj`, `java.lang.Object`, `java.util.Map`, `java.util.concurrent.Callable`, `java.lang.Runnable`, and `clojure.lang.IFn`. Between them, there’s a few dozen functions, many with overlapping functionality, all of which need to be correctly implemented.”

Endre Bakken Stovner11:04:32

But can't I just copy all methods from another implementation and override the few I need?

Endre Bakken Stovner11:04:45

Starting to think OOP wasn't such a bad idea XD

raspasov11:04:59

You definitely can: rrb-vector does it https://github.com/clojure/core.rrb-vector

raspasov12:04:20

But instead of doing all of those things, I would just do:

(let [v     ["a.txt" "b.txt"]
      input {:input-vec v :input-str (apply str v)}]
 (render "{{input-str}}" input)
 (render "{{input-vec.1}}" input))

raspasov12:04:34

And be done with it… I don’t really understand what’s the ceremony here.

raspasov12:04:39

(I haven’t tested the code above or used selmer, but I suppose that’s how it would work)

Endre Bakken Stovner12:04:43

This is in the user-facing part of a language I am writing. So that uglyness won't be hidden away in the internals somewhere, but users would have to write it all the time.

"samtools mpileup -g -f {{file.genome}} {{input.sorted}} | bcftools call -mv - > {{output.1}}"
would suddenly become
"samtools mpileup -g -f {{file.genome}} {{input-str.sorted}} | bcftools call -mv - > {{output-vec.1}}"})

raspasov12:04:25

I see; now I understand the reason behind it; Perhaps you might want to look at the “hard” way then; For whatever it’s worth, strings in Clojure do support indexed lookup via (nth…) (nth “123" 0 :not-found) => \1 (nth “123” 42 :not-found) => :not-found

raspasov12:04:03

But again, that depends on the implementation of the render library; is it using (nth …) when doing that *.1 magic? I don’t know

raspasov12:04:51

I am sorry, there might be some super concise way to achieve what you’re trying to do via deftype, etc but I just don’t know it; I am guessing there isn’t but I would like to be proven wrong;

delaguardo13:04:04

why not to use for from the library iteself?

(render "all: {% for item in lst %}{{item}} {% endfor %}one: {{lst.2}}" {:lst [1 2 3]})
this one will give you exactly what you are trying to do

👆 3
raspasov13:04:41

Ok, I figured out something which is crufty and almost 100% sure not the correct way but:

(defn custom-v [& i]
 (proxy [clojure.lang.IPersistentVector] []
  (seq [] i)
  (nth [idx not-found]
   (nth i idx not-found))
  (toString [] (apply str i))))
(custom-v 1 2 3)
=> [1 2 3] (str (custom-v 1 2 3)) ;custom => “123” (.toString (custom-v 1 2 3)) ;custom => “123" (nth (custom-v 1 2 3) 0 :not-found) => 1 (nth (custom-v 1 2 3) 42 :not-found) => :not-found You get “vectors” that are not really vectors; they satisfy, say, (vector? …) but many of the Clojure functions that work on vectors, like (rseq …) don’t work in this case; It throws UnsupportedOperationException (as expected) I’m actually curious; Is there an easy/straightforward way to implement, let’s say, a custom (str …) for a specific Clojure collection without “losing” functionality?

👍 3
raspasov13:04:41

Ok, I figured out something which is crufty and almost 100% sure not the correct way but:

(defn custom-v [& i]
 (proxy [clojure.lang.IPersistentVector] []
  (seq [] i)
  (nth [idx not-found]
   (nth i idx not-found))
  (toString [] (apply str i))))
(custom-v 1 2 3)
=> [1 2 3] (str (custom-v 1 2 3)) ;custom => “123” (.toString (custom-v 1 2 3)) ;custom => “123" (nth (custom-v 1 2 3) 0 :not-found) => 1 (nth (custom-v 1 2 3) 42 :not-found) => :not-found You get “vectors” that are not really vectors; they satisfy, say, (vector? …) but many of the Clojure functions that work on vectors, like (rseq …) don’t work in this case; It throws UnsupportedOperationException (as expected) I’m actually curious; Is there an easy/straightforward way to implement, let’s say, a custom (str …) for a specific Clojure collection without “losing” functionality?

👍 3
alexmiller13:04:09

is that your actual goal?

alexmiller13:04:32

if so, you can do that by overriding the print-method for the type

alexmiller13:04:23

for creating custom collections, there are a variety of different interfaces you might need to implement to replicate different parts of collection functionality

raspasov13:04:38

That’s what I thought. So the short answer is: no, there’s no “one liner” way to extend vectors….

alexmiller13:04:59

you can see what normal vectors implement for example:

alexmiller13:04:02

user=> (ancestors clojure.lang.PersistentVector)
#{clojure.lang.IPersistentCollection java.lang.Iterable clojure.lang.Sequential clojure.lang.ILookup clojure.lang.APersistentVector java.util.List java.lang.Runnable java.io.Serializable clojure.lang.Indexed clojure.lang.IPersistentVector java.lang.Comparable clojure.lang.IReduce clojure.lang.Associative clojure.lang.IMeta clojure.lang.IEditableCollection clojure.lang.IObj clojure.lang.IFn java.util.concurrent.Callable clojure.lang.AFn java.lang.Object java.util.Collection java.util.RandomAccess clojure.lang.Reversible clojure.lang.IKVReduce clojure.lang.IHashEq clojure.lang.Counted clojure.lang.Seqable clojure.lang.IReduceInit clojure.lang.IPersistentStack}

raspasov13:04:20

Ah, that’s neat to know; never knew of that fn

alexmiller13:04:49

if you're interested in this topic, I wrote a section in Clojure Applied about it with a picture that maps Clojure api functions to methods in the internal interfaces

raspasov13:04:48

Very cool, will check it out. Thank you for your input Alex, always the trusted source clj 🙂

alexmiller13:04:52

this is kind of a cool helper function too: https://gist.github.com/semperos/3835392

alexmiller13:04:26

will create a deftype scaffold with a lot of the necessary stuff for a custom type (you will still need to tailor it appropriately but can help a lot)

👍 3
raspasov13:04:50

No; I went on a Java interface/class rabbit hole exploration 🙂 And trying to figure out if there’s a generic “proper” way to do it; or it would involve re-writing everything as protocols;

alexmiller13:04:06

(in general, I'd say this topic is not a #beginners topic and probably better asked in #clojure instead)

raspasov13:04:44

Yes… It stemmed from a #beginners conversation… I agree

Lukas13:04:27

Hi 👋, I'm learning about macros.  After doing the basic conditional tutorials, I now want to write some more "advanced" macros to wrap my head around the topic. My goal is to write a macro that returns a function that combines str/replace functions.

;; It should later work like this:
((combine-replace #"a" "A" #"b" "B") "ab") => "AB"
I started with the base case
(defmacro combine-replace [x y & xs]
   `(fn [s#]
      (-> s#
          (str/replace ~x ~y))))

 ((combine-replace #"a" "A") "ab") => "Ab"
what I now want is to "write"  (str/replace ~x ~y) n times for all pairs in xs. The generated code should look something like this
(-> s#
    (str/replace ~x ~y)
    (str/replace ~x2 ~y2)
    ...) 
Currently, I'm sitting on this
(defmacro combine-replace [x y & xs]
   `(fn [s#]
      (-> s#
          (str/replace ~x ~y)
          [email protected](loop [xs# xs]
              (when xs#
                `(str/replace ~(first xs#) ~(second xs#))
                (recur (nnext xs#)))))))
can someone help me to understand what I actually have to do, to make this work?

paulocuneo14:04:11

maybe this will hint what your a looking for:

(let [xys [#"1" "2" #"3" "4" #"5" "6"]]
  (assert (= (mod (count xys) 2) 0) "Expression must be multiple of 2")
  (->> xys
       (partition 2)
       (map (fn [[left right]]
              `(some-fun ~left ~right)))))

NoahTheDuke14:04:33

you gotta move the xs handling outside of the syntax unquote:

(defmacro combine-replace [x y & xs]
  (let [replaces (for [[x1 y1] (partition 2 xs)]
                   `(str/replace ~x1 ~y1))]
    `(fn [s#]
       (-> s#
           (str/replace ~x ~y)
           [email protected]))))

NoahTheDuke14:04:01

@U487Z32UF good call with the assert. I forgot that partition handles odd-count seqs

✌️ 3
Lukas14:04:16

Thank you both so much 🙏❤️

👍 6
Scott Starkey16:04:19

OK, I seem to be having Java “classpath” problems when trying to load external libraries. Am I doing something wrong? 1. First I add to the :dependencies section of my project.clj file. 2. Then I do a lein deps. It seems to load the files from clojars. 3. Then I add the library line(s) in my (ns … (:require … )) . But I get a classpath error: Could not locate ring/middleware/json__init.class, ring/middleware/json.clj or ring/middleware/json.cljc on classpath. Probably a dumb newbie mistake, but what am I doing wrong? (Images included.) It makes me think that my classpath is missing something.

hiredman16:04:06

you don't need to run lein deps

hiredman16:04:52

my guess is whatever repl your editor is connected to is either not based on your project.clj, or hasn't been restarted since you added the dependencies

West17:04:29

Hey guys, so I'd like to use something datomic like for a desktop application I was building. Apparently datomic is not free software, but there are libraries out there that behave like datomic. What are some of the lesser known options out there? So far I know about... • Datahike • Datascript • Datalevin

Scott Starkey18:04:50

@hiredman - You are totally right. I exited completely out of Emacs, and then back in, opened up a REPL, and magic! 🎩 Thanks for your help.

Oliver18:04:24

I am just making my first attempts with Clojure (Linux, Spacemacs). Now I would like to use data.json (https://github.com/clojure/data.json) but cannot figure out how to install it. Any help very appreciated.

Dimitar Uzunov18:04:07

You need to look up leiningen or tools deps. Here is the guide for the latter: https://clojure.org/guides/deps_and_cli

Dimitar Uzunov18:04:38

You need to specify your dependencies in a file of a format specific to each of the tools and then use it to download your dependencies and add them to your class path. They can also do other tasks.

Oliver18:04:35

This part here? "To work with this library, you need to declare it as a dependency so the tool can ensure it has been downloaded and add it to the classpath. The readme in most projects shows the name and version to use. Create a deps.edn file to declare the dependency:"

Dimitar Uzunov18:04:22

There are some examples in the text, you can copy the snippets from the data.json page for your deps.edn and your source file

Oliver18:04:28

I see this file in /usr/share/clojure. Should I add it there?

Dimitar Uzunov18:04:08

No, you create a new one in the folder you are working in

Oliver18:04:41

Ok. Let me try.

Oliver18:04:31

Still get: Clojure 1.10.3 user=> (ns example (:require [clojure.data.json :as json])) Execution error (FileNotFoundException) at example/eval138$loading (REPL:1). Could not locate clojure/data/json__init.class, clojure/data/json.clj or clojure/data/json.cljc on classpath. example=>

Dimitar Uzunov18:04:04

you need to run clj first so it can setup your classpath

Oliver18:04:29

> >clj > Please install rlwrap for command editing or use "clojure" instead.

Dimitar Uzunov18:04:54

yep, you need rlwrap too

👍 3
Oliver18:04:14

Cool, now it works:<

Oliver18:04:32

Thanks so much!

vlad_poh22:04:14

how would i do the following in clojure (reading a js textbook but can't help myself thinking what the clojure equivalent would look like) ... It finds the first number that is both greater than or equal to 20 and divisible by 7.

for (let current = 20; ; current = current + 1) {
  if (current % 7 == 0) {
     console.log(current);
     break;
  }
}
best I could come up with is
(first (filter #(zero? (mod % 7)) (iterate inc 20)))
doesn't "feel" right

noisesmith22:04:11

a literal translation: (loop [current 20] (if (zero? (mod current 7)) current (recur (inc current))))

vlad_poh22:04:31

ah loop recur! i rarely use it. Still looks verbose

paulocuneo22:04:07

(println 21) ?

vlad_poh22:04:24

:rolling_on_the_floor_laughing: love it

😆 9
noisesmith22:04:09

regarding verbosity, I can't think of the higher level construct that would reduce the size of the code significantly

noisesmith22:04:16

in either the filter or the loop version, there's not much noise - every expression does something meaningful

vlad_poh22:04:35

agreed but i feel i'm overlooking something take, some, take-while, for etc

vlad_poh22:04:58

i like this one but

(some #(when (zero? (mod % 7)) %) (iterate inc 20))
just replaces first, filter with some, when

noisesmith22:04:26

right, some just makes it more complicated