Fork me on GitHub
#beginners
<
2024-02-04
>
stopa03:02:16

Hey team, I discovered https://github.com/mozilla/mentat/tree/clojure -- a datomic-like wrapper over sqlite, built ~8 years ago. I wanted to play with it, and am trying to run it on my computer. Right now, when I run lein deps I get:

#:clojure.error{:phase :compile-syntax-check, :line 282, :column 1, :source "clojure/core/rrb_vector/rrbt.clj", :symbol deftype*}
I am guessing this is because the version of clojure / lein they used is quite ancient, and I need to go back to it. Is there some incantation I can run with lein, which "sets" me into the same world that this library was in?

stopa03:02:43

Update: I realize lein already installs the older version of clojure (it is specified in the project.clj). I think I may need to downgrade the JDK. Will look into jdk version managers

hiredman04:02:47

What you linked to doesn't have a project.clj

hiredman04:02:02

My guess is you have lein plugin or something that is failing, what is the rest of the stacktrace

hiredman04:02:51

That looks like something is trying to load rbb-vector and failing because the version of clojure being used pre-dates deftype

hiredman04:02:22

It is extremely unlikely that something would specify an ancient version of clojure, but also try and require the rrb vector library, since the library didn't exist then

👍 1
hiredman04:02:34

Which is why my guess is some bit of build tooling or plugin you have in your user profile or something is injecting some dependencies and causing some code to load

stopa04:02:18

https://github.com/mozilla/mentat/blob/clojure/project.clj ^ this is their project.clj Oo will check if I have any plugins. Thanks @U0NCTKEV8 !

hiredman04:02:01

That is not an old enough version of clojure to lack deftype

hiredman04:02:29

Maybe it is trying to load clojure source as clojurescript? You really need to look at the whole stacktrace to see what was being done

seancorfield06:02:52

@U0C5DE6RK I cloned the repo, checked out the clojure branch (with the project.clj file) and got it running with JDK8: lein deps or lein test both work. It doesn't work with JDK11 or later.

seancorfield06:02:51

All the dependencies in that project are pretty ancient so that's why they're not compatible with more modern JDKs -- so it might be a combination of updating all the dependencies and making some code changes if you want to try it with a newer JDK.

stopa23:02:45

JDK 8 did the trick -- thanks @U04V70XH6 and team!

nando16:02:33

As a side note, I’ve been able to get Datomic-Pro working using SQLite as its storage service. I can share details if anyone is interested.

Rohan Sharma15:02:24

Hi, I am following a particular example in web on reducers. The task is to find the total number of children in a Village:

(def village
  [{:home :north :family "smith" :name "sue" :age 37 :sex :f :role :parent}
   {:home :north :family "smith" :name "stan" :age 35 :sex :m :role :parent}
   {:home :north :family "smith" :name "simon" :age 7 :sex :m :role :child}
   {:home :north :family "smith" :name "sadie" :age 5 :sex :f :role :child}

   {:home :south :family "jones" :name "jill" :age 45 :sex :f :role :parent}
   {:home :south :family "jones" :name "jeff" :age 45 :sex :m :role :parent}
   {:home :south :family "jones" :name "jackie" :age 19 :sex :f :role :child}
   {:home :south :family "jones" :name "jason" :age 16 :sex :f :role :child}
   {:home :south :family "jones" :name "june" :age 14 :sex :f :role :child}

   {:home :west :family "brown" :name "billie" :age 55 :sex :f :role :parent}
   {:home :west :family "brown" :name "brian" :age 23 :sex :m :role :child}
   {:home :west :family "brown" :name "bettie" :age 29 :sex :f :role :child}

   {:home :east :family "williams" :name "walter" :age 23 :sex :m :role :parent}
   {:home :east :family "williams" :name "wanda" :age 3 :sex :f :role :child}])

Rohan Sharma15:02:11

I have tried the following solution which does not seem to work(throws exception):

