Fork me on GitHub
#beginners
<
2021-07-26
>
Jacob Rosenzweig04:07:34

Anyone care to roast a function? I'm trying to get a good idea of how to format Clojure code correctly and this looks wrong for some reason.

(defn ->user-update
  "Validates and constructs a user update from the provided body."
  [body]
  (let [card? (fn [key-value]
                (let [[key value] key-value]
                  (if (= key :card)
                    [key [:lift value]]
                    [key value])))]
    (into {} (map card? (seq (assoc (select-keys body [:card :user-id :oauth-id]) :updated-at (c/to-sql-time (t/now))))))))

lsenjov04:07:14

Some threading could probably make that body a little better

Jacob Rosenzweig04:07:32

I'm kind of a noob to threading. Could you provide an example?

Jacob Rosenzweig04:07:03

Oh I think I see what you mean. At the (map card? ...) part

lsenjov04:07:29

At a quick rush:

(-> body
    (select-keys [:card :user-id :oauth-id])
    (assoc :updated-at (c/to-sql-time (t/now))
    seq
    (->>
      (map card?)
      (into {})))

lsenjov04:07:17

Otherwise it's pretty good

Jacob Rosenzweig04:07:56

(defn ->user-update
  "Validates and constructs a user update from the provided body."
  [body]
  (->> body )
  (let [card? (fn [key-value]
                (let [[key value] key-value]
                  (if (= key :card)
                    [key [:lift value]]
                    [key value])))]
    (-> body
        (select-keys [:card :user-id :oauth-id])
        (assoc :updated-at (c/to-sql-time (t/now)))
        seq
        (->>
          (map card?)
          (into {})))))
That definitely helps for readability.

👍 4
lsenjov04:07:41

Oh, also your anon function

lsenjov04:07:49

You can destructure in your args

lsenjov04:07:01

So (fn [[key value]] ...)

lsenjov04:07:17

And can do (fn [[key value :as key-value]] ...) if you need it intact

lsenjov04:07:54

Also I don't think you need the seq call, map should handle that for you

lsenjov04:07:39

Actually, why are you mapping across all fields just to find :card ?

Jacob Rosenzweig04:07:18

Oh it's just a dumb way of saying "if it's this key, go ahead and surround it with a [:lift]

lsenjov04:07:37

(update body :card (fn [v] (when v [:lift v])))

lsenjov04:07:01

Ehhhh kinda, probably don't need the when

lsenjov04:07:55

(cond-> (:card body) (update :card (fn [v] [:lift v])))

lsenjov04:07:09

Replace the seq and ->> with that

Jacob Rosenzweig04:07:25

Yeah I'm trying that now actually

lsenjov04:07:27

(this is making the assumption that :card is optional)

lsenjov05:07:31

For completeness: there's also some-> and as-> for threading goodness

Jacob Rosenzweig05:07:52

Like this?

(defn ->user-update
  "Validates and constructs a user update from a given body."
  [body]
    (-> body
        (select-keys [:card :user-id :oauth-id])
        (assoc :updated-at (c/to-sql-time (t/now)))
        (->>
         (cond-> (:card body) (update :card (fn [v] [:lift v])))
         (into {}))))

Jacob Rosenzweig05:07:57

I don't think this is right.

lsenjov05:07:00

Nah, remove the ->>

lsenjov05:07:10

And the into

lsenjov05:07:19

We're never changing it to a seq, it's staying as a map the whole time

lsenjov05:07:54

We want to pass body, after it's transformed a little, as the first argument to cond->, not the last (which ->> would do)

lsenjov05:07:14

Then cond-> checks if :card exists on body

Jacob Rosenzweig05:07:32

OH, so it's just the last statement of the threading macro?

lsenjov05:07:30

That's what ->> does, yes

lsenjov05:07:52

-> is thread first, so it passes each result as the first argument of the next body

lsenjov05:07:58

->> is thread last, same but as last argument

lsenjov05:07:14

-> is used for maps/objects, ->> is for sequences

lsenjov05:07:16

Generally, anyway

Jacob Rosenzweig05:07:09

Yup, I get it now. Nice way of putting everything under one macro. I guess I need to practice writing more functional code. Any good cheat sheet for the clojure core functions?

Jacob Rosenzweig05:07:19

Thanks for the help btw.

👍 3
Jacob Rosenzweig05:07:53

Haha, it has a good area for transducers. I think that's where I'll go next.

lsenjov05:07:56

There's a lot there. Have a peak through them, and 90% of the time you ask "surely there's a function for this" there probably is

lsenjov05:07:33

So feel free to ask in here about "is there a function that does X?" when you come to that point

Jacob Rosenzweig05:07:17

That's what I kind of like about clojure, there's a function for everything. Meanwhile in JS, I have to conjure some weird JS syntax to do something as simple as cloning a list.

seancorfield05:07:33

I think I saw someone say there's about 600 functions in clojure.core but I haven't counted them...

seancorfield05:07:00

@rosenjcb Are you using clj-time?

Jacob Rosenzweig05:07:31

(defn ->user-update
  "Validates and constructs a user update from a given body."
  [body]
    (-> body
        (select-keys [:card :user-id :oauth-id])
        (assoc :updated-at (c/to-sql-time (t/now)))
        (cond-> (:card body) (update :card (fn [v] [:lift v])))))

seancorfield05:07:33

You know it's deprecated? We're trying to get people to stop using it.

borkdude08:07:18

Perhaps archive the repo? Or is that too drastic?

borkdude09:07:37

@U04V70XH6 Perhaps clj-kondo could lint deps.edn for deprecated libraries :)

