clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Matt Roll 2021-06-14T00:33:07.351600Z

more specifically I’m interested how people are integrating datahike/datascript into projects? Do you run clojure on a server-side framework like pedestal and have datahike as a dependency for communicating w/ say postgres?

borkdude 2021-06-14T08:31:41.361300Z

Asami also has disk storage now btw

👀 2
Matt Roll 2021-06-14T00:33:37.352100Z

Trying to do research but there doesn’t seem to be a lot written about this that is accessible for someone new to the clojure ecosystem

seancorfield 2021-06-14T00:44:36.352200Z

If you’ve required next.jdbc.date-time then this protocol will be in effect: https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/date_time.clj#L40-L50 and Instant will be stored via .setTimestamp. If you’re seeing that exception, That protocol is not in effect. Without seeing more of your code, it’s hard to tell what you’re doing tho’

seancorfield 2021-06-14T00:44:53.352400Z

(also, I’d have probably seen this sooner if it was posted in #sql)

2021-06-14T01:59:59.352700Z

Datascript is normally used in ClojureScript web-apps as an in-memory DB where you maintain the app state (like the UI/UX state). Datahike is used for very small applications, it be a good candidate for a desktop app written in Clojure. Or a client/server app, but with very small number of concurrent users. So maybe internal tooling and such. It doesn't scale to large dataset and it runs on a single machine, so it is made for client/server apps where your server is expected to run in a single node, thus non-distributed. Don't expect it to be resilient either, it won't replicate itself or anything, so have backups. In neither case is PostgressSQL involved. With Datascript, sometimes people have POstgressSQL as their backend DB to their webapp, and DataScript as the client-side in-memory DB used by the client.

👍 1
2021-06-14T02:01:55.353Z

I think people use either Ring or Pedestal for the most part.

2021-06-14T02:08:45.353200Z

Other used ones, but I'd say not as common as Ring or Pedestal are: Yada, Holplon and Duct.

2021-06-14T02:16:19.353400Z

Otherwise, if you need high scale, resiliency, fault tolerance, you are looking at using Datomic on the backend or Crux. Those would replace your use of PostgressSQL.

2021-06-14T02:20:10.353600Z

So, best to think of it this way: For server backend: - Where you'd use SQLite -> use Datahike or Datalevin - Where you'd use PostgressSQL, MongoDB, etc. -> use Datomic or Crux For client-side apps: - Where you'd use SQLite -> use Datahike or Datalevin or DataScript (where you'd save/load to/from EDN on disk) - Where you'd just store data in-memory in atoms or variables -> use DataScript or Asami

2021-06-14T02:21:27.353800Z

Or you can always choose to also just use SQLite, PostgressSQL, H2, and other relational databases. The above is if you'd prefer using a Datalog based more graph oriented DB instead.

Matt Roll 2021-06-14T02:56:13.354900Z

@didibus this is exactly the kind of answer i was looking for! Very helpful in wrapping my head around where these tools fit into the stack.

Matt Roll 2021-06-14T02:57:21.356600Z

I’m looking to try a graph oriented db for a project I’m starting. It’s a relatively standard web app with a user interface in the browser and needs auth and data store. Crux sounds like the best free option?

2021-06-14T03:00:07.356800Z

Well, I'd say it depends what this project is. Like if its a hobby, you can start with Datalevin or Datahike. That will give you a chance to learn the whole Datalog thing. And then if you need to scale, you could afterwards consider switching to Crux.

2021-06-14T03:00:44.357Z

And if your browser user interface is a SPA, you can try using DataScript in it.

Matt Roll 2021-06-14T03:28:35.358500Z

Yes it’s a hobby project with no users yet so the datahike idea might be good to try. And yes it’s an SPA in react so I think I’d like to try datascript as well.

Matt Roll 2021-06-14T03:29:24.360Z

One question about hooking the client up to the server - do you just use standard JSON over http to communicate, and then stick the parsed data into datahike/datascript?

2021-06-14T04:29:07.360200Z

Its more typical to use Transit when your use case is Clojure backend and ClojureScript frontend, and you don't intend other languages to be using the APIs. You should look into: https://github.com/metosin/muuntaja/ [preferred most commonly used] https://github.com/ngrunwald/ring-middleware-format [simpler, you could just use EDN directly with this one, only downside is slower than muuntaja]