(def map-children-to-value1 (map #(if (= :child (:role %)) 1 0)))
(reduce + 0 (map-children-to-value1 village))
But if change it slightly, it works
(def ar (map #(if (= :child (:role %)) 1 0) village))
(reduce + 0 ar)

Rohan Sharma15:02:25

Could someone please explain why the first solution does not work.

RK C14:02:58

(map f) ==> returns a transducer It doesn't return a simple function. I'm new to Clojure but I don't think you can use a transducer as the function parameter in reduce You could try this - make map-childeren-to-valu1 a function (defn map-children-to-value1 [x] (map #(if (= :child (:role %)) 1 0) x)) => #'get-started.hello-repl/map-children-to-value1 (map-children-to-value1 village) => (0 0 1 1 0 0 1 1 1 0 1 1 0 1) and pass that to reduce (reduce + 0 (map-children-to-value1 village)) => 8

kennytilton15:02:00

Just curious. Did you mean to create a transducer in the first solution? https://clojuredocs.org/clojure.core/map

Rohan Sharma15:02:59

yeah mostly, but I wanted to define a separate variable for the long map expression.

kennytilton15:02:42

Sorry, I forgot to respond in a thread, :face_palm: "I wanted to define a separate variable for the long map expression." I would move just the

#(if (= :child (:role %)) 1 0) 
...into a standalone fn using defn, then use it inside the reduce fn. Let reduce iterate over village, and the reducer + handle the summing.

kennytilton15:02:35

Wait, that's a mess ^^^. brb

Bob B15:02:41

the reason the first example throws an error is that map with one arg creates a transducer (as was eluded to earlier), and transducers aren't quite functions that can be called in function position... they require a transducing context, like sequence for example. There's a deeper explanation of transducers at <https://clojure.org/reference/transducers>. To tweak the first example:

(def map-children-to-value1 (map #(if (= :child (:role %)) 1 0)))
(reduce + 0 (sequence map-children-to-value1 village)) ; => 8

; or even just
(transduce map-children-to-value1 + 0 village) ; => 8
A slight tweak that might be a bit shorter is:
(count (filter #(= :child (:role %)) village)) ; => 8

🤙 2
kennytilton15:02:56

haha, I do not use reduce much, either! Agreed with @U013JFLRFS8 on the simpler solution using sequence operators. If the exercise really wants reduce to be used, and we do not want simply to say (reduce + <a sequence>), you can do (tested!):

(reduce add-child-ct 0 village)
...and in the add-child-ct function apply your logic to reach resident, adding the result to the first (accumulator) param. Now as punishment for my sloppy help I will try again using a transducer. 🙂

kennytilton16:02:45

Ah, you were close @U06GX80NS78 :

(transduce map-children-to-value1 + village)
Tested. 🙂

Ben Lieberman16:02:42

or this kinda cursed solution:

(transduce
 (comp (map :role)
       (replace {:child 1
                 :parent 0})) + 0 village)

adi06:02:15

@U06GX80NS78 I feel you can build on this idea:

(reduce
  (fn [counts x] (update counts (:home x) (fnil inc 0)))
  {}
  village)

;=> {:north 4, :south 5, :west 3, :east 2}

Rohan Sharma07:02:59

Thanks everyone. I read on Transducer after seeing @U013JFLRFS8 comment. I think it is making more sense now on how the first example is not working. Also got a lot of different ideas to implement the same.

👍 1
nitin09:02:33

(reduce (fn [acc {:keys [home role]}]
            (merge-with + acc (when (= role :child) {home 1})))
          {} village)
Using just merge-with to replace update and fnil in @U051MHSEK's idea.

nitin09:02:10

If you were to remove the check for (:role :child) and use merge-with alone:

(reduce (fn [acc {:keys [home]}]
            (merge-with + acc {home 1}))
          {} village)
It would still work correctly because merge-with does not rely on the absence of a key to determine the default value, it simply merges the values of matching keys according to the provided function (`+` in this case).

nitin10:02:08

Which you can then generalize to find count according to the predicate passed:

(defn count-matches [target-key predicate accum-key coll]
  (reduce (fn [acc item]
            (merge-with + acc (when (predicate (target-key item)) {(accum-key item) 1})))
          {} coll))

(count-matches :role #(= % :child) :home village)
(count-matches :sex #(= % :m) :family village)
(count-matches :age #(< % 18) :home village)

Edit: Swapped predicate, target-key order in args to give better reading.
:)

adi12:02:57

More or less that. If the solution needs to be that general, I think I would lift out a "counter" method.

(defmulti counter ...) ; return the appropriate counter function

(defn village-stats
 [stat-type villages]
 (reduce (counter stat-type) {} villages)
(edit: with a little rejigging, the counter could emit a reducing transducer per stat-type.)

adi13:02:35

Re: the merge-with solution, I was thinking of the more boring alternative...

(reduce
  (fn [counts x] 
    (if (pred x) ; e.g. child?
      (update counts (:home x) (fnil inc 0))
      counts)
  {}
  village)

Rohan Sharma15:02:21

Also, I noticed that if I use clojure.core.reducers instead, the code seems to work fine as well. Not sure how reducers map is different compared to core map

(def map-children-to-value1 (r/map #(if (= :child (:role %)) 1 0)))
(r/reduce + 0 (map-children-to-value1 village))

nitin11:02:38

This explains why, way better than I would be able to: https://www.braveclojure.com/quests/reducers/know-your-reducers/

sheluchin21:02:59

I'm working on a CLJS app. After connecting to the shadow build, I can print stuff and it shows up in my browser console as well as in my editor's REPL window. I'm wondering why the values print as I expect in the browser console, but show up like this in the REPL window:

; eval (root-form): (go (-> (http/send! client {:method :get :url "<https://h>...
#object[cljs.core.async.impl.channels.ManyToManyChannel]
whereas in the browser it just shows me {:foo bar}

Omer ZAK22:02:22

(The real question is about good methods for finding Clojure resources for _____ when Google is not helpful.) Are there free resources about Clojure for data sciences? I am looking for a function to fit a Gaussian to a vector of [x,y] values. https://github.com/phronmophobic/dewey was not helpful for me. Neither was Google (irrelevant URLs, books which cost money for unlimited access).

Bob B22:02:52

In general, it can be difficult for any language to sort of centralize diverse sub-communities. IMO, this slack is probably the best way to get clued in to "where to look for X". To the specific question about data science, I'm aware of the scicloj community that is quite active in the clojure data science space. There's a site at <https://scicloj.github.io/> that has learning resources and libraries and that type of content. There are also some channels on this slack like #data-science and #sciclojml where folks might be able to make even more specific recommendations.

👍 2
2