👍 3
💯 2
Jacob Rosenzweig05:07:53

That's what I saw on a quick google search.

seancorfield05:07:16

The README on the repo is pretty clear 🙂

seancorfield05:07:43

I suspect (java.util.Date.) would be sufficient as a replacement for (c/to-sql/time (t/now))

seancorfield05:07:07

(the JDBC libraries should take care of converting it to a java.sql.Timestamp)

zackteo09:07:20

How do I force evaluation on something lazy like a for loop

jaju09:07:10

Or, into

💯 3
pyry09:07:58

Also, if you don't care about the return value but just want to perform side effects, doseq accepts the same syntax as a for expression.

zackteo09:07:24

idk why, into worked out but not doall

zackteo09:07:55

maybe into helped me format the data as well hmmm

jaju09:07:02

I guess a more complete example will make it easier to understand. Laziness can be tricky!

zackteo09:07:17

Was sending the data outputted from a for into jdbc but it was having some issues. And I was thinking it might be a result of laziness

jaju09:07:13

doall returns back the same passed lazy seq - while forcing side-effects. into creates a new, realized collection and returns it. Not much insight into the doall implementation, but the source is easy to follow and see what is happening.

zackteo09:07:43

Maybe I just needed the data to be in a vector and not a seq

noisesmith19:07:35

