Fork me on GitHub
#beginners
<
2018-03-11
>
brandon.ringe00:03:33

I think I found one. Looked it up right after your response.

mfikes00:03:09

I bet you can even get fancier by using something like component to try to preserve state in the face of reloads.

suryapjr02:03:34

Morning !!!

suryapjr02:03:14

When to use (delay ) in my code

isac_asj9102:03:34

Hi, you’ve ebook about clojure for recommend me? I beginning with clojure.

suryapjr03:03:43

Clojure for the true and brave

suryapjr03:03:59

A very good book.... That moves at a decent pace

suryapjr03:03:48

Living clojure will get your started very quickly

noisesmith03:03:35

delay is useful for situations where you might want to evaluate some code, but not yet, and if you do you might use the value multiple times but only want to calculate it once

noisesmith03:03:55

like a lazy-seq but it's just one value

suryapjr03:03:21

Great !! Gracias!

jlmr08:03:27

Hi! I'm trying to get Sente to work. Right now client and server seem to be connected (I see ping messages). But when I try to push a message from the server to a client like this ((:send-fn channel) "dabecca4-ca3a-496a-9016-cf46ddc34f45" [:sample.event/some-id {:data "data"}]), I get the following error: java.lang.IllegalArgumentException: No implementation of method: :pack of protocol: #'taoensso.sente.interfaces/IPacker found for class: taoensso.sente.EdnPacker

jlmr08:03:12

I have been staring at the code for a couple of hours now and can't think of anything else to try

jlmr08:03:21

Any help would be appreciated

nikoszp09:03:08

Hey peeps, I'm using Brave and True to learn Clojure and I've noticed that it doesn't even mention the for iterator. Is that intentional you think? Is using for - unless absolutely necessary - regarded as bad form in Clojure?

gklijs10:03:53

@nikoszp I think they don't mention it because it's not that often used. Most cases where you could use for, it's more idiomatic to use map, reduce, or doseq. It's useful to generate multiple elements from a list for example.

suryapjr11:03:58

Guys.. I have a list : (def mylist '() ) How do I write a function that adds elements to the list by user input without the use of atoms

gklijs11:03:41

Why no atom? Clojure is immutable by default, but you could use a transient vector to store them which is mutable, or use java interop and use a java list. But really one of the greatest things in clojure is the atom, cause it's a very nice and easy way to work with concurrency.

curlyfry11:03:20

@suryapjr Sounds a bit like you still need to get into a functional mindset! πŸ™‚ (conj mylist the-new-input) will return a new list with the element added. You'll use this new list in whatever coming function calls you're performing. What is the end goal in your program? It will be a bit easier to help if you elaborate some more. PS: Using the list literal '() is fairly uncommon (in fact, I've never seen it in production code). Vectors [] is the data structure people tend to use for ordered collections.

suryapjr12:03:24

@curlyfry true as (= 1 1)

suryapjr12:03:54

Not getting into FP properly

suryapjr12:03:35

I want to see the elements added in my list in the end

suryapjr12:03:09

(conj mylist input) returns a new list which has to assigned to a variable using def or let ..but since I don't know how much elements will be added in the future,I can't bind values to def ..

suryapjr12:03:58

@gklijs..someone told me that atoms are rarely used in clojure πŸ™‚

curlyfry12:03:05

I try to avoid atoms as much as possible unless there is no other way to do something (usually something inherently stateful from "the outside world" such as a database connection)

curlyfry12:03:28

@suryapjr I'll try to write up a quick example of a more functional user input loop, just a sec!

suryapjr12:03:49

Thanks ! @curlyfry

gklijs12:03:54

Eventually you want to have state somewhere, it's easier to have it in an atom than external. If you just want to use the list for something else you don't need an atom.

suryapjr12:03:31

@gklijs agreed..but since list is immutable ..need a atom

gklijs12:03:57

Transient list is not

noisesmith13:03:25

@gklijs @suryapjr transients need to be used as if they were not mutable, and there is no such thing as a transient list, only associative data is transient and lists are not associative. If you use transients as if they were mutable (not using the return value) you will lose data.

suryapjr12:03:15

Transient list ??..I'll look it up..

curlyfry12:03:30

Here is an example of updating a list based on a user input loop:

(defn user-input-loop [input-list]
  (let [user-input (read-line)
        updated-list (conj input-list user-input)]
    (println updated-list)
    (when-not (= user-input "quit")
      (recur updated-list))))

(defn start-input-loop []
  (user-input-loop []))

curlyfry12:03:36

without using any atoms!

curlyfry12:03:50

You start the loop using (start-input-loop)

curlyfry12:03:59

@suryapjr Try to see if you understand this example. The gist of it is that we are calling the user-input-loop function over and over again (with recur) using the new, updated list as an argument.

leonoel12:03:19

@pooboy @gklijs transients can't model identity and I would definitely not suggest it to a beginner

curlyfry12:03:34

I agree with @leonoel

suryapjr12:03:00

I understood the example..but why is there another function called start-input-loop

suryapjr12:03:18

@curlyfry..great example ... :+1:

curlyfry12:03:05

It's just a small convenience so we don't have to give an empty vector as an argument when we start the user-input-loop. Basically just a habit of mine!

suryapjr12:03:43

@curlyfry..when-not ?

curlyfry12:03:12

Look it up on clojuredocs!

suryapjr12:03:17

What's recur ?

suryapjr12:03:23

I'm not getting it

mfikes12:03:22

@suryapjr Consider this example

(defn foo [x]
  (if (zero? (rem x 107))
    x
    (foo (inc x))))
and what happens to the stack if you call it with (foo 110). You can eliminate the stack use by replacing foo with recur:
(defn foo [x]
  (if (zero? (rem x 107))
    x
    (recur (inc x))))
In this case, the recur target is foo. There is a more general form where you can use loop as your target, but this illustrates the general idea. It ends up feeling like an imperative loop, and in fact it has the same performance, but it remains a functional construct.

suryapjr12:03:57

@mfikes Crystal clear !! Thanks !!

mfikes12:03:24

@suryapjr Sometimes it helps me to see the code generated for these things. If you start up Lumo or Planck with the -v option (for verbose) you would see the JavaScript generated for each, which might help in seeing what it does in a more familiar language. The first produces

