Fork me on GitHub
#beginners
<
2022-10-16
>
Tomasz Kontusz10:10:40

Is there any nice REPL I can use on an Android phone? So far I've tried: • Replate REPL, but it has some serious UI problems (mishandled pasting, duplicating characters), and • Replit, but this one is not really a Clojure REPL - it's a "run your whole script" setup that kills any background REPLs too eagerly (on the free version)

practicalli-johnny11:10:36

The nicest REPL I have found is to simply run Linux on an Android phone, using the Termux application from the F-Droid store Then you can use a Terminal UI REPL like Rebel Readline, or Neovim, or Emacs. This even works with Clojure LSP and Clj-kondo, to give a full development environment on your phone Ideally you would use an external keyboard (I also used a keyboard for Replete, which I found an okay app for a repl) https://practical.li/neovim/termux/

Tomasz Kontusz11:10:41

Thank you! I've forgot about running Linux tools on Android. I'll try setting that up.

practicalli-johnny11:10:32

Termux works very well. I’ve been using a Google Pixel 2XL and a Lenovo Tab P11 plus tablet successfully. I developed some code for my current work project using Termux setup, whist I was travelling (especially each time I’ve been delayed at the airport).

Joe Duhamel15:10:16

I'm trying to figure out the idiosyncratic approach to this. I know how to brute force it. I'm using repeatedly to generate 3,000,000 random strings from "1111111" to "9999999" but what I really want is 3,000,000 unique random strings. I can do (distinct (repeatedly 4000000 create-new-id)) and take the first 3,000,000 but that feels just a bit odd. Ideas?

pppaul15:10:50

can you give more context. how are you using these? you need them all in memory or you use one at a time and don't want dupes?

Joe Duhamel15:10:08

I need them in memory

Joe Duhamel15:10:53

Basically I'm generating random data and the item I'm generating has multiple keys which need to be unique..

pppaul16:10:41

unique matters, order doesn't?

pppaul16:10:18

if it matters, that sounds like you could get some easy bit based compression. if it doesn't, then maybe you could use a set over distinct

Joe Duhamel16:10:50

I was thinking doing a version of repeatedly with take-while.

Joe Duhamel16:10:52

but I didn't see how to reference the underlying coll in the take-while

Bob B16:10:12

