Fork me on GitHub
#beginners
<
2017-09-22
>
practicalli-johnny07:09:09

Anyone suggest coding kata sized challenges (1-4 hours long) for those just starting clojure / clojurescript? Links to simple solutions a bonus. Would like to add some challenges to the Clojurebridge content we are updating. Thanks.

vinai08:09:55

@jr0cket How about the usual katas, e.g. prime factors, bowling game, string calculator and so on?

vinai08:09:18

I don't have links to solutions but happy to drop them into a gist

vinai08:09:15

Can't promise my solutions are idiomatic though, I used them to practice while learning, too.

vinai09:09:06

Here are a few: Prime factors: https://gist.github.com/Vinai/a4d6c56e1a92f388edb72ffd30513733 String calculator: https://gist.github.com/Vinai/ca004731d8e1fb385bc238d997cdc3d3 Print diamond: https://gist.github.com/Vinai/8294a03975d1f67563b258636bbdbda7 Roman numerals: https://gist.github.com/Vinai/47c4d607567bab159781fd4c3c01e9bc Another fun one that takes a few hours is the ugly trivia legacy code refactoring kata: https://github.com/jbrains/trivia/tree/master/clojure In order of ascending complexity, I think those would be 1. Roman numerals 2. String calculator 3. Prime factors 4. Print diamond 5. Ugly trivia

practicalli-johnny09:09:01

genec10:09:20

@seancorfield @schmee Here's the smallest example I could find that will reproduce the problem. I'm using Atom/Proto-Repl, When I evaluate the file, the Main Form is displayed. When I close the Main Form, it closes and I get the message "REPL Closed" in Porto-Repl. Then I have to restart Proto-Repl, and everything works fine again. (ns sherpa.main-form (:use [seesaw.core])) (def main-frame (frame :title "Main Form" :minimum-size [400 :by 200] :on-close :exit)) (-> main-frame show! pack!)

genec11:09:55

and if I run the code from a terminal REPL I get the following error when I close the window SocketException The transport's socket appears to have lost its connection to the nREPL server clojure.tools.nrepl.transport/bencode/fn--10199/fn--10200 (transport.clj:95) clojure.tools.nrepl.transport/bencode/fn--10199 (transport.clj:95) clojure.tools.nrepl.transport/fn-transport/fn--10171 (transport.clj:42) clojure.core/binding-conveyor-fn/fn--4676 (core.clj:1938) java.util.concurrent.FutureTask.run (FutureTask.java:266) java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1142) java.util.concurrent.ThreadPoolExecutor\$Worker.run (ThreadPoolExecutor.java:617) java.lang.Thread.run (Thread.java:745) Bye for now!

genec11:09:48

so the moral is read the docs (lol) Note that :exit will cause the entire JVM to exit when the frame is closed, which may be reasonable for a standalone app, but probably isn't for use in the REPL. The default value is `:hide`. Changed :on-close :exit to :hide and works fine. Then you can re-show the form with (show! main-form)

Ben Redden11:09:22

is it safe to think of Vectors as similar to Arrays? (coming from JavaScript world)

Ben Redden12:09:29

lol i’d just found that, actually. it seemed like it was pretty similar to an array, but wanted to check before I assumed

genec12:09:41

It is but it isn't held in contiguous memory like arrays in most languages, but that's not an issue performance wise. I find the community docs are very good too. https://clojuredocs.org/clojure.core/vector_q

Ben Redden12:09:25

cool, thanks for the info! lots of new stuff to learn, coming from a primarily PHP/Node mindset

genec13:09:57

I've done a lot of F# and a bit of Clojure over the last few years, I really like Clojure a lot. The .net / .netcore stuff is a mess right now.

genec13:09:18

Clojure / Clojurescript just seems to work easily and the community / conferences are great

schmee12:09:37

@genec haha yes, `:on-exit` is sneaky like that, that’s why I asked 🙂

genec15:09:59

:on-close :dispose seems to do the trick, closes the frame and the repl stays open