> Was sending the data outputted from a for into jdbc but it was having some issues. And I was thinking it might be a result of laziness this is highly unlikely - if jdbc reads the value, it will be calculated (btw. nitpick there's no "for loop" in clojure, for is a list comprehension not a looping construct)

zackteo09:07:46

Is there a more succinct way to do both filter and remove ? Or do I just parse my collection through both of them just like that

jaju09:07:57

Can you explain with a small example collection? Are the filter and remove predicates complements of each other?

zackteo10:07:03

Yeap complements. So tgt what is filtered and what is filter will return the entire collection

jaju10:07:23

Does group-by work for you? You have to adjust the fn accordingly.

zackteo10:07:39

Hmmm that might work o:

Alberto Villa15:07:02

Hi, I've just started to learn Clojurescript and I was wondering if you could recommend me some resources (courses, books or tutorials). I've developed in several languages (Python, Javascript, Ruby, Java, C, C++) and I am (or was) aware of the principles of functional programming so I don't mind jump into Clojurescript with some intermediate resources.

asmeredith19:08:42

I know that this is a late reply, but I wrote https://www.learn-clojurescript.com to help JavaScript developers. Let me know if it's useful!

zackteo15:07:53

@a.villa.diez I have always found https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f quite a useful list of resources to start from 🙂

Adam Kalisz16:07:44

I am not sure, how to work with a Java interface from Clojure in this particular case: https://oshi.github.io/oshi/oshi-core/apidocs/oshi/software/os/InternetProtocolStats.html I would like to getTCPv4Stats but I am doing something wrong. I don't think I can just instantiate the interface since there is obviously no constructor. What I aim to do, is get a list of network connections and their state. I have a dependency: [com.github.oshi/oshi-core "5.8.0"] and I import the interface like this (import 'oshi.software.os InternetProtocolStats)

Russell Mull16:07:46

After you import it, you can use (InternetProtocolStats/getTCPv4Stats)

Adam Kalisz16:07:42

I don't think so: (InternetProtocolStats/getTCPv4Stats) Syntax error (NoSuchFieldException) compiling . at (/tmp/form-init261843597932239110.clj:1:1). getTCPv4Stats

Russell Mull16:07:03

Oh goodness, i've completely misunderstood your question

Russell Mull16:07:06

This is an interface

Russell Mull16:07:33

you'll need to find some class that implements the interface, instantiate that, and call the method on it

dpsutton16:07:02

there's one for each os listed at the top

dpsutton16:07:16

WindowsInternetProtocolStats, MacInternetProtocolStats, etc

Russell Mull16:07:55

(.getTCPv4Stats (LinuxInternetProtocolStats.)), or similar

Adam Kalisz16:07:18

Ok, thanks. I was hoping I wouldn't have to be platform specific like that...

Russell Mull16:07:36

It appears to be a limitation of the library you're using

👍 2
Adam Kalisz16:07:24

Thank you all! I was really starting to pull my hair. Not really working with Java much...

V17:07:20

I wish to implement a chess game engine in clojure using bitboards. With bitboards you represent the chess board using an unsigned long giving you a bit for each space on the chess board between 0 and 63. However, Java, and therefore clojure, uses signed bits, which makes it impossible to be to use bit-wise operations on the very left most bit, since it will throw an error. Is there a way around this? I saw biginteger as an alternative solution, but bitshift operations are not supported here. I also question how bigintegers might impact the performance

Robert Mitchell18:07:27

What error do you get interacting with the last bit in a long? this works fine in my repl:

(let [show-bits
      (fn [x]
        (dotimes [bit 64]
          (print (if (bit-test x (- 63 bit)) "1" "0")))
        (println))
      x (bit-or (bit-shift-left 1 63)
                (bit-shift-left 1 25)
                (bit-shift-left 1 4))]
  (show-bits x)
  (show-bits (bit-set x 50))
  (show-bits (bit-clear x 63))
  (println (bit-test x 63))
  (println (bit-test x 25))
  (println (bit-test x 4))
  (println (bit-test x 0)))
1000000000000000000000000000000000000010000000000000000000010000
1000000000000100000000000000000000000010000000000000000000010000
0000000000000000000000000000000000000010000000000000000000010000
true
true
true
false

💯 6
noisesmith19:07:41

yeah, you can do bitwise ops on all 64 bits of a long, if you are treating it as a series of 64 bits the question of sign shouldn't come up at all

V20:07:41

Thank you all for your kind answers. It seems to work now using only longs 🙂 - I describe the solution in greater detail here - https://clojurians.slack.com/archives/C053AK3F9/p1627332915264100?thread_ts=1627322982.252400&amp;cid=C053AK3F9

deleted17:07:41

like instead of & it's .and

👍 2
V17:07:29

Ah i see, didn' realise they had their own functions. Thanks. I had hoped to use longs, but i guess if that is not possible, biginteger are the best bet

deleted18:07:51

oh, fun, just found this, it has a bitboard impl using bigint https://github.com/mdallastella/vinnik

hiredman18:07:36

some of these seems to point to unfamiliarity with working with bit operations, like https://github.com/mdallastella/vinnik/blob/master/src/vinnik/bitboard.clj#L13-L16 uses raising 2 to the 64 power to create a board of all 1s instead of a bitewise not operation

hiredman20:07:18

depends, if you stick with biginteger (dec (.shiftLeft (biginteger 1) 64)) should be sufficient

hiredman20:07:16

(bit-or (bit-shift-left (bit-not (bit-shift-left 1 63)) 1) 1) or similar if you want to stick with longs

hiredman20:07:04

it replaces the power call with the shift

hiredman20:07:40

2^64 <=> 1 << 64

hiredman20:07:22

-1 being the 64 bit long that gives you 64 1 bits

hiredman20:07:50

user=> (Long/toBinaryString -1)
"1111111111111111111111111111111111111111111111111111111111111111"
user=> (count (Long/toBinaryString -1))
64
user=>

hiredman20:07:23

sure, I mean, if you want do absolutely nothing

ghadi18:07:17

> "makes it impossible to use bit-wise ops on the leftmost bit" not true

ghadi18:07:40

if you are a beginner in clojure, I'd caution from spending your learning budget doing heavy bit arithmetic

💯 4
ghadi18:07:27

Clojure, being a hosted language, has a few points of intersection with the platform (the JVM) where you really have to be secure with what all is going on

ghadi18:07:01

(I say that realizing that you said it was your explicit goal to use bitvectors)

💯 2
ghadi18:07:42

user=> (bit-shift-left 1 63)
-9223372036854775808
user=> (bit-test -9223372036854775808 63)
true
user=> (bit-test -9223372036854775808 60)
false
I promise you can use all 64 bits

V19:07:48

Yea i can use all 64 bits, but the problem arrises when i want to bitshift the boards to calculate moves and attacks. If i place a king on the 64th bit, i will not be able to shift it properly. At least not with my current implementation. I get the correct result from every other position than this specific one.

(def not-A-file (unchecked-long 0x7f7f7f7f7f7f7f7f))
(def not-H-file (unchecked-long 0xfefefefefefefefe))
(def not-G-file (unchecked-long 0xfdfdfdfdfdfdfdfd))        ;; Used for knights
(def not-B-file (unchecked-long 0xbfbfbfbfbfbfbfbf))        ;; Used for knights

;;Directions
;; northwest    north   northeast
;;         +9    +8    +7
;;             \  |  /
;; west     1 <-  0 -> -1    east
;;             /  |  \
;;         -7    -8    -9
;; southwest    south   southeast

;;Bit-shift-left is for positive directions. Right is for negative directions
(defn north-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 8)))