2021-06-14T04:29:49.360500Z

The advantage of Transit over raw JSON is that Transit can encode all the Clojure standard types like keywords, where-as JSON can't.

2021-06-14T04:30:33.360700Z

Using EDN directly is nice, but the disadvantage is the compression and performance of serializing/deserializing JSON isn't as optimized as Transit.

2021-06-14T04:47:40.360900Z

A good place to start on the server would be with: ring/ring-core ring/ring-jetty-adapter metosin/reitit metosin/muuntaja

Matt Roll 2021-06-14T05:26:28.361100Z

you’re a saint @didibus this is a treasure trove of info for a beginner and gives me a solid foundation and direction to work in. Many thanks! 🙂

Endre Bakken Stovner 2021-06-14T15:03:10.365400Z

I've never used spec. Is there a way to ensure that (during development) whenever the key :mykey is used in a map the corresponding value has to have certain properties?

alexmiller 2021-06-14T15:04:15.366600Z

As a blanket check, no - you need to either explicitly validate with a spec or instrument a spec’ed function to enable this

👍 1
alexmiller 2021-06-14T15:05:18.366900Z

See https://clojure.org/guides/spec for a tutorial

Endre Bakken Stovner 2021-06-14T16:00:50.369500Z

What is a good and fast way to find the hash of a hash-map that will be the same between different invocations of my Clojure? And preferably between different versions of Clojure.

Endre Bakken Stovner 2021-06-14T16:01:20.370500Z