cljs.user.foo = (function cljs$user$foo(x){
if((cljs.core.rem.call(null,x,(107)) === (0))){
   return x;
} else {
   return cljs.user.foo.call(null,(x + (1)));
}
while the second produces an actual imperative loop, involving an assignment to x:
while(true){
  if((cljs.core.rem.call(null,x,(107)) === (0))){
     return x;
  } else {
     var G__22 = (x + (1));
     x = G__22;
     continue;
  }
  break;
}

suryapjr13:03:24

@mfikes :+1:

suryapjr13:03:32

What are some of the best practices for writing more readable clojure?

mfikes13:03:53

@suryapjr A couple of lessons to learn: 1) Don’t try to create very compact, terse code. Use (fn [ ] ...) instead of #( ... ) more often than you might initially think. 2) Try to break things apart. You can end up with a function that essentially looks like a gigantic single equation on a blackboard, which is hard to read. Find the pieces inside of it and break them out into separate functions.

mfikes13:03:33

Having said the first item above, there is definitely a balance, and some constructs leading to shorter code make it more readable. For example, using destructuring. And, if you can express an idea more succinctly with less code, it can be more readily understood. Just don’t try to squeeze it down beyond where it becomes hard to read.

mfikes13:03:15

Another bit of advice for readable code is to not to initially worry too much about performance. Write code that is clear and if it is fast enough, then you end up with a readable result. πŸ™‚

mfikes13:03:18

The threading macros -> and ->> often lead to more readable code

suryapjr13:03:44

@mfikes great !!!

tbaldridge14:03:49

@suryapjr and take the clojure-style-guide with a grain of salt. Most devs I know have more than a few objections with it. To a pirate from a movie: "it's more of what you call guidelines than actual rules".

suryapjr14:03:51

@tbaldridge noted !!

suryapjr14:03:28

Guys ..should I master clojure and then jump to clojurescript?

suryapjr14:03:42

Or I can learn both of them simultaneously?

tbaldridge14:03:16

you can learn them both. 90% of the languages is the same, it's only when you dig into more complex topics that the differences show up.

suryapjr14:03:45

I want to use ArrayList from java

suryapjr14:03:25

How should I proceed? I did this ..

(import java.util.ArrayList)

(def a (new ArrayList))

spacemods14:03:32

(.add a 1) (.add a "apple") works - it's untyped, though

tbaldridge14:03:22

@suryapjr right, a.add(42) in Java becomes (.add a 42) in clojure

suryapjr14:03:11

Coool..this is greatly

suryapjr14:03:20

When I was importing the Java.util.Random library , I had done this : (.nextInt (java.util.Random). 100)

suryapjr14:03:30

And it had worked..

suryapjr14:03:10

For ArrayList, ` (def a (new ArrayList)) `

suryapjr14:03:29

More than one way of doing things in clojure ? πŸ˜€

spacemods14:03:03

https://clojure.org/reference/java_interop lists both of those next to each other - I think they're the same

matan14:03:37

Why was it so important not to implement contains? over lists, in clojure core? how would you support that design decision? thanks!

sundarj14:03:28

one of the principles underlying Clojure's library of functions is that everything has a consistent performance profile. contains? only doing a key lookup means it's always as fast as a key lookup; if it were to support lists as well it would be slow for those cases because the only way to check if a list contains a value is to iterate through it from the start to the end

sundarj14:03:47

this is the same reason that e.g. conj adds to the head for lists but the tail for vectors

mfikes14:03:23

@matan Well, lists are not keyed by indexes

mfikes14:03:20

But, perhaps you are asking a different question; you can do (.indexOf '(:a :b :c) :b) in both Clojure and ClojureScript

mfikes14:03:33

Combine that with neg? and you have a way to do it.

mfikes14:03:16

Using the example from that post

(def beatles ["John" "Paul" "Ringo" "George"])
(not (neg? (.indexOf beatles "Ringo")))

matan15:03:30

Thanks a lot, for reminding me of the reason. I knew there was a logical reason but couldn't recall it.

mfikes14:03:02

If you want to iterate in Clojure: (some #{"Ringo"} ["John" "Paul" "Ringo" "George"])

matan15:03:37

thanks a lot!

suryapjr14:03:25

Guys when we do ...

(def magic (new Random)) 

suryapjr14:03:39

Do we create a object?

mfikes14:03:38

Yes, and an alternative syntax for (new Random) is (Random.)

mfikes15:03:19

The . at the end is a macro-ish thing.

(macroexpand '(Random.))
to see

suryapjr15:03:24

But clojure is a functional language,right ? :)

suryapjr15:03:33

Noob question..sorry

mfikes15:03:40

Definitely, but it lets you do imperative things if you really want

sundarj15:03:53

Clojure is functional, but Java is not

suryapjr15:03:23

Java interop is very useful for clojure

sundarj15:03:51

indeed πŸ™‚

suryapjr15:03:35

What are macros used for

suryapjr15:03:04

Contains? Only works on hash-sets?

bronsa15:03:09

extending the core syntax, stuff like cond or -> are macros

bronsa15:03:31

contains? works on sets maps and vectors (but on vectors it tests on index, not on element equality)

suryapjr15:03:01

Can you give me a simple example of a macro ?

bronsa15:03:32

of the definition of a macro?

bronsa15:03:06

(defmacro my-when [test & body] `(if ~test (do [email protected])))

suryapjr15:03:01

Looks like a func

bronsa15:03:13

macros are functions of data to data

bronsa15:03:22

except the data is then interpreted as code

suryapjr15:03:25

Thanks @bronsa!

mfikes15:03:09

@suryapjr As an exercise, if you try to write my-when above as a function, you will come away understanding why it needs to be a macro

suryapjr15:03:39

@mfikes sure !!!

suryapjr16:03:25

When we call java in clojure..take the example of a linked list ..we can mutate the object.. immutability is switched off during interop with Java ?

alexmiller16:03:09

no, you can mutate

alexmiller16:03:33

you’re just making the exact same call you’d make in Java, with the same effects

suryapjr16:03:43

Yesss...normally clojure is immutable but when interop with Java ..we can mutate

sundarj16:03:58

@suryapjr immutability isn't a property of Clojure the language itself, it's a property of the data structures Clojure uses. Java has immutability for e.g. strings, but for most other data structures in Java, you mutate to use them (even from Clojure).

sundarj16:03:13

Clojure's strings are in fact just Java strings (since they're immutable):

=> (type "")
java.lang.String

sundarj16:03:23

you can also use Clojure's immutable data structures from Java, just like you can use Java's mutable data structures from Clojure

suryapjr16:03:02

@sundarj thanks for the detailed explanation ,mate.

sundarj17:03:58

no worries

tkjone17:03:13

Hey all, for the new clj --main cljs.main --compile hello-world.core --repl command. This is going to implicitly look for hello-world.core within a src directory, yes? How can I change where this command looks for my namespace?

rauh18:03:26

@tkjone Add :paths ["foo/bar"] to your deps.edn

tkjone18:03:49

Nice. Thanks!

dpsutton18:03:30

https://github.com/clojure/clojurescript/wiki/Using-cljc#general-considerations this seems to say that src/cljc/project-name is acceptable. but when using the clj command line tool its not finding this. is there something i'm doing wrong?

dpsutton18:03:03

[[email protected] paths]$ tree
.
β”œβ”€β”€ deps.edn
β”œβ”€β”€ scripts
β”‚   └── run
└── src
    └── cljc
        └── paths
            └── paths.cljc

noisesmith18:03:57

if src/cljc is on your path, you should be able to require paths.paths

dpsutton18:03:05

clj sees it automatically when its at src/paths/paths.cljc but not with the intermediate level of cljc folder there

noisesmith18:03:24

then you need to make sure src/cljc is on your classpath

noisesmith18:03:38

I wouldnt' expect that to be automatic

noisesmith18:03:34

also, separate folder structures for clj/cljc/cljs isn't needed, but it is a convention some people like

noisesmith18:03:39

once src/clj, src/cljc, and src/cljs are all on the classpath, as far as clojure is concerned the stuff underneath could be in any of those directories

dpsutton18:03:43

thanks. i'm just now venturing into cljc territory and want to use clj. a lot of moving parts πŸ™‚

tkjone18:03:04

Question about clj --main cljs.main and the watch option If I have a directory structure like this:

test
   src
       test.cljs
   deps.edn
How would I run watch on the above? I have been trying clj --main cljs.main --compile test --repl --watch test or clj --main cljs.main --compile test --repl --watch src Further, it is my understanding that running watch will recompile my test.cljs file when a change is found on file save? (Yes, single segment names are not good, I am just playing around πŸ™‚ )

tkjone19:03:28

For anyone who gets stumped on this, the order of the --watch CLI option seems to matter. To get this to work:

clj --main cljs.main --watch src --compile test --repl

mfikes19:03:32

Yeah, do -h and it will explain the argument order.

matan20:03:04

how would I force lazy seqs to show their values in the repl?

noisesmith20:03:43

matan use pr-str instead of str

matan20:03:03

So say I have this function:

(defn regex-escape [s]
  " turn a regex agnostic string into its corresponding escaped regex string "
  (str
    (map
        #(if (clojure.string/index-of "#$()*+.?[^{|\\" %)
              (str "\\" %)
              %)
         s)))

noisesmith20:03:06

pr is what the repl uses to show values, so if you directly return the lazy-seq to the repl you will see the right thing

noisesmith20:03:30

use apply str instead of just str in that code

noisesmith20:03:45

because the string form of the lazy-seq still isn't what you want

noisesmith20:03:39

@matan to be clear: (str (map ...)) gives a silly object notation, (pr-str (map ...)) is a string of a list of things, (apply str (map ...)) is the string made of each thing returned by map

matan20:03:57

mmmm that works. trying to figure why from your last comment. thanks!

noisesmith20:03:39

user=> (str (map identity "hi"))
"[email protected]"
user=> (pr-str (map identity "hi"))
"(\\h \\i)"
user=> (apply str (map identity "hi"))
"hi"

noisesmith20:03:23

and for symmetry

user=> (apply pr-str (map identity "hi"))
"\\h \\i"

matan20:03:40

yeah should be easy, just a long day....

matan20:03:39

oh of course, apply makes perfect sense

matan20:03:57

tinkering it after solving my itch, this is a little confusing:

=> (str ["3333" "3333"])
"[\"3333\" \"3333\"]"

noisesmith20:03:58

that's the string representation of a vector of strings - it looks better if you call println on it

matan20:03:27

okay πŸ™‚ kind of internal-ish but makes sense

matan20:03:36

thanks again for pointing out my error

noisesmith20:03:38

I don't think you made an error - I'm just saying that's why it looks like that

noisesmith20:03:58

it's a little odd since (str "foo") isn't "\"foo\""

matan20:03:04

well thanks none the same!

matan20:03:43

I wish to share something more abstract. Compared to typed languages, I find that for me writing clojure creates a lot of technical debt: it is simple to put together a small library, but then unless I sprinkle type and shape checking all over the place, it is close to impossible to evolve and maintain the code later on. Any caller can easily abuse my code with wrong input typing, and most of all, I'd find it hard to follow what types a function expects when I come back to old code.

sundarj21:03:40

have you tried core.typed?

sundarj21:03:19

i know of at least one company that moved away from core.typed and towards schema, though: https://circleci.com/blog/why-were-no-longer-using-core-typed/

sundarj21:03:06

they seem to be using spec a lot these days

matan15:03:33

@sundarj well thanks, I guess above all this suggest that they'll also drop spec and clojure, as it is very much core to how clojure programming really works for projects that aren't small!

matan20:03:32

So if I need to deliver and maintain quality code, I end up working harder to type-check stuff with my own lines of code, compared to a typed language, and using spec for that isn't that elegant/compact compared to typed-languages.

matan20:03:01

Are you guys sure that large teams can use clojure like that?

noisesmith20:03:22

@matan I've had these same problems

noisesmith21:03:04

part of this is a trade off for lisps - I'm very interested by the addition of extensible variant types in recent OCaml versions (but this goes into off topic)

lee.justin.m21:03:05

@matan One thing: I would point you to prismatic/schema instead of spec. spec so so heavyweight that i found myself avoiding it. schema has a much lighter syntax and does the thing I care about the most: check the shape of the map while acting as internal documentation. if you’ve ever used flow type or typescript, the feel of schema is very similar

lee.justin.m21:03:28

of course it is runtime

joelsanchez21:03:45

using spec with the purpose of checking incoming data and acting as internal documentation is made much better by https://github.com/metosin/spec-tools#data-specs

lee.justin.m21:03:18

I still get caught out by some of the complexity of spec. Like instrumenting functions that have side effects, for example.

lee.justin.m21:03:31

spec is doing a lot of stuff whereas schema mostly does one thing

yogidevbear21:03:32

Hi everyone πŸ‘‹ Curious if anyone knows how easy it would be to get a nested sequence collection based on knowing a value within the sequence? For example, given [(:foo :bar) (:baz :bat)] and I know :bat, I would like to get (:baz :bat)

joelsanchez21:03:57

(->> '[(:foo :bar) (:baz :bat)]
     (filter #(= (last %) :bat)))

yogidevbear21:03:35

Won't necessarily be the last item though πŸ™‚

yogidevbear21:03:48

Guessing I could use filter with something like contains?

joelsanchez21:03:10

yep, basically you need filter, also you'd want to use first since filter returns a coll

joelsanchez21:03:55

if you need more advanced filtering consider specter

yogidevbear21:03:00

Cool, thanks. Trying to avoid extra libraries on this one as it's a learning exercise πŸ™‚

noisesmith21:03:55

filter returns a lazy-seq, which doesn't work with contains?

noisesmith21:03:27

contains? is specifically for associative structures, and only looks by key

noisesmith21:03:32

user=> (contains? '(a b) 'b)

IllegalArgumentException contains? not supported on type: clojure.lang.PersistentList  clojure.lang.RT.contains (RT.java:814)
user=> (contains? '[a b] 'b)
false
user=> (contains? '[a b] 1)
true
user=>  ('[a b] 1)
b

joelsanchez21:03:31

i think he was talking about the test used for filtering, such as

joelsanchez21:03:35

(->> '[(:foo :bar) (:baz :bat)]
     (filter #(contains? (set %) :bat)))

joelsanchez21:03:43

implying the input sequences are actually meant to be sets

noisesmith21:03:04

oh, right, I misread

yogidevbear22:03:05

@noisesmith just come back to slack. I am hitting that exact issue re lazy-seq and contains? not supporting lazy-seq

noisesmith22:03:01

right, if you have a specific question about my example above let me know - the simple thing might be to call set on the thing first

yogidevbear22:03:39

Perfection. Thanks @noisesmith. I wrapped my % in my anonymous function for the contains? and it sorted the issue