Fork me on GitHub
#beginners
<
2019-08-08
>
GC01:08:28

I was looking at https://www.tutorialspoint.com/clojure/clojure_loop_statement , the loop thing, I can't understand how it is different from a for loop in other languages

jaihindhreddy09:08:47

Massive red flags. Whoever wrote that hasn't the faintest clue of Clojure.

jaihindhreddy09:08:39

It seems they just googled "how to do x" where x is the things they typically do in languages they are familiar with.

seancorfield01:08:45

Loops in other languages are almost always statements and have no value. Loops in Clojure return a value from the whole loop evaluation.

hiredman01:08:11

the diagrams on that web site are not great

seancorfield01:08:20

(to be honest, I consider tutorialspoint to be a pile of junk)

hiredman01:08:35

they kind of imply that the loop condition is part of the loop form

seancorfield01:08:46

I agree with @hiredman -- it's more confusing than helpful @gagan.chohan

hiredman01:08:50

which would make it like a for loop, but that is not the case

hiredman01:08:16

loop is similar to a recursive function call

seancorfield01:08:30

It's a terrible tutorial (now that I've actually read it!).

hiredman01:08:35

so a function like (defn f [a] (f a)) as a loop would be (defn f [a] (loop [b a] (recur b)))

GC01:08:41

I find clojure's official docs to be hard to understand sometimes

seancorfield01:08:45

Several of the examples here https://clojuredocs.org/clojure.core/loop are much better.

seancorfield01:08:56

^ That's not the official docs BTW

GC02:08:46

yea, tutorialpoint one is not, I feel clojuredocs are sometimes hard to follow

seancorfield02:08:37

Tutorialspoint is often just plain wrong. I strongly advise ignoring it.

seancorfield02:08:22

http://clojuredocs.org is not official either @gagan.chohan -- it's a community site, with community-submitted examples.

GC02:08:58

I always thought it to be official, since when you Google, it comes at top, and has org at end

GC02:08:07

I thought they were official ones

andy.fingerhut02:08:04

It doesn't make them "bad" necessarily, but if there are any incorrect examples on them, it means that they unfortunately might remain that way for some time, until someone confident enough that they are correct notices and updates them.

hiredman02:08:48

Recur is kind of tricky to explain without backing up a long way into recursion and iteration and their expression in functional languages and how they are usually optimized because it exists in dialog with that history

andy.fingerhut02:08:55

You are certainly welcome to ask here, of course, about any such examples, but in general, Google and other search engine results are not the decider of what is 'official' documentation.

GC02:08:57

It's kinda confusing because https://clojure.org/api/cheatsheet, the cheatsheet on official site also points to clojuredocs

andy.fingerhut02:08:23

What programming language(s) are you familiar with already?

seancorfield02:08:07

