Fork me on GitHub
#off-topic
<
2022-06-12
>
Benjamin16:06:36

util vs utils and why. For example for namespaces / files

1
🍿 1
lread17:06:53

Some think both are code smells!

Benjamin17:06:29

damn that sounds purist. Is there a blog post making the argument?

lread17:06:14

I’ve heard it many times over the years. The argument I remember is that you are maybe being lazy. Just come up with a good name and group like-things under that name. I prefer not using ‘utility’ groupings because they often become a kitchen sink of unrelated functions. But I do use them. I’m only human after all.

Benjamin17:06:16

kitchen sinks can be useful 😛 but I get the point

✔️ 1
teodorlu18:06:56

I tend to just inline small utilities as private functions next to where they are used used if I can't figure out a better namespace and var name. Where private functions are used is never vague, current namespace only. "util" is a quite vague name. Perhaps not even a name, just a place to put stuff. I guess whether you value reuse / SRP or deletability matters. I like deleting code. This might be easier to discuss with a specific example of a function you want to put in "util" :)

Ben Sless18:06:47

The kitchen sink namespace actually hides three namespace. Either split them, or keep the functions only where they're used

jumar18:06:14

You could say clojure.core is a big “util” namespace :)

Martynas Maciulevičius19:06:46

utils_util.cljc utilities.cljc kitchen_sink_util.cljc

🔥 1
😅 1
sheluchin19:06:48

I tend to have a "util" ns that I use to collect functions that don't yet fit within some specific ns. Once a pattern forms and there are enough similarities to form logical groups, I move things around.

1
teodorlu19:06:17

> You could say clojure.core is a big “util” namespace :) > I'd rather say that it's Clojure's core troll But seriously, clojure.core is a very opinionated set of functions working on core abstractions as sequences, maps and sets. "util" in comparison would just be... Stuff

didibus19:06:29

The question to ask is, if you were looking for something, where would you go to find it? Say you don't know where it is. What's easier? 1. Go through the file-system folder hierarchy trying to figure out from the names what categories the thing you want could be in, maybe sometimes there's two categories of things where it could be in, so now you look through both, etc. This reminds me of pre-search engine times, where you just had these categorized listings, it was hard to find anything. 2. Have everything in a big file and Ctrl+f or whatever search your current editor/viewer supports, and try different keywords for what you're looking for.

p-himik19:06:24

3. Search all symbols/docstrings with your IDE. ;)

didibus19:06:09

#3 is just something you have to do because someone did #1 but you wanted #2 😝

lispyclouds20:06:05

@U02CV2P4J6S here is a blog i know of https://dave.cheney.net/2019/01/08/avoid-package-names-like-base-util-or-common Quite Go centric but i think is interesting

👍 1
lread23:06:25

Ah, I love those grumpy chronicles!

didibus00:06:18

Wow, I was really expecting it to be satire but it wasn't

didibus00:06:29

Well, I just don't agree with the premise at all. I say things are much more discoverable when they're all in one place you know to look for them. You can have order in your utils as well, just section them with comments.

didibus00:06:50

Over time, your utils will start to depend on others anyways.

lread03:06:38

It’s likely more a subjective thing, yeah? To each their own.

didibus05:06:33

In my opinion its OCD, I don't think there is a real problem, only OCD from people.

didibus05:06:13

And I mean it either way. Real problems would be dealing with circular dependency, having name clashes, etc.

didibus05:06:55

For me, I have the real problem of not finding the function I need sometimes, and having lots of small files where the function could be is a lot slower for me to find it, and forces me to navigate to a lot of places to check, or do a more complex IDE configuration where I teach my IDE where all the possible files are so it could do a multi-file search, etc. Where-as I find when there's less places it could be, its much quicker to find it.

didibus05:06:16

I also feel it re-creates the naive design that I find problematic in OO. The assumption that everything has a clean single logical category. For example, I want to do something with a file, but what if that thing is credentials for a web request? So does it go in web.clj or file.clj ?

didibus05:06:39

In OO you'll often find the pattern then of like "service" classes, that is like a class that takes other classes to do things that aren't only about one or the other, but need both. So you start getting things like: web-files.clj for example 😄

didibus05:06:20

slurp takes a file, but also a url.... Where to put slurp ? Is it file.clj or web.clj ? If you have a utils it don't matter 😛

Martynas Maciulevičius05:06:41

stuff.clj random.clj utilitarian.clj Who could've known that util could be start of the word for so many words: https://www.thefreedictionary.com/s/Util

p-himik05:06:55

Just put slurp in slurp.clj - easy.

1
Benjamin07:06:46

(defn read-json [s] (json/read-str s :key-fn keyword))