(defn south-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 8)))

(defn east-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 1)))

(defn west-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 1)))

(defn north-east-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 7)))

(defn south-east-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 9)))

(defn north-west-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 9)))

(defn south-west-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 7)))

(defn white-king-moves [bitboards]
  "White king can move in 8 different directions


           1   2  3
           8   K  4
           7   6  5


  "
  (let [white-king-bboard (get-in bitboards [:white :K])
        white-pieces-bboard (get-in bitboards [:occupancy :white])

        king-clip-file-A-bboard (bit-and white-king-bboard not-A-file)
        king-clip-file-H-bboard (bit-and white-king-bboard not-H-file)


        move-ne (north-east-one king-clip-file-H-bboard)
        move-nw (north-west-one king-clip-file-A-bboard)
        move-w (west-one king-clip-file-A-bboard)
        move-sw (south-west-one king-clip-file-A-bboard)
        move-se (south-east-one king-clip-file-H-bboard)
        move-e (east-one king-clip-file-H-bboard)
        move-n (north-one white-king-bboard)
        move-s (south-one white-king-bboard)

        ;;Union all possible moves and remove moves where white already has pieces
        king-moves (bit-and (bit-not white-pieces-bboard) (bit-or move-ne move-nw move-w
                                                                  move-sw move-se move-e
                                                                  move-n move-s))

        ;;Remove moves that will result in check.

        ]
    king-moves))

(white-king-moves {:white     {:K (unchecked-long -9223372036854775808)}
                        :occupancy {:white 0
                                    :black 2048}})

V20:07:16

I use this site to calculate the bitboard positions and what i am expecting - https://gekomad.github.io/Cinnamon/BitboardCalculator/

V20:07:34

So in case of 64th bit i would expect the bitboard representing the kings position to be 9223372036854775808 - Then the possible moves would be the 3 bits sorounding it - 4665729213955833856 - However what i return is -18014398509481984

noisesmith20:07:52

I haven't fully parsed out what's going on here, but a minor style note, most of those usages of unchecked-long do nothing - it's a function that coerces a runtime value to long even if it's outside range, it isn't a constructor for some special "unchecked" version of a long as a unique type, so using it on literals that are clearly small enough to fit in a Long is strange.

V20:07:00