The official docs/site (http://clojure.org) points to a lot of community resources.

GC02:08:41

Java, python, C, javascript

andy.fingerhut02:08:42

So you may get this in slightly different variants from multiple people here, if we're not careful 🙂 I'll let hiredman go for it 🙂

hiredman02:08:25

Often in functional langues iteration (for loops or while loops in C) are expressed as recursive function calls and the compiler recognizes those recursive calls as being loops and compiles them as such. Clojure doesn't do that, and instead provides the less general combination of loop/recur which in usage follow the same pattern as an iterative process encoded as recursive function calls

hiredman02:08:23

(this might be a good point for something about for not being a loop in clojure)

seancorfield02:08:39

(Clojure's for loop is like for-comprehensions in Python, I think?)

seancorfield02:08:11

(! 604)-> python
Python 2.7.10 (default, Feb  7 2017, 00:08:15) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> [2 * x for x in [1, 2, 3, 4]]
[2, 4, 6, 8]
>>> ^D

Wed Aug 07 19:18:23
(sean)-(jobs:0)-(/Developer/workspace/wsmain/build)
(! 605)-> clj
Clojure 1.10.1
user=> (for [x [1, 2, 3, 4]] (* 2 x))
(2 4 6 8)
user=> 

andy.fingerhut02:08:47

I actually have a hard time not jumping in, after all. Maybe one way to think of loop in Clojure is that while it is possible to write loops similar to how they are written in imperative languages, it is definitely not typical "idiomatic" Clojure code. A purely functional loop expression does not modify any values that are visible when you enter the loop, and technically it doesn't modify the 'loop variables' either. The first time you start the loop, the symbols given in square brackets are 'bound' (similar to assigned, but only once) to the initial values shown.

andy.fingerhut02:08:58

Leave out the possibility of nested loop expressions for simplicity for the moment.

andy.fingerhut02:08:29

The loop body is evaluated, say having some conditional expressions like if or cond, but no assignment statements like you would find in imperative languages.

andy.fingerhut02:08:05

Either that branching ends with evaluating an expression that is not a recur expression, and that value becomes the return value of the entire loop expression.

andy.fingerhut02:08:12

Or it ends with evaluating a recur expression. That is similar to a recursive call to the beginning of the loop expression, with new initial values for all loop 'variables' (except they are not variables). So those loop variables are kinda sorta "assigned a value only once at the beginning of each loop iteration", not at arbitrary points in the middle.

andy.fingerhut02:08:40

Not sure if that is clear, but I'll take a breath there.

seancorfield02:08:05

To illustrate Andy's description

user=> (loop [acc 0 n 10]
         (if (pos? n)
           (recur (+ acc n) (dec n))
           acc))
55
user=> 

andy.fingerhut02:08:46

So that is maybe a 'negative' way to explain it, by the restrictions imposed on you -- you get to 'assign' (really 'bind') a value to the loop 'variables' (more technically 'symbols') at most one time per iteration through the loop, not any number of times you want like you could in imperative programming languages. Also the loop symbols are purely local to the loop -- they are out of scope when the loop returns (again, unlike in most imperative languages).

andy.fingerhut02:08:26

What do you get in return for those restrictions? You get something easier to reason about. Frankly, it can be a little mind-twisting at first when trying to write such loops, and there are plenty of other Clojure functions that handle the common cases of what you want to do looping for, e.g. map, filter, remove, reduce, and those are often clearer than a loop, when they do what you need.

👍 4
GC03:08:08

Going through stuff you guys wrote

GC03:08:19

and that example helped a lot

andy.fingerhut03:08:44

Yeah, probably Sean had a better idea to start with a small useful example, rather than throwing all the general rules at you as I did 🙂

👍 4
GC04:08:33

I think I understand it now, played around with Sean's example, now I can understand more complex examples too

seancorfield05:08:36

Excellent! I think loop/`recur` and for trip beginners up quite a bit depending on their background / other languages.

👍 4
seancorfield05:08:58

Since you know Python, Clojure's for is probably more familiar already.

GC06:08:07

I have one question , might not be related to clojure, so if I specify +10:00 as timezone in clojure, which means melbourne is +10 hours ahead of UTC, does it affect calculations, when day light saving hours are used or not

jumar08:08:54

It depends on how you set it but if you just use an offset, than I guess the DST is not accounted. But you should probably only use ZoneId (when speaking in terms of java.time)

manutter5111:08:54

I had to write a function for printing an Instant formatted for local time just the other day:

(defn format-ts
  [timestamp]
  (let [zone (ZoneId/systemDefault)
        zone-rules ^ZoneRules (.getRules zone)
        zone-offset (.getOffset zone-rules timestamp)
        local-time (LocalDateTime/ofInstant timestamp zone-offset)
        fmtr (jt/formatter "YYYY-MM-dd HH:mm:ss")]
    (jt/format fmtr local-time)))

Andros08:08:56

What is the most generalist option for web development, Luminus?

Chase09:08:35

so recur has to come in the "tail position" right? So I often find myself changing the logic in my head and function to put the recur in the else branch of the if statement. But in seancorfield's loop/recur example above, it is in the then position which I guess makes sense because only one branch is evaluated so whichever branch it is is considered the tail position?

👍 8
jaihindhreddy09:08:54

One way to deal with this is to take whatever is causing the other branch, and make it another argument to your fn.

schmee09:08:24

“tail position” means that the recur has to come last in whatever statement it’s in, so it’s not correct to say that the “then” or the “else” is in tail position, both those statements can contain recur as long as it is the last form. for instance, this is perfectly legal:

(loop [x 0]
  (if (< x 10)
    (recur (+ x 2)
    (recur (+ x 1)))

schmee09:08:53

(that this is an infinite loop is another story 😂 )

Chase09:08:41

makes sense to me. thanks for the clarification

borkdude09:08:09

periodic reminder: if there are beginners here who would like to work on open source issues, here are some ideas: https://clojureverse.org/c/questions-help/starter-issues

thumbsup_all 4
jumar10:08:42

a much bigger list (although not sure if up-to-date) is here: http://open-source.braveclojure.com/

Anik Chowhdury10:08:22

hi everyone. I am newbie in clojure. I want to know how to halt future in clojure from executing functions before finishing future's processing?

jumar10:08:31

Not exactly sure what do you mean but perhaps you look for future-cancel?

Anik Chowhdury11:08:58

@U06BE1L6T Dont want to cancel future, just to halt future from executing some dependancy functions before future's processing.

jumar11:08:54

There's nothing like that, imho. You should do this explicitly inside your future if it has some dependencies. Relying on thread scheduling is extremely fragile

Anik Chowhdury11:08:22

How to do multi threading using go block of core.async ? can anyone give some examples? i have gone through docs but can't understand properly yet.

Drew Verlee13:08:43

I just read that someone was upset that > a macro can only evaluate to a single form I'm confused, first can some point to the definition of form so I can be 100% I get it? Secondly, assuming a form can be a collection, this wouldn't seem to be a limitation.

Serhii Fesiura13:08:43

Hi guys. Does clojure compile the code to jvm bytecode or java sourcecode?

manutter5113:08:09

Loosely speaking it’s a unit of code wrapped in parentheses, so (def foo 1) is a form and (let [a 1] (println a)) is a form inside a form (but the [a 1] bit is not itself a form).

manutter5113:08:28

Clojure compiles to jvm bytecode.

👍 4
Serhii Fesiura13:08:40

What about strict types? I got null pointer exception few times. How often do you use defrecord deftype?

Alex Miller (Clojure team)13:08:27

The bytecode compiled from Clojure is not directly representable as Java source code

👍 4
Alex Miller (Clojure team)13:08:00

When doing interop, Clojure makes no effort to protect you from NPEs and that's the main place you'll see them

Alex Miller (Clojure team)13:08:12

Not sure what your question is about strict types

Alex Miller (Clojure team)13:08:53

deftype is used rarely (it's really for creating new data structures). defrecord is used sometimes, depends on need, often maps suffice

Serhii Fesiura13:08:42

Currently, I build my small program only with lists. And now, I start suspecting that I may do something wrong. And there is a better way.

Alex Miller (Clojure team)13:08:11

well certainly vectors and maps are used far more commonly than lists

Alex Miller (Clojure team)13:08:28

but I wouldn't recommend making "types" with defrecord unless you have a good reason to do so

4
Serhii Fesiura13:08:48

I a little bit affraid of vectors. Because list is a ... list. But I do not know, how does clojure allocate a new memory for it.

Serhii Fesiura13:08:24

I know that php and js are allocating memory by blocks, for future.

Serhii Fesiura13:08:45

As I understand vector is an analog to array.

Alex Miller (Clojure team)13:08:11

it has an indexed api, like arrays

Alex Miller (Clojure team)13:08:28

the implementation is actually a tree (of arrays)

Alex Miller (Clojure team)13:08:42

but I'm not sure why you should care

Serhii Fesiura13:08:29

About NPE. I want the compiler to check, that a value with index :a (for example) will be in that map. That`s why I am thinking about records.

Alex Miller (Clojure team)13:08:19

you won't get npes when using maps

Serhii Fesiura13:08:32

even untypized maps?

Alex Miller (Clojure team)13:08:49

maps just return nil if the key is not present

Serhii Fesiura13:08:04

Thank you a lot.

Alex Miller (Clojure team)13:08:06

and you can supply a value to return instead if needed

Alex Miller (Clojure team)13:08:16

maps and records implement the exact same interfaces and in general (there are a couple exceptions, but they are not something you will normally run into) will give you the same result, so consumers don't care

csd17:08:44

for larger projects, how do people like to organize their specs?

cbleslie23:08:07

Is there an implementation of a macro similar to a Ecma Script “Template literal”? I have some (println) forms in a small project that are suffering from a rash of \t and \n. Looking a bit messy. Wondering if the community has a standard procedure for outputs like this.

cbleslie23:08:00

For example

(println
      "\tBranch: "(:git.branch body) "\n"
      "\tCommit ID: "(:git.commit.id body) "\n"
      "\tCommit ID Short: "(:git.commit.id.abbrev body) "\n")

noisesmith23:08:47

you could use format, and strings are allowed to span multiple lines and have real newlines in them

noisesmith23:08:37

(println (format "\tBranch: %s
\tCommit ID: %s
\tCommit ID SHort: %s
"
                      (:git.branch body)
                      (:git.commit.id body)
                      (:git.commit.id.abbrev body)))

noisesmith23:08:02

it does look awkward that the next line of the string needs to be unindented though

cbleslie23:08:42

Sometimes stuff like this is so subjective to the reader, right?

noisesmith23:08:10

yeah - also note that you could probably do something with string/join for the \n\tab combos

hiredman23:08:24

you can also use with-out-str

hiredman23:08:42

or use print and then println at the end

hiredman23:08:29

and there is printf, which combines print and format

noisesmith23:08:00

so (println (apply format (string/join "\n\t" ["\tBranch: %s" "Commit ID: %s" "Commit ID Short: %s"\n"]) (map body [:git.branch :git.commit.id :git.commit.id.abbrev]))))

cbleslie23:08:50

I am spoiled for choice.

cbleslie23:08:12

Truly a massive core library.

cbleslie23:08:56

Thoughts? Worth it?

hiredman23:08:28

something like

(defn p [form]
  (if (coll? form)
    (doseq [thing (seq form)]
      (p thing))
    (print form)))
could be useful

hiredman23:08:41

user=> (p [\tab "Branch: " (gensym) \tab "Commit ID: " (gensym) \tab "Commit ID Short: " (gensym) "\n"])
	Branch: G__145	Commit ID: G__146	Commit ID Short: G__147
nil
user=>

noisesmith23:08:49

if you want templating i'd look into a real string template lib there's a few good ones

cbleslie23:08:20

Recommendations always welcome.

seancorfield23:08:04

yogthose/Selmer is always my go-to library for templating.

cbleslie23:08:35

@seancorfield Cool. I will check it out.