(defn
  decode-hex-v1
  [s]
  (transduce
   (comp
    (drop 2)
    (partition-all 2)
    (map #(apply str %))
    (map #(Integer/parseInt % 16))
    (map #(Character/toString %)))
   str
   (seq s)))
the 2 functions I currently have in utils.clj

Martynas Maciulevičius07:06:35

@U02CV2P4J6S I think that your decode-hex-v1 is going to be used once. And then you would be using the function that uses the wrapping one. There is no way you need to decode hex in more than five places in your code. So IMO it's extracted prematurely and it makes the code too specific for no reason (except if you would write some kind of parser and not a backend).

👍 1
didibus07:06:49

I guess io_utils.clj and say pure_utils.clj might be an okay split. Anyways, I tend to be a bit middle of the road leaning in less overall namespaces that each have more things in them. But ya, it's kind of personal preference, doubt it has much impact on any measurable metric either way.

Martynas Maciulevičius07:06:15

I have a namespace io.clj in one of my projects. When I import it I don't understand what it does because it's actually related to the database IO. So I'm not happy about that name.

didibus07:06:46

I think we're exclusively talking about utils here right? Like nothing here is domain/business/app specific. I'm not saying to put all your app logic and behavior in one giant namespace called utils. When you have things that are not app specific where to put them for the app that needs them I think is the question. So I would be confused too about io.clj having the code for my app DB layer.

Ben Sless07:06:20

There is an orthogonal domain though, which is the application / system domain. There is file io, logging, error handling, cryptography... All of those are definitely not just utils. They have their own domains

sheluchin09:06:00

Namespacing everything is just a way of organizing code into a tree. I often wonder if code could be organized into a graph at a lower level than files and what the pros/cons of that would be. Kinda reminds me of when I switched my personal notes from a folder hierarchy to a single folder with notes that link to each other and have tags. It was a pretty liberating change. Think traditional note taking systems vs. graph-based ones like logseq. IIRC the Unison language does something like this.

👍 2
Ben Sless11:06:14

Namespaces aren't really hierarchical so you can say it's already a graph

👍 1
sheluchin11:06:59

Aren't they? > Due to this loading convention, most Clojure is structured with a 1-to-1 mapping of namespaces to files, stored in hierarchical fashion that maps to the namespace structure.

teodorlu11:06:11

namespaces can definitely be mapped to a file hierarcy. Which namespaces depends on which tends to look more like a graph. In Tonsky's article linked previously, he's been able to design a clean dependency graph:

teodorlu11:06:52

He has chosen not to try to reflect the dependency graph in the file hierarchy. Everything is just grumpy.{module}. Note - he has a two layered approach. feed / telegram / auth (features) might depend on their others, or on "primitives", like time, coll, config, etc. There's no dependencies from the tom row (time, coll, config) on the bottom row.

sheluchin12:06:06

That approach of keeping the file hierarchy flat sounds very much like my note taking approach of just having one big folder for all notes, with links and metadata to create the edges between them. Still, even organizing things into files is single level hierarchy. I do wonder if that too could be eliminated. Could all the functions just live in a single big registry?

1
sheluchin12:06:03

The Unison idea sounds pretty much like that, actually. I should read more about it - maybe I read it some time ago and got the notion from there 🙂 all of the definitions are just content-addressed by the hash of their syntax tree. It all goes to one big registry and you create new definitions when you need to update something (rather than mutating text files and vars). > In Unison, changing what names are mapped to which hashes is done with structured refactoring sessions, rather than the typical approach of just mutating text files in place and then fixing a long list of misleading compile errors and failed tests. A Unison codebase is never in a broken state, even midway through a refactoring.

👍 1
Martynas Maciulevičius12:06:21

You could also do the "golang way" where they have multiple files in one directory but then it's a single namespace :rolling_on_the_floor_laughing:

sheepy 1
Ben Sless13:06:57

Let's not do that 🙂

Ben Sless13:06:28

In unison you can still have namespaces, but the definition names refer to are global if I'm not mistaken

❤️ 1
sheluchin13:06:37

Indeed, you are right. It's an interesting distinction that where the definition goes matters not, but names still have some structure. > So rename and move things around as much as you want. Don't worry about picking a perfect name the first time. Give the same definition multiple names if you want. It's all good! This sounds like more of a graph than the way we do things in Clojure by usually matching filesystem structure to namespace structure.

didibus03:06:24

Categories have been superseded by Tags in almost all other use cases I feel. I wonder what if you tagged your functions and then could just look for all functions under one or more tags. You can probably do it DIY, add some #file #io #web to the doc string then you can search for those.

❤️ 1
Ben Sless03:06:29

That's a pretty neat idea. Requires tooling but interesting implications

Ben Sless03:06:42

Do it with metadata

1
1
teodorlu05:06:58

So... With a piece of static analysis, you can create a knowledge graph straight from your source code? That's a very interesting idea.

Benjamin10:06:27

that would be so much better than files. Also 1 to many and such

sheluchin12:06:01

Yep, that's the conclusion I arrived at with my note system. It's just way easier to think about and I don't have to load my arbitrary category hierarchy into my brain every time I want to save a new thing. Add in some fuzzy search (❤️ fzf) for the tags and content and things just feel so much simpler. I guess there's some argument to be made for the value of the exercise of building category trees and thinking about where things belong, but I think the reduction in friction between idea->creation is worth the tradeoff.