Fork me on GitHub
#beginners
<
2021-09-11
>
Cyrus Malekani08:09:26

Goooooooood morning, is anybody available to help me step through some code? xD

Cyrus Malekani08:09:36

(defn balanced? "Parenthesis balance checker." {:test #(do (assert (true? (balanced? "()"))) (assert (true? (balanced? "()()"))) (assert (true? (balanced? "(()(()))"))) (assert (true? (balanced? "(h(e)j(h(o)p)p)"))) (assert (false? (balanced? "(he)j)h(opp"))) (assert (false? (balanced? "())("))))} [s] (->> s ;; remove non-paranthesis characters (filter #{\( \)}) ;; reduce down to an empty or non-empty vector (reduce (fn [stack item] (cond (#{ \( } item) (conj stack item) (and (#{ \( } (last stack)) (= ({ \) \( } item) (last stack))) (pop stack) :else (conj stack item))) []) ;; return whether we have any unbalanced brackets empty?))

minor, but `(peek stack)` has better performance than `(last stack)` for vectors

Cyrus Malekani08:09:57

I'm wondering about the first condition ...

Cyrus Malekani08:09:07

(#{ \( } item) (conj stack item) Specifically what kind of check does the condition do? I've tried to test it out but I'm not getting much wiser ... (#{ \( } item) <- ?

Leaf Garland09:09:27

`\(` is the syntax for a single character, in this case the `(` character. `#{}` is the syntax for a set literal. `#{\(}` is a set with a single `(` character in it. When we apply sets to a value they return the value if it is in the set, or nil if it is not. So this condition will be nil if item is not a `(`. I'm not an experienced clojure person but that looks like a really confusing way to check if item is `(`.

Cyrus Malekani09:09:52

nvm Got help otherwise ... Apparently (!) Sets can take an argument and they return the argument if they contain it.

2
Benjamin11:09:20

Unexpected error (IncompatibleClassChangeError) macroexpanding clojure.core/gen-class at (com/github/benjaminasdf/idlelib/completion.clj:1:1). class com.intellij.codeInsight.completion.CompletionExtension has interface com.intellij.lang.LanguageExtension as super class does anybody have advice on this? Context is that I'm hacking around with adding local jars (intellij-idea installation libs) as deps. But I'm new to java so I did not run into common issues yet

Fredrik11:09:07

Can you share the ns form?

Benjamin11:09:13

seems like it is fixed by removing openapi from my deps (also needed to add another jar from the intellij lib)

popeye15:09:25

``````{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.3.2"]]}} ``````

Chase16:09:55

I think `lein deps` should dl your deps, if that's what you are looking for. Running `lein repl` should dl too before it starts up your repl

popeye16:09:53

Getting this issue in VScode, But it is able to run the ring server, strange!

Chase16:09:17

What is it saying the actual error is? Is it just a warning? Might just be something to do with `clj-kondo` (the linter) which doesn't like `:refer :all` if I remember correctly

Chase16:09:54

I've created a little full stack web app but am having difficulty connecting the backend to the frontend. When running `shadow-cljs watch app` I see the `Hello world!` message. But when starting up the backend server, it compiles and runs fine but the html is blank even though I think I'm telling it to serve the `index.html` file that calls the `js` generated by `shadow-cljs`. Would anyone be willing to look at the code real fast? It's small

Chase16:09:53

When I evaluate the expressions in my `index-handler` function I am seeing it return the full html of `index.html` as a string which I think is how it should be returning.

Fredrik16:09:03

Try replacing `[["/"] {:get index-handler}]` with `["/" {:get index-handler}]`

Chase16:09:02

Ahh, that did help! What a tough little type to spot. Thank you! Unfortunately, now it's on to a cljs issue as it is telling me that `ai is undefined` in the js script code where `ai.core.main()` is called

Fredrik16:09:39

Did you run `npm install` ?

Chase16:09:44

Yep. The frontend runs fine when I run the dev build under `shadow-cljs watch app` It also runs fine if I build a release build and run it with a basic http server application (like python's built in). There is just something I am doing wrong in connecting it to my own backend server

Fredrik16:09:08

Is your backend server serving the js file?

Chase16:09:43

I don't know. All my backend code is just in that repo setting up the handler and route and starting up the jetty server. The handler is pulling in the html from `public/index.html` . That html file is then calling the js which is also in that public folder

Fredrik16:09:18

But the backend server is also responsible for serving that js file

Fredrik16:09:28

And currently it doesn't know how to do that

Fredrik16:09:07

If you replace app with

``````(def app
(ring/routes
(ring/ring-handler
(ring/router
["/" {:get index-handler}]))
(ring/create-file-handler {:path "/"})))``````

Chase16:09:11

Ahh, yeah, this is where my misunderstanding must be. So I also need a way to tell the server to serve that js (and css) file?

Fredrik16:09:21

Yes ðŸ™‚

Fredrik16:09:42

The reason why it is working on the shadow-cljs dev server, is because it is already set up to server those resources

Chase16:09:54

Fredrik16:09:55

But on your own backend server, you must manually take care of this

Fredrik16:09:08

This is also why your python http server worked, because it serves all files in the directory

Chase16:09:09

and how does `(ring/create-file-handler {:path "/"})` tell it to do so?

Chase16:09:19

Is that just what that function does?

Chase16:09:06

Cool. Yep, I have all those docs up but I was just trying to get the most basic thing working (for motivation haha) with some quick reading before I took a real deep dive. I will get to slow reading now. Thank you!

Fredrik17:09:19

Yes, that is all the function does. Good luck! Getting over such hurdles is the joy and frustation of building things from scratch

Chase17:09:28

Yep! I am so happy already that I ditched this huge Luminus template I started with and literally just started over with `mkdir ...` and went from there. I've probably learned more about professional web development in 2 days than the previous 2 months.

popeye17:09:42

Hello Team, I was just going through one of the clojure video and found below points 1. Clojure has immutable data structure 2. When any element added to any datastructure,example (def a [ 1 2 4]) (def b (conj 5 a)) This will does not create any new vactor instead, memory will be point to the two location of new vector b? is this right? By this clojure handles any new element added Is my understanding is right?

Fredrik17:09:55

I found this helpful in understanding how Clojure vectors are implemented: https://hypirion.com/musings/understanding-persistent-vector-pt-1

ðŸ’¯ 2
popeye17:09:38

ahh, I was reading this!! So got this confusion ðŸ˜„

Fredrik17:09:13

I think you got the general idea correct. When adding a new element to `a` , `a` itself is never modified. Instead `conj` returns a new vector `b` , which has structural sharing with `a`

popeye17:09:47

but it is not same as the one it is described in document right!

Fredrik17:09:37

There are a lot of implementation details such that sometimes, for efficiency's sake, some copying is actually done

popeye17:09:12

oh yeah it is the same! just assing reference in the node

popeye18:09:14

I was trying to understand `transient` and I found few document are very useful, Transient are useful when there is large operation such as `append` and `update` on the collection, is my understanding right?

Fredrik18:09:18

Yes, transients are used as a stand-in replacement for the usual persistent data structures, in places where the extra performance you can get out of them is really needed

popeye19:09:33

cool! thaks

popeye19:09:35

So in production code, does everyone use persistent data structure operations?

Fredrik19:09:53

What do you mean by production code? As opposed to, say, libraries?

popeye19:09:46

usually in my appicatin we have made use of assoc and conj functions,i am trying to ask is it better if we use assoc! in the code?

Fredrik19:09:10

I don't know exactly what kind of code you are writing, but you are with high probability better off sticking to the usual persistent operations.

Fredrik19:09:59

Transients are for very specific use cases. If after testing you find there's a bottleneck in your code, say a lot of `assoc` operations with the same map that you need to speed up, then transients might help improve that.

popeye19:09:07

ok lets consider I have 1000 elements in the vector and I try to call reduce function on each element and doing assoc opeartion, what do you suggest in that case `assoc` or `assoc! ?`

Fredrik19:09:53

This is just my opinion, but in such a case I would just use the usual persistent data structures. Then you don't have to worry about converting to and from transients, and remembering to use the transient operations everywhere.

Fredrik19:09:14

If I have a performance issue, and switching to transients solves that, then it's a another story.

popeye19:09:59

ah I understood! thanks

popeye18:09:21

where we make use of `assoc , conj, update` we should make use of `assoc!, conj!`

hi guys, can u suggest me please how to make it more concise, readable and comprehensive?

``````(defn delete-clip
[clip-id]
(swap! app-state assoc :twitch-clips (filter #(not= clip-id (:id %)) (:twitch-clips @app-state))))``````

Fredrik18:09:02

My suggestion:

``````(defn remove-by-id [coll id]
(remove #(= id (:id %)) coll))

(defn delete-clip!
[clip-id]
(swap! app-state update :twitch-clips remove-by-id clip-id))``````

Fredrik18:09:50

Depending on how general you want the code, make `delete-clip` also take in the `app-state` and the path, in this case `:twitch-clips`, as parameters

Alex Miller (Clojure team)18:09:29

you definitely don't want to @app-state in there - you are losing the atomicity you get with swap! doing that

Alex Miller (Clojure team)19:09:05

you have a data structure and a modification to it - make a function out of that - just immutable data -> immutable data

Alex Miller (Clojure team)19:09:43

then make a stateful function that applies that move from one immutable state to another to the app-state

Alex Miller (Clojure team)19:09:04

separating this means you can fully develop and test the immutable parts independent from the stateful parts

Alex Miller (Clojure team)19:09:53

any time you have an atom, you should treat it like red hot lava - isolate where you touch it to as few places as possible and make it clear that the functions that use it are stateful