genec12:09:46

@schmee yeah, thanks! I'm finding seesaw really nice for prototyping a simple GUI

rcustodio14:09:17

which one is better?? core.async or java Thread?

rcustodio14:09:34

or core.async just uses java Thread behind

vinai14:09:24

@rcustodio I think it depends on what you want to do. core.async seems to be the choice of many though.

Charleshd14:09:40

If you want to build some asynchronous application core.async is better. If you want to just spawn threads here and there java Threads are great.

vinai14:09:43

For blocking stuff maybe plain Threads or futures are more suited than core.async

rcustodio15:09:59

I’m doing a service that will get some things in asynchronous and then work with the result

rcustodio15:09:18

Quite simple

vinai15:09:12

Sounds like core.async would be a good way to go.

rcustodio15:09:53

Do they use Java Thread or its different?

vinai15:09:35

They use some form of threads from a thread-pool reserved for core.async. Probably at some level it will be Java threads, but I don't know.

rcustodio15:09:37

I see... thanks

leonoel15:09:08

@vinai @rcustodio https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async/impl/exec/threadpool.clj core.async delegates to the Executor framework, which uses java threads, which wrap system threads on most runtimes

chris15:09:12

this is likely an implementation detail

chris15:09:21

so don't rely on that fact

chris15:09:45

(though, yes, the clj version of core.async will probably always use Threads and a threadpool of some sort)

leonoel15:09:35

what is not an implementation detail is that go blocks should be free of blocking operations

noisesmith16:09:45

core.async uses a small pool of threads that are shared by all go blocks in a cooperative manner, the thread is kept until you use a channel to read or write, then another go block can use it

noisesmith16:09:17

this is more than just an implementation detail - if you don't understand how it's working you can starve core.async easily, resulting in none of your go blocks executing at all

noisesmith16:09:54

this is why you shouldn't do expensive tasks (including and especially blocking IO) inside core.async go blocks

schmee18:09:40

what are you supposed to do inside go blocks?

schmee18:09:57

all I ever see here is “don’t use them for this”, “don’t use them for that” 😛

noisesmith18:09:48

@schmee coordinate between various asynchronous operations

noisesmith18:09:18

the issue is that asynchronous coordination is a problem that often comes up when you are doing thing parallel, which gets people confused and they assume they can use go blocks for parallelism

noisesmith18:09:21

which breaks things

schmee18:09:33

out of curiosity: does the same principle apply to Go channels?

schmee18:09:46

or do they use fibers or something to get around that?

noisesmith18:09:10

I'll let someone else answer that

bfabry18:09:19

go blocks are a godsend if you are dealing with an API that uses callbacks

rcustodio18:09:58

for parallel tasks future is a good choice?

rcustodio18:09:32

`(let [x (future (http/get x)) y (future (http/get y))])`

noisesmith18:09:36

it's usually the simplest option, as long as you are starting a small number and don't need to do any interesting coordination

rcustodio20:09:56

Hi... is atom the only way???

