other-languages

here be heresies and things we have to use for work
benoit 2021-03-07T02:07:52.024200Z

I'm not sure to see the difference in coupling in your example between Clojure and Java. For both languages, the function that manipulates the customer is coupled to the description of the customer. If you change the key in your Clojure map representing customer data or if you change the name of the attribute on your Java class, both functions will not work as expected. For me, that's what coupling means. A change in one part propagates to the other part. Two things are coupled because they are somehow (logically) linked together and effects propagate over this link.

2021-03-07T02:38:29.024400Z

I’ll give it a shot 😅 So at a former job, we had this very specific Bid class that represented a Bid model, stored in a bids table in the db. It had methods for dealing with it from all possible contexts: public users, vendors, and admins. The problem is, the idea of a Bid is actually pretty different between each group and by the time I left, the Bid model was gigantic with methods like .send_emails then .send_vendor_emails and .send_user_acknowledge_emails. If this were Clojure we could have had a common set of attributes that represented all possible fields from the db, then defined a Bid for each context separately by composing those attributes and having groups of functions for operating on it for each context like company.user/send-emails and company.vendors/send-emails. Subclassing addresses some of the surface level problems but in reality it’s the same data type, but have different meaning in each context. I hope that makes sense.

Yehonathan Sharvit 2021-03-07T08:52:35.025Z

@jayzawrotny I think your example illustrates the benefits of separating code from data. Could you have gained the benefits you mentioned by storing bid data in various data classes like BidForVendors, BidForUsers …?

2021-03-07T16:08:23.025700Z

If you know all the possible contexts from the beginning, it may be possible to get some of those advantages with subclasses. But realistically, new contexts emerge which means a continuous cycle of moving methods and attributes from the base class into the subclasses. The one wall you might hit is if you want to perform a sequence of actions across contexts. So if an admin closes the bid, we may want to send closed emails to both the vendors and users. With that separate Clojure setup, that’s trivial to do since you have a single bid object hash-map that can be passed between functions across any context. With subclasses, you would need to create new instances of the next subclass you need:

2021-03-07T16:14:16.026100Z

(ns company.admin.bid.api
  (:require
   [company.db :as db]
   [company.user.bid :as user]
   [company.vendor.bid :as vendor]))

(defn close
  [bid]
  (let [closed-bid (assoc bid
                          :closed true
                          :status :closed)]
    (db/update! db/conn bid)
    (user/send-closed-bid-email closed-bid)
    (vendor/send-closed-bid-email closed-bid)))

Yehonathan Sharvit 2021-03-07T16:21:36.026300Z

I see. I think it’s called the ClassAWithThisAndThat problem

Yehonathan Sharvit 2021-03-07T16:21:52.026500Z

You need to create a class for every combination of fields!

Yehonathan Sharvit 2021-03-07T16:21:58.026700Z

That’s insane 😱!

Yehonathan Sharvit 2021-03-07T16:23:15.027100Z

What’s even more insane is that Java folks don’t seem to be aware of this insanity