Fork me on GitHub
#beginners
<
2021-11-25
>
zackteo04:11:27

Hello, I am currently on a team using intellij with cursive and I'm personally using emacs for development. May I ask if there are any suggestions to ensure consistent formatting? I know the standard is cljfmt but from my understanding there is no integration with cursive. I understand there's https://clojureverse.org/t/cursive-how-to-match-cljfmt-formatting/7246 but I'm not sure how well it works. I also looked into https://cursive-ide.com/userguide/formatting.html#matching-indentation-to-emacs but that doesn't seem to be entirely consistent with cljfmt

pithyless06:11:34

Both intellij and emacs have a way of configuring an external formatter to run on save. It's not perfect, but perhaps it would be good enough? Everyone could then install cljfmt, cljstyle, or clojure-lsp (which uses cljfmt internally) and use the same config from the repo.

zackteo07:11:26

Could you point me to how I can configure format on save on both intellij and emacs?

1
randomm char23:11:29

If you use git you could use git-hooks pre-commit to format code before committing?

Shmarobes12:11:34

Hi I'm wondering what are the ways to implement a 2d grid using clojure data structures. It sounds obvious: just use nested vectors or something, but I'm curious if this is idiomatic in clojure. Or, more importantly, is this the best solution for the problem at hand. In particular, I'm trying to create a https://en.wikipedia.org/wiki/Cellular_automaton, so the operation I'm going to need the most is getting the neighbors of each cell. What approach would you recommend? Also, how do I parallelize this task? I'm thinking pmap, but how exactly do I use it for this?

emccue20:11:55

I have an impl of conways game of life sitting somewhere i can share

emccue20:11:06

(ns conway.core
  (:import [java.awt Color Dimension Graphics]
           [javax.swing JPanel JFrame Timer]
           [java.awt.event ActionListener KeyListener])
  (:require [clojure.set])
  (:gen-class))

