Fork me on GitHub
#polylith
<
2021-06-01
>
Nikolas Pafitis14:06:06

Hello guys, great project, big fan. I'd like to ask a couple of questions: • Is there going to be a feature/flag where you select the extension of your generated components/base? For example clj, cljs, cljc, cljr, clje or whatever next implementation of Clojure can be? • Is there going to be a feature/flag where poly cli adds the newly generated brick's path to dev's or another brick's paths? • Why does the interface namespace is generated with each component? Couldn't it be done by having an interfaces/ dir where you have only one interface namespace per, well interface, which that interface would then delegate "polymorphically" to the core namespace, for example.

Nikolas Pafitis14:06:37

The mechanism for comparing the same interface namespaces to have the same API looks kinda funky to me

seancorfield15:06:49

I’ve asked about #3 and it seemed that there might, in the future, be an option to have the component name replace the fixed name interface so you would have com.acme.thing.thing instead of com.acme.thing.interface. You can already have com.acme.thing.interface.subthing (which I’ve been using for protocols: com.acme.thing.interface.protocols). Personally, I’d prefer just com.acme.thing as the interface name but I understand that complicates the directory/file structure for the tool (currently com/acme/thing is always a directory).

seancorfield15:06:43

Regarding #2, it does seem that updating :extra-paths in the :dev and :test aliases in the workspace deps.edn could be automated (but I would only want that if it preserved whitespace and comments when editing my deps.edn file).

Nikolas Pafitis15:06:07