Like, will (hash {:a "hey"} always return the same value?

ghadi 2021-06-14T16:01:21.370600Z

don't rely on hashes unless you've defined the hashing algo

Endre Bakken Stovner 2021-06-14T16:01:41.370800Z

Okay, I'll find my own then.

ghadi 2021-06-14T16:02:26.371700Z

what goal are you trying to achieve with a stable hash?

Endre Bakken Stovner 2021-06-14T16:03:40.373300Z

I have lots of metadata about how a file was created. I'd like to turn that metadata into a path prefix so that I can easily tell whether the file was made with the current settings or needs to be recreated.

Endre Bakken Stovner 2021-06-14T16:03:53.373500Z

Cool!

dpsutton 2021-06-14T16:06:15.375500Z

i've been down similar roads before, and hash is very unforgiving of equivalence. if you change a representation but it "means" the same thing

Endre Bakken Stovner 2021-06-14T16:07:50.375700Z

I know that I will have many false negatives, but it is worth it to ensure the files from my workflows are up to date.

2021-06-14T16:15:21.378400Z

how can one branch out in a transducer pipeline?

(comp
 (map parse-string)
 (xif some-branching-condition
      (comp (map extra-work) (map some-work))
      (map some-work))
 (map process))
i.e. how to implement something like xif? some-brancing-condition is a function of the input i thought the following would work but ofcourse it isn’t
(defn xif
  [f if-xform else-xform]
  (fn [rf]
    (fn
      ([] (rf))
      ([result] (rf result))
      ([result input]
       (let [nrf (comp (if (f input)
                         if-xform
                         else-xform)
                       rf)]
         (nrf result input))))))
I can certainly get away with it by doing the following
(comp
 (map parse-string)
 (map (fn [m]
        (if (some-branching-condition m)
          (some-work (extra-work m))
          (some-work m))))
 (map process))
i wonder if there was a better way

dpsutton 2021-06-14T16:17:12.378800Z

(comp
 (map parse-string)
 (map (if some-branching-condition identity extra-work))
 (map some-work)
 (map process))

2021-06-16T13:58:48.432600Z

that's a good call - there's no reason the function inside map can't contain a conditional

2021-06-14T16:22:21.379Z

yes certainly, for this simple case i didn’t want to introduce any hypothetical scenario just for the sake of a solution, but a construct like

(defn xif [f if-xform else-xform])
could be more expressive i don’t think its trivial to implement a linear transducer if the if-xform & else-xform in itself were some complicated transducers

2021-06-14T16:23:11.379300Z

i wonder if i am hitting the limitation of transducers here? or is this possible?

2021-06-14T16:23:39.379500Z

tried looking at xforms library. it didn’t have anything either

2021-06-14T16:25:00.379700Z

actually i wonder if it even makes sense

2021-06-14T16:25:52.379900Z

e.g. if it was then what would the following even do if some-branching-condition kept alternating for each element

(comp
 (map parse-string)
 (xif some-branching-condition
      (comp
       (take 10)
       (filter extra-work)
       (map some-work))
      (map some-work))
 (map process))

2021-06-14T16:27:29.380200Z

my bad, some-branching-condition is a function of the input

2021-06-14T17:06:07.381500Z

oh - I misunderstood here, deleting my irrelevant suggestions

2021-06-14T17:08:25.382500Z

i don’t think it makes much of a sense except for simple branches as explained here https://clojurians.slack.com/archives/C03S1KBA2/p1623687952379900?thread_ts=1623687432.378800&cid=C03S1KBA2

2021-06-14T17:09:16.382800Z

i was probably trying to squeeze too much out of transducers 😅

2021-06-14T17:09:56.383100Z

from a higher level vantage point, it's much easier to use comp / fn first then optimize with transducers later

🙌 1
2021-06-14T17:10:52.383400Z

doing the transducing part first puts more demands on your code reading ability, is much more error prone, and can't do anything functions can't, except for doing it faster

🙌 1
Ed 2021-06-14T17:19:08.383700Z

(defn if-tx [test-fn true-xf false-xf]
  (fn [rf]
    (let [t (true-xf rf)
          f (false-xf rf)]
      (fn
        ([] (rf))
        ([result] (f (t result)))
        ([result input]
         (if (test-fn input)
           (t result input)
           (f result input)))))))

(comment

  (sequence (comp (map inc)
                  (if-tx even? (map str) (map double)))
            (range 10))

  )
I think this does what you want ... but I'm not sure it's a good idea ...

➕ 1
2021-06-14T17:20:19.384Z

i certainly won’t use that 😛 but i learnt something about the inner working of transducers today (that let binding) :thanks2:

Ed 2021-06-14T17:29:10.384800Z

I do keep thinking that it would be nice to have more expressive ways of combining transducers together ... but I can't say for certain that I've really got that problem ... I keep having doubts that I'm just creating a big mess and just writing the straight code would be easier to read and work with ... I guess it depends on how much you end up sharing transducers across modules (or whatever)

2021-06-14T17:33:41.385200Z

i am actually using core async which is why I'm leveraging transducers more often. without them i end up something like https://blog.golang.org/pipelines

2021-06-14T17:33:47.385500Z

which is fine

2021-06-14T17:34:33.385800Z

anything that gets difficult via transducers can be easily plucked out into its own function that accepts from an in chan and outputs to an out chan

zendevil 2021-06-14T19:18:41.386500Z

What is a chunked-seq? , chunk , etc. and why is there no documentation about it? In the definition of concat, why is there a check for chunked-seq? https://github.com/clojure/clojure/blob/38bafca9e76cd6625d8dce5fb6d16b87845c8b9d/src/clj/clojure/core.clj#L718

2021-06-14T19:20:51.387700Z

chunking is a behavior that realizes N elements of a lazy seq instead of just one at a time

2021-06-14T19:21:00.388Z

I'm not sure about the documentation

2021-06-14T19:21:37.388300Z

concat is preserving the chunkiness of the input

2021-06-14T19:29:57.390500Z

@ps just subjectively I've seen little documentation about chunked lazy collections, and when people run into bugs caused by chunking the standard reply is that if realizing 32 items for one call instead of one item per call breaks your algorithm you shouldn't be using a lazy data type anyway

alexmiller 2021-06-14T19:30:18.390900Z

these functions are designed for implementors of chunked sequence functions, not for general api usage (which is why they are not part of the public docs)

alexmiller 2021-06-14T19:36:17.392900Z

Rich did a great talk about chunked seqs at the bay area clojure meetup during JavaOne in 2009. I happened to be there but I don't know that it was recorded.

alexmiller 2021-06-14T19:37:47.393Z

hey look, I wrote stuff down... https://puredanger.github.io/tech.puredanger.com/2009/06/10/clojure-rich-hickey/

1
👀 3
1
2021-06-14T23:34:11.393300Z

The transducer loops one element at a time and applies all transforms. So you can just do:

(sequence (comp (map inc)
                (map #(if (even? %) (str %) (double %))))
          (range 10))