(defn make-game-of-life
  ([initially-filled]
   {:alive (into #{} initially-filled)})
  ([]
   {:alive #{}}))

(defn make-cell [x y]
  {:x x :y y})

(defn filled [game-of-life]
  (:alive game-of-life))

(defn- neighboring-cells [cell]
  (let [{:keys [x y]} cell
        neighbor-fns [[dec dec]
                      [dec identity]
                      [dec inc]
                      [identity dec]
                      [identity inc]
                      [inc dec]
                      [inc identity]
                      [inc inc]]
        neighbors (for [[x-fn y-fn] neighbor-fns]
                    (make-cell (x-fn x) (y-fn y)))]
    neighbors))

(defn- alive-neighbors [game-of-life cell]
  (let [{:keys [alive]} game-of-life
        neighbors (neighboring-cells cell)
        not-open-neighbors (filter (fn [cell]
                                     (contains? alive cell))
                                   neighbors)]
    (set not-open-neighbors)))

(defn- who-dies [game-of-life]
  (let [{:keys [alive]} game-of-life
        to-die (filter
                 (fn [cell]
                   (not (contains? #{2 3}
                                   (count (alive-neighbors game-of-life cell)))))
                 alive)]
    (set to-die)))

(defn- who-is-born [game-of-life]
  (let [{:keys [alive]} game-of-life
        all-neighbors (mapcat identity
                              (for [cell alive]
                                (neighboring-cells cell)))
        count-of-neighbors (frequencies all-neighbors)
        has-3-neighbors (filter
                          (fn [cell] (= (get count-of-neighbors cell) 3))
                          (keys count-of-neighbors))
        to-be-born (filter
                     (fn [cell] (nil? (get alive cell)))
                     has-3-neighbors)]
    (set to-be-born)))


(defn advance [game-of-life]
  (let [{:keys [alive]} game-of-life
        to-die (who-dies game-of-life)
        to-be-born (who-is-born game-of-life)]
    (assoc game-of-life
       :alive
       (-> alive
           (clojure.set/difference to-die)
           (clojure.set/union to-be-born)))))

;; ----------------------------------------------------------------------------


(def starting-cells #{;; Gosper Glider gun
                      (make-cell 1 5)
                      (make-cell 2 5)
                      (make-cell 1 6)
                      (make-cell 2 6)
                      (make-cell 11 5)
                      (make-cell 11 6)
                      (make-cell 11 7)
                      (make-cell 12 4)
                      (make-cell 13 3)
                      (make-cell 14 3)
                      (make-cell 12 8)
                      (make-cell 13 9)
                      (make-cell 14 9)
                      (make-cell 15 6)
                      (make-cell 16 4)
                      (make-cell 17 5)
                      (make-cell 17 6)
                      (make-cell 17 7)
                      (make-cell 18 6)
                      (make-cell 16 8)
                      (make-cell 21 3)
                      (make-cell 21 4)
                      (make-cell 21 5)
                      (make-cell 22 3)
                      (make-cell 22 4)
                      (make-cell 22 5)
                      (make-cell 23 2)
                      (make-cell 23 6)
                      (make-cell 25 1)
                      (make-cell 25 2)
                      (make-cell 25 6)
                      (make-cell 25 7)
                      (make-cell 35 3)
                      (make-cell 35 4)
                      (make-cell 36 3)
                      (make-cell 36 4)})

(def field-width 50)
(def field-height 30)
(def point-size 15)
(def square-color Color/BLACK)

(defn game-panel [initial-game-of-life-state]
  (let [game-of-life-atom (atom initial-game-of-life-state)]
    (proxy [JPanel ActionListener KeyListener] []
      (paintComponent [g]
        (proxy-super paintComponent g)
        (let [game-of-life @game-of-life-atom]
          (doseq [{:keys [x y]} (filled game-of-life)]
            (.setColor g square-color)
            (.fillRect g
                       (* x point-size)
                       (* y point-size)
                       point-size
                       point-size))))
      (getPreferredSize []
        (Dimension. (* (inc field-width) point-size)
                    (* (inc field-height) point-size)))
      ;; ActionListener
      (actionPerformed [_]
        (.repaint this))
      ;; KeyListener
      (keyPressed [_]
        (swap! game-of-life-atom advance))
      (keyReleased [_])
      (keyTyped [_]))))

(defn game []
  (let [game-of-life (make-game-of-life starting-cells)
        frame (JFrame. "Conways Game of Life")
        panel (game-panel game-of-life)
        timer (Timer. (/ 1 60) panel)
        _ (do (.setFocusable panel true)
              (.addKeyListener panel panel)
              (.add frame ^JPanel panel)
              (.pack frame)
              (.setDefaultCloseOperation frame JFrame/DISPOSE_ON_CLOSE)
              (.setVisible frame true)
              (.start timer))]
    frame))

(defn -main
  "Starts the default game. TODO: Custom input to specify the initial state."
  [& args]
  (game))

Shmarobes08:11:36

@UE0T2PKJA Wow, I'm impressed. I like the idea of using a set of living cells to represent the board state. I would like to modify it to work with any ruleset, though. I'm not sure if it's possible for this solution, but I feel enlightened nonetheless.

Pagoda 5B12:11:07

Hey, hello, I'm writing my first code attempts in clojure using hackerank, and I'm solving a "string compression" task by reducing a string with a transducer that compact duplicates The issue is that - probably for longer inputs - some of the checks fails because they take too long to finish. Is there any known practice or solution to speed up such kind of operations. I'm thinking of map-reducing, in parallel chunks, yet there might be simpler solutions before going async Thanks for any advice

papachan13:11:10

Maybe with #code-reviews you can generate more discussion.

thanks2 1
thom13:11:13

What counts as long here? Both a simple threaded version (`(time (->> input (partition-by identity) (map first) (apply str) (def output)))`) and a tranducer version (`(time (def output (apply str (transduce (comp (partition-by identity) (map first)) conj input))))`) seem to work up to strings of length 1e7 in reasonable time. Beyond that it's more painful, and you'd want something like:

thanks2 1
thom13:11:31

Not thoroughly tested at all though.

thom13:11:13

(that's just translating what I'd do in C, anyway, there may be better approaches)

❤️ 1
Thierry13:11:36

Can anyone that uses Calva in VSCode tell me why my cursor doesn't tab forward when I press tab? Space works just fine but is kind of slow if I want to line up something with the line above.

Shmarobes13:11:49

I think there is a Ctrl+] shortcut for indentation, maybe it helps.

Thierry13:11:01

indeed, but thats only the starting indentation. I need indentation between wrapped expressions

Thierry14:11:26

I had found that indeed, but it doesn't change anything

Thierry14:11:06

The red is what i want to line up

pez16:11:17

In Calva the tab key is used to format the current form.

👀 1
Thierry17:11:30

Okay, so what replaces the tab key then?

pez17:11:30

Nothing. 😀 but it is just defaults. You can of course rebind the tab key, or bind something else to the insert tab action.

Thierry19:11:29

okay, well thats poop haha

popeye15:11:37

Was going through component https://github.com/stuartsierra/component i did not get

(defn new-database [host port]
  (map->Database {:host host :port port}))

popeye15:11:20

because , this has 2 parameters, does it act like arbitrary argument?

(defrecord Database [host port connection]

Ed16:11:01

map->Database is an automatically generated function that will take a map and turn it into a database record. ->Database will take 3 args of host port and connection and return a database record. This https://clojure.org/reference/datatypes#_deftype_and_defrecord says: > when a defrecord Bar is defined a corresponding function map->Bar is defined that takes a map and initializes a new record instance with its contents

👍 1
sP0re16:11:05

Hello! I was wondering if it's realistic in 2022 to find a job as a Clojure Dev? Beginner Clojure Dev but experienced developer (from Java and Kotlin) Thanks a lot!

1
👍 2
pyry16:11:09

I do think it is realistic, though of course this might depend a lot on eg. geography - different countries will have different amounts of Clojure jobs available.

pyry16:11:56

Clojure being a rather niche language, I think a lot of places will not require you to have prior experience in Clojure per se, but rather ask for general programming experience and perhaps previous exposure to functional programming.

👍 2
pyry16:11:47

I do think your JVM (and especially Kotlin) background will be to your advantage, though - again - this will depend a whole lot on your particular situation.

pyry16:11:08

(There's also #jobs-discuss in case you have more targeted questions regarding finding a Clojure job.)

👍 1
Ory Band16:11:18

The company I work at are recruiting Clojure developers as much as we can find. We are located @ Tel Aviv

Ed16:11:48

maybe checkout #jobs and #remote-jobs ?

Mno17:11:29

Me and several people I know have managed to migrate... so I think it's reasonable.

sP0re18:11:18

Ok thanks for the info. I live in Berlin, I don't know here the situation about clojure. But I guess there are also opportunities full remote from other countries right?

sP0re18:11:53

I have the feeling that it's a niche language but probably it's quite stable on the market (or maybe it's growing)

pyry19:11:21

Well, Nubank should have offices in Berlin, so there's at least that. 😄

pyry19:11:44

And you can also search with various criteria on sites such as https://functional.works-hub.com/ or https://jobs.braveclojure.com/

Ben Sless19:11:45

Seems like the jobs channels have been incredibly active in the past few years

sP0re20:11:57

Cool thanks! For now I have to think about learning the language and practicing 😁 I'm reading Clojure for brave and true, then I want to explore testing and also some stack for building APIs, use queues, etc

Ory Band21:11:46

Adding to what I wrote about my company looking for Clojure programmers - since Clojure experience is not very common, we are looking for people with knowledge in functional programming as well. Check the career page link I posted

👍 1
Muhammad Hamza Chippa21:11:48

I have list of urls (url1, url2, url3 ..) and I am trying to make a ajax/get-json request by mapping but it is not working anything wrong with that code? (map (fn [x] (ajax/get-json x (fn [response] (if (empty? response) (notify 102 "No results") (do (debug "intra-day-analysis returned...") (swap! intra-analysis-data assoc (keyword x ) response)))))) url-list)

pmonks21:11:47

map is lazy, so unless you’re doing something to realise the result, it will appear to not do anything. The easiest way to do that is to wrap the call to map in a doall - that forces de-lazying.

pmonks21:11:20

e.g. (doall (map (fn [x] (ajax/…

pmonks21:11:24

For anything side-effecty (especially I/O) within map, I usually just wrap it in a doall to force de-lazying ASAP. Makes it easier for me to reason about the temporality of the side effects.

Muhammad Hamza Chippa21:11:46

it works brother thank you so much

pmonks21:11:53

You’re welcome! Laziness is a common trap - I think everyone has been bitten by it at one time or another. 😉

🙌 1
seancorfield22:11:00

If you're calling the function purely for side-effects and don't care about the result, use run! (or doseq). Otherwise, consider mapv instead of doall/`map` (my opinion). mapv is eager -- based on reduce -- and you get a vector of the results.

🙌 1
emccue03:11:38

Also probably a time where your code could benefit from promises

clj꞉user꞉> 
(defn mock-ajax-get [callback]
  (future
    (Thread/sleep 1000)
    (callback {:data 123})))
#'user/mock-ajax-get
clj꞉user꞉> 
(def result 
  (p/all (for [_ (range 5)]
           (let [p (p/deferred)]
             (mock-ajax-get (fn [response]
                              (p/resolve! p response)))
             p))))
#'user/result
clj꞉user꞉> 
@result
[{:data 123} {:data 123} {:data 123} {:data 123} {:data 123}]
clj꞉user꞉> 

emccue03:11:53

(really liking the promesa library)

emccue03:11:40

so in this case you could make your requests in parallel, then just have regular code build up that map / do your notify

emccue03:11:48

no atoms/coordination required

adityaathalye03:11:41

Some observations re: OP's original code: 1. If a result isn't found, it's better to explicitly keep information of that fact. Chances are something else may need to make decisions about missing data later. 2. The atom seems unnecessary. Keywordising the URL seems unnecessary. 3. Even if one replaces map with mapv, the expression is doing too much. It is hard to test. It's harder to improve, e.g. notifier need not care how a response is fetched. It is also brittle because multiple things can go wrong (ajax fails, notify fails, ajax is slow and notify is slow etc.) Here's a translation of the original code to something more testable, debug-friendly, and robust:

(defn proc-response
  [response]
  ;; this definition of response can become a standard 
  ;; invariant for the whole program
  (if (empty? response)
    ::no-data
    response))

(defn fetch-intra-data
  ([url]
   (fetch-intra-data url ajax/get-json))
  ([url fetcher-fn] ; now it's easy to mock out ajax/get-json
   (fetcher-fn url
               proc-intra-response)))

(defn fetch-all-intra-data
  [url-list]
  (reduce (fn [intra-analysis-data urlx]
            ;; record data for all urls, response or not
            (assoc intra-analysis-data
                   urlx ; no need to keywordise url
                   (fetch-intra-data urlx)))
          {} ; now we don't need an atom
          url-list))

;; decouple notification logic from fetching logic
(defn notify-missing-analysis 
  [intra-analysis-data]
  (doseq [[url response] intra-analysis-data]
    (when (= response ::no-data)
      (notify 102 (str "No response for " url)))))

emccue21:11:19

your code isn’t async/parallel though^

1
adityaathalye03:11:45

It isn't, but we now have targeted places to put async in if we need to. Usually I'd just do straight-through processing, and consider async/parallel execution in places where bottlenecks form.

adityaathalye03:11:36

Also, since OP is new to Clojure, I thought better to try and provide the design feedback I did, over layering more functionality. That said, your point is well taken. Thanks :)