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.
I agree with @seancorfield 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.
The mechanism for comparing the same interface namespaces to have the same API looks kinda funky to me
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).
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).
I think you're right @seancorfield 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).
@seancorfield about #2 you can do that using https://github.com/clj-commons/rewrite-clj which preserves whitespace.
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.
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.
Polylith seems to be one of those ideas that is very hard to really appreciate until you actually start using it…
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
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.
And then you get the poly cli to compare your different namespaces to have the same defs between them. Feels clunky and not DRY
I’m not sure what you mean about “compare your different namespaces”?
if you have 2 components of the same interface
the poly CLI checks whether or not their interface.clj match
otherwise poly check would fail
Ah, you mean like the local vs remote component example in the docs?
That's how they ensure that your components that implement the same interface don't differ in their API
I would just think it's easier if you have another interfaces/ directory where you have only one interface.clj per interface basically
i think that's how the leiningen version of polylith did it, if i'm not mistaken
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.
You'd still have that though
You'll have a single user.interface namespace that's all
Where it delegates to the core implementation
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
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).
Would love to hear @tengstrand's opinion about this
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.
Slowly making progress on the refactoring (of mostly low-level stuff so far):
At least it will be easier to refactor these components into smaller components if needed.
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.
I'm also finding a lot of functions that are unused after a decade of code maintenance :thinking_face:
@tengstrand 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 @silencioseu’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)
Wow, exciting!
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).
And one of our old subprojects has gone away now, refactored into four separate components (cough!).
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?!
Nice!
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.
And macros still need to be in .clj
files in ClojureScript, right?
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)
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).
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)”
(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)
I guess time will tell how people will want to work with Polylith.