clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Jakub Holý 2021-04-16T07:33:52.154Z

For a project I have written a stateful map and stateful filter using transducers. Perhaps it can be useful to others as well? https://gist.github.com/holyjak/578571a134ce90526e6907436e91014a

Ben Sless 2021-04-16T09:17:39.154200Z

Can it be generalized to a reducing function?

Ben Sless 2021-04-16T09:33:22.154400Z

I think there's some common ground with https://github.com/cgrand/xforms/blob/master/src/net/cgrand/xforms.cljc#L491

Ben Sless 2021-04-16T10:00:06.154700Z

With something like

(defn with-state
  [txf trf f init]
  (let [state (volatile! init)
        tf (txf trf)]
    (fn [in]
      (let [old @state]
        (vswap! state tf in)
        (f old in)))))
You can generalize. the t stands for transition

Ben Sless 2021-04-16T10:02:34.154900Z

With some more hand waving:

(defn with-state
  ([trf f]
   (with-state trf f nil))
  ([trf f init]
   (with-state identity trf f init))
  ([txf trf f init]
   (let [state (volatile! init)
         tf (txf trf)]
     (fn [in]
       (let [old @state]
         (vswap! state tf in)
         (f old in))))))

(defn right [x y] y)
(sequence (stateful-filter not= right) [1 2 2 2 3 4 4 5])
(sequence (filter (with-state right not=)) [1 2 2 2 3 4 4 5])

Jakub Holý 2021-04-16T10:55:45.155100Z

Great comments, Ben, thanks a lot!

Ben Sless 2021-04-16T11:25:51.155300Z

Cheers 🙂

Ben Sless 2021-04-16T11:28:36.155500Z

If you don't want to use a volatile for performance reasons, since it won't be thread safe anyway, you can use a box type. Probably overkill 🙂

(definterface IBox
  (_put [x])
  (_look []))

(deftype Box [^:unsynchronized-mutable v]
  IBox
  (_put [this x] (set! v x))
  (_look [this] v))

(defn with-state2
  ([trf f]
   (with-state2 trf f nil))
  ([trf f init]
   (with-state2 identity trf f init))
  ([txf trf f init]
   (let [^Box state (->Box init)
         tf (txf trf)]
     (fn [in]
       (let [old (._look state)]
         (._put state (tf old in))
         (f old in))))))

❤️ 1
Ben Sless 2021-04-16T11:28:55.155700Z

I used an interface because I couldn't get rid of reflection warnings otherwise

Ben Sless 2021-04-16T11:30:17.155900Z

Probably didn't try hard enough

Timofey Sitnikov 2021-04-16T12:11:25.160100Z

Good morning Clojurians, I read the https://unixsheikh.com/articles/sqlite-the-only-database-you-will-ever-need-in-most-cases.html article this morning and wanted to know if it makes sense for a small Clojure website with the DB on the the same machine, with the goal to minimize the complexity. Also, if SQLite is used, does it make sense to use HekaryCP?

Timofey Sitnikov 2021-04-17T10:29:14.184600Z

@vemv, Yes, this is an interesting comment, I do feel the same way. That article made me waver but every developers dream is for their website to go viral. If that happens, and multiple machines need to be spun up, scaling postgres is quick, so it is probably a better choice.

nwjsmith 2021-04-16T12:36:55.160200Z

Good morning! I've been building some automation at work with Clojure/SQLite recently, and it's been great. Would be fantastic for a small website. Regarding connection pooling, it might make sense to use it. The JDBC driver for SQLite will serialize when using the same connection across multiple threads: https://github.com/xerial/sqlite-jdbc/issues/369

nilern 2021-04-16T13:02:26.160500Z

Did you know that https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Box.java

👀 1
nilern 2021-04-16T13:05:18.160800Z

There are also pure Java alternatives like H2, Derby and HSQLDB

👍 1
nilern 2021-04-16T13:07:34.161100Z

TBH I have only used SQLite though. It has been pretty good although the column types are kind of crappy compared to e.g. Postgres.

Timofey Sitnikov 2021-04-16T13:09:44.161300Z

I do wonder if the SQLite is quicker than Postgres when both run on the same machine.

nwjsmith 2021-04-16T13:28:01.161500Z

That'll depend on the workload. For something read-heavy like a website, SQLite will probably be as fast or faster than PostgreSQL. For anything write-heavy, PostgreSQL will perform better.