``````clojure
(defn- get-orders [items access-token]
(let [orders (atom {})]
(when (= (:status @items) 200)
(loop [orders-id (distinct (mapv #(:order_id %) (:body @items)))]
(swap! orders
assoc
(first orders-id)
'((ml-orders/get (first orders-id) access-token)
(ml-orders/get-feedback (first orders-id) access-token))))
@orders)))
``````

rcustodio20:09:41

well.. the real question would be.. is there a better way?

noisesmith20:09:01

why would that even need an atom? and why are you using loop with no recur?

rcustodio20:09:15

sorry... i didnt put the recur yet

noisesmith20:09:44

OK - anyway, instead of swapping the atom, you can put the new value into the loop arg, and return the result from the loop

bfabry20:09:46

yeah this function raises a lot of questions... you definitely do not need an atom anyway. if the loop has a recur then you can just pass the new orders map into the recur

rcustodio20:09:27

I will work with result in another function

rcustodio20:09:34

``````(defn- get-orders [items access-token]
(let [orders (atom {})]
(when (= (:status @items) 200)
(loop [orders-id (distinct (mapv #(:order_id %) (:body @items)))]
(when orders-id
(swap! orders
assoc
(first orders-id)
'((ml-orders/get (first orders-id) access-token)
(ml-orders/get-feedback (first orders-id) access-token)))
(recur (rest orders-id))))
@orders)))
``````

noisesmith20:09:35

that's why you return it

noisesmith20:09:36

so `(when orders-id ...)` becomes `(if-not orders-id orders ...)` - returning the orders instead of building up the value

noisesmith20:09:58

also, (recur (rest ...)) indicates that your code could be improved by using reduce instead of loop

rcustodio20:09:20

I see... if i need the result, using reduce is better

noisesmith20:09:21

that way clojure does the sequence tracking for you, and the orders are the value returned from your reducing function

noisesmith20:09:41

if you are consuming a collection in order, reduce is better than loop, period

noisesmith20:09:44

it's even faster

rcustodio20:09:02

It's a collection of "future"

rcustodio20:09:06

In this case, map would be better, no?

rcustodio20:09:16

Cuz my return is a map of orders (futures)

noisesmith20:09:40

map doesn't return a hash-map though, it returns a lazy-seq

noisesmith20:09:11

if you are OK with returning a lazy-seq instead, and you want to process the input lazily, sure, map works

rcustodio20:09:23

Yes... thanks

rcustodio20:09:33

``````(defn- get-orders [items access-token]
(when (= (:status @items) 200)
(mapv #(hash-map :order (ml-orders/get % access-token)
:feedback (ml-orders/get-feedback % access-token))
(distinct (mapv #(:order_id %) (:body @items))))))
``````
something like this

rcustodio20:09:43

And then just work with the seq

noisesmith20:09:25

sure - but if you are using mapv, that means you don't want it to be lazy and it might make more sense to use reduce (or into with a map transducer) to create your hash-map

rcustodio20:09:49

Sorry, i've changed to map

noisesmith20:09:04

also, how is this items atom being used?

rcustodio20:09:09

rcustodio20:09:21

I removed the atom

noisesmith20:09:37

oh - I see (:status @items) - is that accessing a future then?

rcustodio20:09:57

From another api request

rcustodio20:09:03

The API have a lot of endpoints

rcustodio20:09:08

That connect with each other

rcustodio20:09:33

From that I must create an order to insert in the system

mjcleary23:09:42

Can anyone help me understand why repeatedly would only call a function once?

bfabry23:09:56

@mjcleary almost definitely because it's lazy and you're only realising the first thunk

bfabry23:09:32

``````(take 1 (repeatedly #(println "whoa")))
whoa
=> (nil)
(take 2 (repeatedly #(println "whoa")))
whoa
whoa
=> (nil nil)
(take 3 (repeatedly #(println "whoa")))
whoa
whoa
whoa
``````

mjcleary23:09:31

Should I use (take n (repeatedly )) over (repeatedly n)?

bfabry23:09:32

what do you want to do, exactly?

mjcleary23:09:49

Read number from console, then perform function that also reads from console that many times

mjcleary23:09:35

`(repeatedly (Integer. (read-line)) (check-weights (read-line)))`

bfabry23:09:45

there's lots of different ways you could do this. I think the most intention revealing would be

``````(dotimes [_time-num (Integer/parseInt (read-line))]
``````

bfabry23:09:34

but, repeatedly would also work. using `(dorun (repeatedly (Integer/parseInt (read-line)) #(check-weights (read-line))))` or `(take (Integer/parseInt (read-line)) (repeatedly #(check-weights (read-line))))` or `(nth (repeatedly #(check-weights (read-line))) (Integer/parseInt (read-line)))`

bfabry23:09:13

the first thing I posted is far easier to read and think about than any of those though

bfabry23:09:53

some of my parentheses are off in those ^

mjcleary23:09:19

Thank you very much