I think you're right @U04V70XH6 about #3, although it might complicate component "polymorphism", so that's why the devs thought another level of indirection with an interface (I have to give it some thought i don't have a real opinion).

Nikolas Pafitis15:06:38

@U04V70XH6 about #2 you can do that using https://github.com/clj-commons/rewrite-clj which preserves whitespace.

seancorfield15:06:48

And, yes, #1 would be a nice setting to have in workspace.edn — but be aware that the Polylith team say they are specifically targeting (server-side) Clojure and not ClojureScript because it’s on the server side where the architecture makes sense: a frontend code base is likely to be structured very differently, and you also don’t have quite the same REPL-based workflow in non-Clojure contexts. I only do server-side Clojure so I don’t really have a good grasp on cljs workflows, for example, but the couple of times I’ve dabbled with cljs in the last year or too it has seemed frustratingly different/behind the Clojure workflow.

seancorfield15:06:41

The interface naming seems very “odd” when you first encounter it — that was probably the thing I liked least about Polylith’s approach when I first read about it — but I’ve quickly gotten used to it now. And the substitutability (and very clean separation of “interface” and implementation) are worth the “cost” of the slightly odd naming/structure.

seancorfield15:06:09

Polylith seems to be one of those ideas that is very hard to really appreciate until you actually start using it…

Nikolas Pafitis15:06:08

About #1 i made a dummy fullstack project using clj/cljs and it works pretty fine actually, makes alot of sense for SPAs as well. It's just that currently i go and rename my files from clj to cljc/cljs and add clojurescript dependency where necessary. Also other implementations of Clojure are viable for server-side, even cljs. I might want to try polylith witth clojerl one day let's say, i think it would be nice to just have an option which just changes the extension of your file

Nikolas Pafitis15:06:27

I don't mind having an "interface" namespace as much, even though it's not classic idiomatic clojure. It just feels weird to me having to maintain multiple namespaces of the same "interface" where they have to look identical. Don't get me wrong i love the project.

Nikolas Pafitis15:06:12

And then you get the poly cli to compare your different namespaces to have the same defs between them. Feels clunky and not DRY

seancorfield15:06:45

I’m not sure what you mean about “compare your different namespaces”?

Nikolas Pafitis15:06:02

if you have 2 components of the same interface

Nikolas Pafitis15:06:17

the poly CLI checks whether or not their interface.clj match

Nikolas Pafitis15:06:29

otherwise poly check would fail

seancorfield15:06:43

Ah, you mean like the local vs remote component example in the docs?

Nikolas Pafitis15:06:11

That's how they ensure that your components that implement the same interface don't differ in their API

Nikolas Pafitis15:06:41

I would just think it's easier if you have another interfaces/ directory where you have only one interface.clj per interface basically

Nikolas Pafitis15:06:02

i think that's how the leiningen version of polylith did it, if i'm not mistaken

seancorfield15:06:49

Right. I wonder how common that really is in the wild? What I like about the interface/implementation separation is that you can have a very nice, clean interface.clj with everything in alphabetical order, and then your implementation in whatever order Clojure requires. So I find there’s value in the separation even aside from the substitutability.

Nikolas Pafitis15:06:17

You'd still have that though

Nikolas Pafitis15:06:50

You'll have a single user.interface namespace that's all

Nikolas Pafitis15:06:59

Where it delegates to the core implementation

Nikolas Pafitis15:06:50

It will delegate to a different implementation of user.core namespace exactly how it's done now depending on which implementation you include in your path

seancorfield15:06:11

Having each component be self-contained has value though. Having the interfaces be “elsewhere” would complicate things a lot, especially with the limitations of relative paths in deps.edn (which is part of why recent changes have been made on the issue-66 branch, which is what we’re using).

Nikolas Pafitis15:06:19

Would love to hear @U1G0HH87L's opinion about this

3
tengstrand18:06:31

Here we go! 1. I haven’t tried it, but I think it will already work in the issue-66 branch to have the monorepo at the root and then have one or several directories per language, e.g. clj (or clojure or something else), cljs, cljc` where some of them are Polylith workspaces (e.g. the clj directory) where the poly tool will work. Then if we put cljc code in the cljc directory, it could be used from the Clojure code via e.g. :extra-paths “../../../cljc/path-to-your-cljc-file. My guess is that you want to have only one Clojure directory, and if so, then we could also let all our cljc code live there, and let the cljs code refer that code. 2. It takes maybe 1 minute to create and add a component manually, which is something you don’t do very often. But the main reson we let you do this manually is that we don’t want to introduce magic, and the rule that we have followed is that the poly tool should only create new files or read existing files, it never updates files (but that can change if we have good reasons). 3. You could have a separate place where you specify your interfaces, and that’s how we implemented it in the [lein-polylith](https://github.com/tengstrand/lein-polylith) tool. The problem with that was that you still have to write your functions (and everything that is included in the interface) in your components. By eliminating these empty interfaces (or [workspace interfaces](https://github.com/tengstrand/lein-polylith#workspace-interface) as we called them) we got rid of that extra moment of maintaining these interfaces. Another thing to notice here is that in most cases, maybe 95% of the time in average, we only have one component per interface (one “copy” of each interface). An example is the Polylith codebase itself that has 25 components and 25 interfaces at the moment. To implement what you suggest, you probably need to use code generation or something similar, at least if you want to keep the code navigation that you get for free today. I will comment on the suggested namespace change another time, got busy.

seancorfield21:06:13

@U1G0HH87L I think it would be a lot more intuitive (and a lot more in keeping with how multi-language Clojure/Script projects work elsewhere), to have components/<thing>/src/clj and components/<thing>/src/cljs as “source paths” because the expectations is likely that a single component might contain some Clojure and some ClojureScript in such projects (as well as .cljc files). But I think @U0105D1EL4B’s original Q was just about telling poly what file extension to use when creating new files? Since create c only creates two source files, I don’t think it’s a big deal, but having ext:cljs as an option to create c might be enough for this? (and presumably for any other commands that create source files)

seancorfield21:06:35

I kind of agree about #2 — adding two lines to deps.edn (`src` and test paths) isn’t much work and you’d have to modify that file any time you add test deps (or dev deps that would be part of a project, rather than part of a brick). And it would be a big tradeoff for the amount of work the poly tool would need to do to correctly edit that file without changing anything else about it — yes, there’s rewrite-cljs but it’s non-trivial to use. And I kinda rail against tools that modify my files anyway (I like tools that tell me what needs changing).

tengstrand21:06:25

I’m not sure why you would want to mix Clojure and ClojureScript code in the same component when the code can’t be shared between them?!

seancorfield21:06:02

The most likely scenario I can think of is a reusable component mostly written in .cljc files but with some platform-specific implementation parts in .clj and .cljs files.

seancorfield21:06:20

And macros still need to be in .clj files in ClojureScript, right?

seancorfield21:06:37

So, yes, mixing .clj, .cljs, and .cljc files in a component is a realistic situation. I guess the question is whether folks would feel any need to separate them into folders by language, given that components are generally going to be small? (probably not)

tengstrand21:06:50

Polylith doesn’t stop people from putting whatever code into a component they want. The poly tool only parses .clj and .cljc code at the moment. So it depends on what people expect the tool to do with the non-Clojure code (if anything).

seancorfield21:06:20

Right. I think this comes down to the simplified point I made above: “Since `create c` only creates two source files, I don’t think it’s a big deal, but having `ext:cljs` as an option to `create c` might be enough for this? (and presumably for any other commands that create source files)”

3
seancorfield21:06:25

(and aside from the actual file extension, the whole issue of a mixed-language workspace is already “permitted” and given the flat structure of Polylith there’s no need for the per-language directory structures that some people adopt in full-stack repos today)

tengstrand22:06:59

I guess time will tell how people will want to work with Polylith.

Nikolas Pafitis14:06:45

I agree with @U04V70XH6 a simple ext:cljs would be more than enough for most needs. And you also don't really need seperate clj/cljs dirs as the current structure perfectly allows mixed language projects.

seancorfield20:06:52

Slowly making progress on the refactoring (of mostly low-level stuff so far):

clojure-spin 6
👍 3
polylith 9
tengstrand21:06:51

Wow, exciting!

seancorfield21:06:21

And one of our old subprojects has gone away now, refactored into four separate components (cough!).

🎉 3
seancorfield04:06:40

Another subproject refactored into a component. Tomorrow will be some of another subproject. The downsides of having subprojects that become “grab bags” of random functionality! 😞

tengstrand10:06:41

At least it will be easier to refactor these components into smaller components if needed.

seancorfield14:06:31

Yeah, what's taking the time here is properly splitting things up into appropriate components instead of just moving subprojects into components! I don't want to persist that mistake.

seancorfield14:06:08

I'm also finding a lot of functions that are unused after a decade of code maintenance :thinking_face:

👍 3