Jakub Holý 2021-04-16T13:42:32.161800Z

not applicable, since we need something mutable

vemv 2021-04-16T15:40:50.162200Z

"small website" is really ambiguous wording tbh. apps can be small but also represent a lot of work and hopes (e.g. your MVP startup) any startup that intends to satisfy actual customers should have make a minimally reasonable HA setup. That practically rules out sqlite and friends I don't want to come off as dogmatic but honestly "cattle not pets" is pretty hard to argue against in 2021. You can get a HA psql for 100 bucks or whatever on aws/heroku/...

Huahai 2021-04-16T15:49:15.162500Z

Shameless plug: if you are not wedded to SQL, you may consider Datalevin, a SQLite like Datalog database for Clojure, it will be much simpler to use in Clojure, no drivers to worry about and native EDN input/output

1
2021-04-16T18:04:57.163Z

Ya, I feel for a small use case like you describe, it will be even better to go with a Java native solution like H2 or Datalevin. Then you don't even need to install anything else, its all embedded in your app.

NoahTheDuke 2021-04-16T18:57:41.164600Z

is this an idiomatic way to schedule a function to be called every second or so? I need it in two places and don't want a whole dependency (like chime) to handle it.

(defn tick
  "Call f every ms. First call will be after ms"
  [active? callback ms]
  (future
    (while @active?
      (Thread/sleep ms)
      (callback))))

borkdude 2021-04-16T18:59:21.165300Z

Maybe you need some error handling in case callback fails?

👍 1
NoahTheDuke 2021-04-16T19:02:21.165600Z

good point

2021-04-16T19:08:23.166600Z

you might want something more robust than Thread/sleep alone if the timing precision matters, but it probably doesn't

NoahTheDuke 2021-04-16T19:08:57.167600Z

it does not. This is to clean up empty lobbies in a game sever

2021-04-16T19:09:35.168300Z

also, I like to use a delay or promise for turn-off-switch args, because unlike an atom they are simple, limited purpose, and one way

2021-04-16T19:10:07.169Z

I like to use the weak / specific construct when possible, it helps clarify intent

NoahTheDuke 2021-04-16T19:10:59.169600Z

I don't know much about those objects. do you have an example of how I would use it here?

2021-04-16T19:11:13.169900Z

example coming up

👍 1
2021-04-16T19:17:08.171700Z

@nbtheduke

(ins)user=> (defn tick [done? callback ms] (future (while (not (realized? done?)) (callback) (Thread/sleep ms))))
#'user/tick
(cmd)user=> (def signal (delay :done))
#'user/signal
(cmd)user=> (tick signal #(println "still going") 10000)
#object[clojure.core$future_call$reify__8454 0x1dab9dd6 {:status :pending, :val nil}]
still going
user=> still going
(force signal)
:done
also I changed the order of callback and Thread/sleep because calling callback again after cancelled seems odd

2021-04-16T19:18:24.172400Z

also the code becomes clearer if you have a helper (def pending? (complement realized?))

alexmiller 2021-04-16T19:40:03.173500Z

or the older https://docs.oracle.com/javase/8/docs/api/java/util/Timer.html

Ben Sless 2021-04-16T19:48:05.173700Z

Java, mutable by default

(.val ^Box b)
1
user=> (set! (. ^Box b val) 2)
2

🤦 1
Ben Sless 2021-04-16T19:48:22.173900Z

I did not know we had this object lying around 🙂

NoahTheDuke 2021-04-16T20:14:28.174600Z

Thank you! I’ll read this over

Leonid Korogodski 2021-04-16T20:28:52.176500Z

The oddest thing. I have two projects. Both use malli 0.2.1 (according to lein deps :tree). But one of them accepts the syntax [:map [:a string?] [:b string?]] just fine, while the other throws an error on (restart):

data-spec collection [] should be homogeneous, 3 values found
Any idea what could be the cause?

borkdude 2021-04-16T20:33:46.176900Z

@lkorogodski Perhaps a question for #malli

Leonid Korogodski 2021-04-16T20:34:25.177300Z

Thanks.

p-himik 2021-04-16T20:35:31.177400Z

If that error might be caused by a difference in malli versions, then perhaps you have an uberjar on your classpath that contains a different version of malli.