something like (map str (take 3000000 (distinct (repeatedly #(+ 1111111 (rand-int 8888888))))))?

Joe Duhamel16:10:10

That works. Almost wish there was a take-unique 🙂

lassemaatta16:10:18

would it be completely silly to do something like (->> (range 0 10) (partition N) (mapcat shuffle) (map str))? that is, you take a block of N consecutive numbers and shuffle them to create some randomness but guaranteeing uniqueness

💪 1
Sam Ritchie16:10:50

(sequence
 (comp (distinct)
       (take 3000000)
       (map str))
 (repeatedly #(+ 1111111 (rand-int 8888888))))

Sam Ritchie16:10:10

there’s the transducer version, if you want to do a single pass

Sam Ritchie16:10:31

(let [src (repeatedly #(+ 1111111 (rand-int 8888888)))]
  (into #{}
        (comp (distinct)
              (take 3000000)
              (map str))
        src))

Sam Ritchie16:10:38

or if you want to get them into a set in memory

Joe Duhamel16:10:18

second is perfect

Joe Duhamel16:10:08

ok wow, comp is kinda cool.

Sam Ritchie16:10:50

yeah this is some conceptually tough stuff, comp with transducers

Sam Ritchie16:10:55

one sec and I can offer my “mental model” here

Joe Duhamel16:10:51

I'm finding some of the lazy-seq stuff challenging after coming to this with 15 yrs of common-lisp experience. so it's really quite fun.

Sam Ritchie16:10:44

it’s so good!

Sam Ritchie16:10:24

so, many of the sequence higher order functions return a “transducer” if you don’t pass the sequence argument;

Sam Ritchie16:10:17

at this level, you can think of these as functions that • take an item from the source sequence • generate 0, 1 or more items… so these are basically scanning transformations along some sequence. (map <f>) is shorthand for, take 1 item, apply f to it and pass ONE item back out. (mapcat <f>) is the more general version that can return 0 or more, and (filter <f>) does what you’d expect

Sam Ritchie16:10:45

(distinct) is basically a mapcat that passes on either nothing, or the input item, based on some internal state it keeps

Joe Duhamel16:10:49

right so in this case map is clear. distinct is acting as a filter.

Joe Duhamel16:10:07

The challenge for me is where is the state that distinct is looking at.

Sam Ritchie16:10:39

(defn distinct
  "Returns a lazy sequence of the elements of coll with duplicates removed.
  Returns a stateful transducer when no collection is provided."
  {:added "1.0"
   :static true}
  ([]
   (fn [rf]
     (let [seen (volatile! #{})]
       (fn
         ([] (rf))
         ([result] (rf result))
         ([result input]
          (if (contains? @seen input)
            result
            (do (vswap! seen conj input)
                (rf result input)))))))))

Sam Ritchie16:10:41

here is the definition

Joe Duhamel16:10:08

my first thoughts were (take-while distinct (repeatably "generator")

Sam Ritchie16:10:25

see (let [seen (volatile! #{})] ...) , so the transducer function is closing over some hidden state

Joe Duhamel16:10:40

ok so it's in the seen set.

Joe Duhamel16:10:02

much cleaner than (lambda (x) (setq seq (transducer) )

Joe Duhamel16:10:29

although under the covers it's conceptually similar-ish? just far more expressive.

Joe Duhamel16:10:20

Thanks for the mental map that really helped

Sam Ritchie16:10:20

for sure!

🙌 1
lassemaatta17:10:56

if you're gonna put the results into a set (and thus don't really care about the order), perhaps you could do something like (->> (map + (range 1111111 9999999 3) (repeatedly #(rand-int 3))) (map str) set) so you could avoid building the large intermediate state within distinct?

didibus22:10:01

I would have probably just done:

(->> (range 1111111 9999999)
     (shuffle)
     (take 3000000)
     (map str))
Which is nice and readable in my opinion, but not fully lazy.

Sam Ritchie22:10:13

That can have duplicates right? And the shuffle is shuffling an already random sequence?

Sam Ritchie22:10:54

I see what you’re trying to do but I don’t think that arity of rand-Int exists? https://clojuredocs.org/clojure.core/rand-int

Sam Ritchie22:10:08

Ah I think you meant range. Yes, a good idea too!

didibus00:10:58

Oh ya sorry, I meant range, let me update

Georgi Stoyanov19:10:31

Hi guys 🙂 I have the following dependencies in my project

:dependencies [[org.clojure/clojure "1.11.1"]
                 [enlive "1.1.6"]
                 [compliment "0.3.14"]
                 [morse "0.4.3"]]
and I'm using them
(ns clojure-noob.test
  (:require [net.cgrand.enlive-html :as html])
  (:require [morse.api :as telegram])
  )
but I receive an exception when I try to use the morse api
Execution error (FileNotFoundException) at clojure-noob.test/eval2760$loading (test.clj:1).
Could not locate morse/api__init.class, morse/api.clj or morse/api.cljc on classpath.
I think it's something really stupid cause I already have external libraries in my code that I use and they are working as expected PS ah it turns out I need to restart the repl so I solved it 😄 But will leave the message in case anyone have a similar problem

dharrigan20:10:51

Gald you got it sorted, btw, you only need one require:

dharrigan20:10:56

(ns clojure-noob.test
  (:require 
   [net.cgrand.enlive-html :as html]
   [morse.api :as telegram]))

1
Georgi Stoyanov21:10:24

@U11EL3P9U thank you, actually a good idea came into my mind - to try ask for a simple code review 😄 If you have time to spot some stupid things I made, I'll be really happy to address them and improve myself

Georgi Stoyanov21:10:25

Hi guys I just finished my first really small project with clojure. Since learning from your mistakes is the best way of doing it, could you make me a kind of code review?

(ns clojure-noob.test
  (:require [net.cgrand.enlive-html :as html]
            [morse.api :as telegram])
  )

(def date (java.time.LocalDateTime/now))
(def date-map {:day (.getDayOfMon9th date) :month (.getMonthValue date) :year (.getYear date)})
(def url (str "" (:year date-map) "-" (:month date-map)))
(def website-content (html/html-resource (java.net.URL. url)))


(defn get-current-feast
  []
  (->> (html/select website-content [:table :td])
       (rest)
       (map html/text)
       (remove clojure.string/blank?)
       (partition 3)
       (map #(let [[e1, e2, e3] %]
               {:day e1 :day-of-week e2 :content e3}))
       (filter #(= (:day %) (str (:day date-map))))
       (first))
  )
(def current-feast (:content (get-current-feast)))

(println current-feast)

(telegram/send-text "my-token" "my-chat-id" current-feast)
Also is there a way I can make this code automatically executed every day at same time?

valerauko01:10:23

That date won't be "now" at every point in time. It'll always be the time when the app started up.

1
Øyvind Jensen05:10:52

@U03SN3M2W0Y: When I wrote a scheduled job, some years back, I used Immutant (http://immutant.org/documentation/current/apidoc/guide-scheduling.html) to do the scheduling - mostly because it was the cleanest way of setting it up. I believe the alternative, that I didn't go for back then, was Overtone (https://github.com/overtone/overtone).

1
pithyless07:10:33

1. FYI, there is also a channel dedicated to code reviews: #code-reviews 2. One popular library to schedule repeating time-based tasks is the Java library: http://www.quartz-scheduler.org/ 3. There are several Clojure bindings to (2) on github; for example, this one: https://github.com/factorhouse/cronut 4. Using any of these scheduling libraries assumes that your JVM process will keep running, so you will need to make sure if it crashes or you restart the computer, etc. that the process starts up again. If this is something that runs infrequently, you may consider instead to just write a -main function (or convert this to a #babashka script) and run it via the operating system scheduler (eg. on linux or macos this would be cron - https://linuxhint.com/cron_jobs_complete_beginners_tutorial/)

1
rolt12:10:18

small note: you should require clojure.string if you're using it. You can't always expect that it"s already loaded most linters will warn you about missing requires

1
mbjarland12:10:18

@U04290RJV0F ...hmm, I believe overtone is for music creation?

Øyvind Jensen06:10:39

@U4VDXB2TU: Correct, and since music has to get timing right they made https://github.com/overtone/at-at.

mbjarland06:10:07

@U04290RJV0F Ah that makes sense now. Thanks for the pointer.