The idea is basically. To calculate the moves of the king, i can move in 8 different directions. BIt shifting in any of these will return a long with a bit set in that position i can move the king. If i union these i get all king moves. The "not-A-file" is for when the king is on the very left file or the very right file. In this case i don't, want to calculate the left or right shifts, since they will set a bit on wrong rank.

hiredman20:07:09

4665729213955833856, the number you say represents the 8 positions around the king, but that number only has 3 bits set

hiredman20:07:19

so maybe that is not right?

hiredman20:07:56

you may need to be using unsigned-bit-shift-right

💯 3
hiredman20:07:07

user=> (bit-shift-right -1 1)
-1
user=> (unsigned-bit-shift-right -1 1)
9223372036854775807
user=> (Long/toBinaryString (bit-shift-right -1 1))
"1111111111111111111111111111111111111111111111111111111111111111"
user=> (Long/toBinaryString (unsigned-bit-shift-right -1 1))
"111111111111111111111111111111111111111111111111111111111111111"
user=>

hiredman20:07:39

(the second one is only 63 digits because a leading 0 is trimmed off)

V20:07:15

Switching to ((unsigned-bit-shift-right) fixed it!! Thank you so much - Also the 64'th position will be the very corner of the chessboard, if the king is here, he only has 3 possible moves. Therefore only 3 set bits 🙂

hiredman18:07:43

(unchecked-long (*' -9223372036854775808 -1)) you can use unchecked-longs to turn numbers that are unsigned longs outside the range of long that you get from examples or some oracle into signed longs with the same bit pattern

V19:07:48

Yea i can use all 64 bits, but the problem arrises when i want to bitshift the boards to calculate moves and attacks. If i place a king on the 64th bit, i will not be able to shift it properly. At least not with my current implementation. I get the correct result from every other position than this specific one.

(def not-A-file (unchecked-long 0x7f7f7f7f7f7f7f7f))
(def not-H-file (unchecked-long 0xfefefefefefefefe))
(def not-G-file (unchecked-long 0xfdfdfdfdfdfdfdfd))        ;; Used for knights
(def not-B-file (unchecked-long 0xbfbfbfbfbfbfbfbf))        ;; Used for knights

;;Directions
;; northwest    north   northeast
;;         +9    +8    +7
;;             \  |  /
;; west     1 <-  0 -> -1    east
;;             /  |  \
;;         -7    -8    -9
;; southwest    south   southeast

;;Bit-shift-left is for positive directions. Right is for negative directions
(defn north-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 8)))

(defn south-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 8)))

(defn east-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 1)))

(defn west-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 1)))

(defn north-east-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 7)))

(defn south-east-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 9)))

(defn north-west-one [bitboard]
  (bit-shift-left (unchecked-long bitboard) (unchecked-long 9)))

(defn south-west-one [bitboard]
  (bit-shift-right (unchecked-long bitboard) (unchecked-long 7)))

(defn white-king-moves [bitboards]
  "White king can move in 8 different directions


           1   2  3
           8   K  4
           7   6  5


  "
  (let [white-king-bboard (get-in bitboards [:white :K])
        white-pieces-bboard (get-in bitboards [:occupancy :white])

        king-clip-file-A-bboard (bit-and white-king-bboard not-A-file)
        king-clip-file-H-bboard (bit-and white-king-bboard not-H-file)


        move-ne (north-east-one king-clip-file-H-bboard)
        move-nw (north-west-one king-clip-file-A-bboard)
        move-w (west-one king-clip-file-A-bboard)
        move-sw (south-west-one king-clip-file-A-bboard)
        move-se (south-east-one king-clip-file-H-bboard)
        move-e (east-one king-clip-file-H-bboard)
        move-n (north-one white-king-bboard)
        move-s (south-one white-king-bboard)

        ;;Union all possible moves and remove moves where white already has pieces
        king-moves (bit-and (bit-not white-pieces-bboard) (bit-or move-ne move-nw move-w
                                                                  move-sw move-se move-e
                                                                  move-n move-s))

        ;;Remove moves that will result in check.

        ]
    king-moves))

(white-king-moves {:white     {:K (unchecked-long -9223372036854775808)}
                        :occupancy {:white 0
                                    :black 2048}})