Fork me on GitHub
#beginners
<
2020-02-25
>
Apple04:02:32

Quick question: bit-and cljs gives different result than clj. How to solve this?

cljs.user=> (apply bit-and ' 3232236032 3232236288 3232236544))
-1062731776
user> (apply bit-and ' 3232236032 3232236288 3232236544))
3232235520

hiredman05:02:41

Cljs and clj are not the same, for example here clj has a number of numeric types, in this case longs, while cljs just has js doubles.

hiredman05:02:00

The solution is not expecting the same thing from things that are different

hiredman05:02:24

In this case though the different numeric interpretation may not even matter, using bit-and implies caring about the bit pattern and maybe not caring about the numeric interpretation, in which case, you should check the bit patterns for sameness

Apple05:02:22

Turns out to be a js issue according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators as the result is treated as 32bit signed. Thanks.

Tarun05:02:32

Can anyone suggest modern (2020) beginner friendly resources to get started with web dev in Clojure? I have previously used Django/Flask in Python.

Crispin06:02:25

try luminus web as a beginning web stack. It will be fairly familiar from a django/flask background. There are other more powerful alternatives, but they may be a little too "alien spaceship" for beginners. https://luminusweb.com/

seancorfield05:02:20

Clojure for the Brave and True (online, also a book). Getting Clojure and Living Clojure (books).

seancorfield05:02:57

Those aren't specific to web dev but you'll need a grounding in the language before trying to build web apps.

seancorfield05:02:26

Web Development with Clojure (Dmitri Sotnikov) is a web dev specific book but I wouldn't say it was "beginner friendly" -- @yogthos would you agree or disagree?

Tarun05:02:06

Thanks! I am already working through Brave and True. I was looking for resources more specific to the webdev ecosystem in Clojure.

hindol06:02:17

@reachtarunhere I had the exact same question a few months back. There are not many resources specific to WebDev. But you can pick a web framework first and go through its guides. There is an excellent comparison here: https://purelyfunctional.tv/mini-guide/clojure-web-servers/

👍 4
seancorfield06:02:41

Just to be clear: that article is about web servers not web frameworks.

seancorfield06:02:17

But my advice when learning web dev in Clojure is to start with the basics: Ring, then maybe Compojure, then maybe Selmer for HTML pages. Luminus (from the Web Dev book) is a template made up of a lot of libraries and it's a lot to learn -- and very hard to figure out if anything goes wrong.

👍 4
seancorfield06:02:29

The bottom line is that Clojure does not have web "frameworks" and it really expects you to learn the "nuts and bolts" of how "The Web" works and to learn how to assemble a web application from various component libraries. Using web frameworks in other languages doesn't help you with Clojure as there are no similarities really.

seancorfield06:02:14

Selmer is an HTML templating library for Clojure that is like a very small subset of what Django provides. Ring is like a small subset of Flask.

hindol06:02:59

Yes, the article is mainly about servers but there is a section that talks about Pedestal and Yada, apart from Ring.

hindol06:02:30

I have since used Pedestal to build a small REST API. The experience was good. For purely frontend work though, I don't know.

seancorfield06:02:56

In terms of "standard" and "popular", I don't think I would recommend either Pedestal or Yada as first steps.

seancorfield06:02:35

But, sure, learn them once you've figured out Ring and Compojure and maybe Bidi and maybe a few other things.

Hojat06:02:07

Hi I'm trying to choose my build tools and I'm stuck What's the difference between figwheel auto reload and shadow-cljs auto reload? Which one is better for development?

hindol08:02:29

sorted-set and sorted-set-by both do not accept a keyfn. What would be the idiomatic approach to build a sorted set with a keyfn? EDIT: It seems possible to build a comparator that applies the keyfn and then compares. But will it call keyfn multiple times for the same element?

jumpnbrownweasel14:02:52

I'm guessing this is not very different than using comparators for Java sorted collections. The comparator (and your keyfn) will be called more than once per element, as needed to place items in sorted position.

👍 4
Sy Borg16:02:09

how to count elements in an atom object (set)?

Sy Borg16:02:39

count not supported on this type: Atom

Michael J Dorian16:02:22

You want to dereference it, the atom just points to some immutable data. So, (count @atom)

Michael J Dorian16:02:55

technically you aren't counting the atom, you are counting whatever value it points too right now. It could change at any time

Sy Borg16:02:25

ah, stupid me

Sy Borg16:02:31

thanks

👍 4
Simon17:02:47

Hi clojurians, This started by a typo but it evaluated. Now I'm wondering: (def foo (conj [] foo)) foo returns an object like this: [#object[clojure.lang.Var$Unbound 0x3e3119 "Unbound: #'data.ruler/foo"]]

hiredman17:02:36

vars that exist, but don't have a set value yet return an Unbound object when deref'ed

Alex Ragone17:02:41

As an exercise to learn more about Clojure, I have set myself the goal of refactoring and updating https://github.com/ilmotta/clojure-ants-simulation (the refactoring of Rich's ant simulation) to reflect current best practices. However, I was hoping the community here could point me in the right direction. So for my questions: 1. When I run lein test the tests are run, however, when I run the tests using M-x cider-test-run-project-tests, it can't find any tests? Cider already has the test directory on the classpath. 2. I want to add spec . Having never used it, which areas would you recommend targeting? Functions? Records? 3. What other improvements would you add to this? Thanks! 🙂

hiredman17:02:29

user=> (declare x)
#'user/x
user=> x
#object[clojure.lang.Var$Unbound 0xae7950d "Unbound: #'user/x"]
user=>

Simon17:02:45

cool thanks good to know. Guess because I never modify x I never thought to declare var before I bind them to a value

hiredman17:02:15

the common case for declare is mutually recursive functions

Simon17:02:25

Is my first example a recursive vector then? Because it conj 'es itself.

hiredman17:02:59

it isn't a recursive function

hiredman18:02:30

the different between that and a recursive function is a function won't deref the var until it is invoked, by which time the definition has happened

Simon18:02:43

ah, cool thanks for the explanation.

mmeix18:02:37

Which fn would you write to turn [4 :d :f :g 8 :c 16 :a :b :c :d 1 :f] into [[4 :d :f :g] [8 :c] [16 :a :b :c :d] [1 :f]] ? I started with partition-by number? but now I’m sort of stuck … the thing is, in my application the source coll could also end with a number, or have two numbers in succession

mmeix18:02:20

And (mapv (comp vec flatten) (partition 2 (partition-by number? coll))) feels somewhat clumsy...

robertfw18:02:42

I think someone else would likely have a simpler solution, but I think a stateful transducer could solve this

mmeix18:02:15

I tried with reduce first, and it was easy to start a new vector in the accumulator, but I found it difficult to conj an item to an already existing last vector in coll.

robertfw18:02:07

If you want to try out the transducer route, this article could be of some use https://labs.uswitch.com/transducers-from-the-ground-up-the-practice/

mmeix18:02:37

Thanks - will read!

ghadi18:02:09

(defn custom
  [coll]
  (let [[num & rest] coll]
    (when (number? num)
      (cons (cons num (take-while keyword? rest))
            (lazy-seq
               (custom (drop-while keyword? rest)))))))

mmeix18:02:49

ah! studying …

mmeix18:02:37

I just had another idea using transient - conj! - persistent! but not sure if this will work

ghadi18:02:57

transient / has nothing to do with this

ghadi18:02:11

that's just a way of making an individual collection

ghadi18:02:32

efficiently

mmeix18:02:05

thanks for clarification

ghadi19:02:54

@U082WFGJJ when you want to do something outside of the normal lazy collection functions, you need to understand how to create a custom lazy-seq

ghadi19:02:54

which.... doesn't seem to have a http://clojure.org official guide

mmeix19:02:01

Ok, that's a valuable hint

ghadi19:02:48

i'll type out something in a few minutes

hindol19:02:45

There is a mention of cons and lazy-seq here: https://clojure.org/reference/lazy but yeah, not exactly a guide.

hindol19:02:03

I also have beginner level knowledge, but I deal with ProjectEuler puzzles a lot which are mathematical in nature and hence I deal with infinite sequences a lot, which has to be lazy. I will try to explain this. Let's say, we want a sequence of all the powers of a number.

(defn powers
  ([x] (powers x x))
  ([x p]
   (cons p (powers x (* x p)))))

(powers 2)
;; => integer overflow
This throws an error because we cannot really calculate an infinite sequence. Let's try to read only the first two elements,
(take 2 (powers 2))
;; => integer overflow
This still throws because before returning the first two elements, it is still trying to calculate the whole sequence! Let's make this lazy,
(defn powers
  ([x] (powers x x))
  ([x p]
   (lazy-seq
    (cons p (powers x (* x p))))))

(take 2 (powers 2))
;; => (2 4)
Notice that we have only added lazy-seq in a strategic place. What this actually does is it delays the evaluation of the thing wrapped in lazy-seq till it's actually requested. So, if we request two elements, only the first two cells of the logically infinite sequence is evaluated. Hope this helps.

mmeix17:02:55

> yes, thanks

bfabry18:02:42

I actually think a loop is going to be the cleanest way to do that

Mario C.18:02:21

Is there a reason someone would send HTML with hidden elements to be used as a data store?

robertfw18:02:11

If I've learned one thing over my years of software development, it's that someone will come up with a reason to do just about anything

bananadance 4
💯 4
robertfw18:02:05

What is the actual problem you are trying to solve?

Mario C.18:02:59

True but Ive had those situations where I see something odd and can't think of a reason why it was done that way and refactor. Only for it to bite me in the behind a few weeks later when the issue becomes apparent.

robertfw18:02:36

A not uncommon problem

robertfw18:02:47

Good test coverage goes a long way

Mario C.18:02:46

I am trying to re-factor a front-end code base. I want to know whether I can get rid of this weird HTML hidden-element data store thing and just get the data back as plain JSON

jakubl18:02:51

hidden fields were a big thing about 20 years ago or so - I fear only you can answer the question - whether they can be removed - in the end it's all just data travelling back and forth - if said data is no longer needed - it can be removed

robertfw18:02:00

You won't find a definitive answer here - that can only be known by knowing how your system is used.

hindol18:02:52

This is a good question for #off-topic

Mario C.19:02:46

Yea I just wanted to know of a reason someone might do such a thing lol

Mario C.19:02:03

Even then there could be infinite many reasons

hindol19:02:38

I mean we happily discuss such things under the channel #off-topic and you are more than welcome to post there. #beginners channel is for Clojure(Script) questions.

Sam Heaton20:02:34

Hi again. I have a beginner ClojureScript question but it deals with core.async which is similar in both languages so I figure it's safe to post here. I'm using cljs-http to make a GET request, and ultimately get the response body. This is within Reagent where I am providing a table component which should display the data. This is my function to get the data:

(defn the-data []
  (go (let [response (<! (http/get ""
                                   {:with-credentials? false
                                    :query-params {}}))]
        (println (:body response))
        (:body response))))
Now, the part where I get confused is this. I know that the (http/get) returns a channel and I know that <! gets the response from that channel. In this case, (println (:body response)) works as expected and spits out the response as a PersistentVector which I confirmed using type. However, when I try to call the-data to make the data available to display in the table, I get: Error: [object Object] is not ISeqable (There is a list comprehension in the component, which I've tested successfully with static data that should look the same.) Which causes an error rendering the table component. I thought for a while that the issue was that the function was returning the channel, not the response. But the response seems fine when it's being printed. There's probably a good reason for all of this, but it's beyond me at the moment. If anyone can help clear this up I would appreciate it.

colinkahn20:02:45

Just to clarify, are you calling it like (display-table (the-data))?

colinkahn20:02:27

I ask because go itself returns a core.async channel that will emit whatever the last expression in the go evaluates to.

Sam Heaton20:02:58

No, my start function which renders a reagent component includes a function that calls (the-data) to get the data for the table. I'm guessing this might be a problem.

colinkahn20:02:14

so to use your function as it is now you’d need to do something like

(let [data (reagent.core/atom nil)]
  (a/go (reset! data (a/<! (the-data))))

Sam Heaton20:02:20

Like this:

(defn start []
  (reagent/render-component [table]
                            (. js/document (getElementById "app"))))

colinkahn20:02:51

of course how you actually do it depends on how you’re managing the state of your components

Sam Heaton20:02:25

My table component is like this following your example, but I am still getting the same error Error: [object Object] is not ISeqable . (State management is something that is really new to me). Possibly the atom and go block should go outside the component?

(defn table []
  (let [data (atom nil)]
    (go (reset! data (<! (the-data))))
      [:table {:class "table table-condensed"}
       [:thead {:align "left"}
        [:tr
         [:th "Field1"]
         [:th "Field2"]]]
       [:tbody
        (for [row data]
          ^{:key row}
          [:tr
           [:td (:field1 row)]
           [:td (:field2 row)]])]]))

Sam Heaton21:02:32

Ok, I've got the state part figured out. Now the only problem is another error:

Error: Objects are not valid as a React child
But I think that has a different cause. Thank you!

Sam Heaton21:02:53

I've got it working now. Thanks again

Scott Starkey20:02:05

Hi there, folks… I’m attempting to create a score-word function for a personal project that works like a binary code for 5-letter words. If a letter is in the first half of the alphabet, it scores. If the letter is in the first position, it scores 16, second position 8, third position 4, fourth position 2, and the last letter 1. So for example: (score-word "apple") => 17. (16 points for the first position “a”, and 1 point for the last position “e”.) Trying to figure this out, I started out with making a check-letter? function:

(defn check-letter? [letter]
    (re-find #"[a-m]" letter)
    )

(check-letter? "a") => "a"
(check-letter? "n") => nil
However, I’m having problems taking the nth letter of the word, and passing it to this function. (My check-letter? function is probably unnecessary, but it’s the path I was taking to figure this out.) Just trying to poke at the first letter, I’m having problems:
(defn score-word
    [word]
    (check-letter? (str (first word))))

(score-word "hello")
ClassCastException java.lang.Character cannot be cast to java.lang.CharSequence  clojure.core/re-matcher (core.clj:4667)
Does anyone have ideas to nudge me in the right direction? Thanks in advance!

manutter5120:02:48

@scotto I get

(defn check-letter? [letter]
  (re-find #"[a-m]" letter)
  )
=> #'user/check-letter?
(defn score-word
  [word]
  (check-letter? (str (first word))))
=> #'user/score-word
(score-word "hello")
=> "h"

manutter5120:02:06

Maybe you have some stale code hanging around?

Scott Starkey20:02:11

Hmmm… Maybe!

Scott Starkey20:02:34

I will double-check. Thanks for the sanity check.

👍 8
Scott Starkey20:02:11

That was it. [head desk]