Namespace inheritance a-la Elixir use/using, is this madness?

Any kind of distribution. I don’t really believe in native apps anymore, as I’ve seen so much sunk cost in the distribution dance over the years. It is very rare that I use an app and think: “I see why this is something I installed from the App Store.” Most of the time I would rather open a browser and open the web app, if any is available. So if we are thinking presentation for consumption through a mobile device, I would prefer a framework that sprinkles a little magic, allowing me to write client server apps with the PWA stuff handled for me (or at least made very easy). To that end, I actually believe Clojure is the perfect platform for this, because we can cross compile to JavaScript and web workers.

In the rare case that I need to access a device API, I would be good with using something like Capacitor.js to handle that abstraction.

I allow myself to recapture the conversation as I understand it (by arranging quotes):

Now I ask myself: does anyone have an idea which design pattern such a future Clojure web framework would follow?

3 Likes

You forgot RMDB, that to me is the best pattern. Basically, just go back to the 60s.

I think Biff is already taking some good steps, but is it still “just” a curated set of libraries. Some select bits of magic and a little inversion of control on top of that could be interesting to try out.

1 Like

Okay… so the presentation layer would pretty much be JS or be targeting JS.

How about the data access/permissions layer? How would that be controlled? What about async events? Actually… there are ready solutions for all those questions in JS land for the backend. What would clojure add besides more complexity for newbies?

Suppose I’d like to make a birthday present for @mdiin . I fork Biff with the aim to convert it from a set of libraries to the data presentation framework he would like to see. Does anyone know a better pattern for this than the inherit macro presented by @didibus?

1 Like

I don’t think you need that macro. Sprinkle a bit of mutation around e.g. the feature idea, such that the user registers a feature and the framework uses that feature registry internally to build up the derived values based on that.

1 Like

I cannot speak for what other people would like in a framework, just what I would like to see myself. Regardless, I apologise for derailing this otherwise very interesting topic with a discussion of frameworks and how to build one.

If anyone wants to take up building a framework, go for it; and make one that pleases yourself, not some imaginary user base.

Man… That was a bit of a cop out. I wasn’t looking to be rhetorical - I was actually really curious as to how you’d design a framework. I’m sorry that it seemed boring to you.

There wasn’t really a compelling argument made for existing clojure architectures, nor why people should choose clojure over something Next.js. So one looking at this discussion can come to the conclusion that Next.js is pretty good choice. It’s got a great user base, definitely not imaginary. And it’s already there, just another iteration on top of a very mature ecosystem.

It didn’t seem boring at all, it’s a very good question to which I’m afraid I don’t have a good answer at the moment. I have been thinking some about whether I want a framework or not over the last couple of weeks, and how I would go about it. The boring conclusion is that I am undecided at the moment. What I can say though is that I think HTMX for the front end is very promising, as is XTDB for the data storage and query layer. Cross compiling the routes from the back end to a service worker for offline capability is something I want to explore at some point. Most frameworks do more than I need in any one app, which is a good thing, but maybe building one is better in small bites.

I can see how it felt like I was cutting the discussion short, apologies for that. I actually think this is a very interesting topic.

2 Likes

awesome. looking forward to the fruits of your research.

In my above gist, I indeed forgot to list RMDB as an alternative pattern to the Biff+Macro pattern. The reason is that I did not really find out what the acronym RMDB stand for (related to RDBMS which Wikipedia has?). I found a 2019 YC discussion between @linpengcheng and James Reeves (aka weavejester) and did not get how the relation of RMDB to Clojure is.

What Clojure adds to the table (for newbies and everyone) are immutable data structures by default (also in the browser). I think those can make the world a better place and should be used more often than done nowadays.

If anyone wanted to play around with what some of these ideas might look like if they were added to Biff, you could copy the code from this namespace into a gist and rewrite it: platypub/sites.clj at master · jacobobryant/platypub · GitHub

(That namespace handles CRUD for the user’s websites, and it also handles previews + deployment to Netlify)

2 Likes

I got that acronym from his posts. It’s probably easier to just call it sql. I meant applications developed like this:

https://developer.oracle.com/dsl/odb-browser-apps-js-rest.html

It doesn’t really have anything to do with clojure.


@linpengcheng had a series of articles pointing out clojure can be mapped to a relational model and why it’s the best thing ever. It’s quite fascinating. His argument tended towards ‘relational is the best’ - which I found strange at the time but definitely rings much more true now.

I don’t care about static or dynamic types, nor about FP, LP, or OO. For me, they are overly complex, unreliable, and unscientific. I think they are very bad and upset me.

The production methods and business management ideas of large industries are also more mature than FP&OO. I have used them as programming ideas.

I think that RMDB is the simplest and most reliable in theory and practice, and it is the most rigorous, long-term, high-stress test in critical situations.

Before using clojure, I was a Foxpro programmer. I used clojure as a super Foxpro, and I also used it successfully in the field of WebApp and R language mixed programming. I will continue to apply this routine to the AI field in the future.

The main development goal of clojure is to write the database. The development idea is actually from the database, not the FP.

The Clojure system can be treated as a RMDB using the Lisp language, RMDB Schema (Clojure Spec) is a static type, and SQL (LISP) is a dynamic type.

Anything besides the ‘because it’s immutable’ argument? There’s lots of ways to achieve immutability - ie. generators.

I think the most common style in Clojure is the slightly more explicit. You would explicitly have to require some namespaces to bring in the common framework functions you need. And then you would have config for hooks.

Granted, I think the most Rails/Pheonix thing missing in Clojure is probably an ORM, like ActiveRecord or Ecto.

But I too am kind of curious, can we discuss framework design pattern here? What other options are there?

What pattern does Next.JS make use of for its framework?

I’m still focused on web, so the target is to run it in a web browser, but that would include wherever a web browser is commonly used, tablet, smartphone, desktop computer, tv os, etc. I would therefore exclude native Windows, Mac, Linux, Android or iOS/FireTV et all apps, even those that would just wrap a web browser and expose additional, OS specific APIs that are not W3C web standard.

I think FoxPro and maybe Visual FoxPro is probably closest to this. I’ve never used FoxPro, but know a bit about it, it’s a programming language that is also a SQL Database, like two in one. The language is thus designed as meant that you would store all state in the database that is inherently a part of FoxPro, and all features are just changes to the DB with possible IO side-effects happening out of that.

Edit: Actually, I’m not sure if its SQL based, might be its own hierarchical like DB.

In a way, this is a common style in Clojure, with say Datascript, or even how Datomic lets you put Clojure code inside the DB itself.

I’m not sure what a framework based on that style would end up looking like though, I’m also not sure how it would design itself as a framework, and not just a set of libraries one can use in the RMDB style.

Joy, fun, pleasure, adventure, and excitement :stuck_out_tongue:

Jokes aside, are there frameworks for those in JS, can you speak to their strategy to expose it a as framework?

Here is their website - https://nextjs.org - it’s pretty detailed. I’m not sure what you mean by ‘pattern’. They use more than just a single ‘pattern’ - a lot of it is directly influenced by rails as well as react.


Definitely right about Datomic and DB functions. I remember reading a comment about going back to the 60s when Datomic first came out. That’s what sparked my interest in learning about how the old heads did things.


I wrote a library called adi which stood for a datomic interface in the early days of datomic to try and simplify querying. Currently hyperfiddle is that on steroids - connecting a data model straight to the web. I’m not sure not permissions is managed but I’m sure the team has that down pretty good. You can probably call that RMDB style.

In the js, in terms of data access, there used to be a library called meteorjs that first did that before react become absolutely dominant. The most batteries included solution that I can think of right now is http://gun.eco → which is a distributed Datomic without the time travel serving straight to the web along with an encrypted permissions model.


@jacob Biff is a really nice library. how are you defining data access models? are you also generating the front end as well in platypub?

I’m asking like how do devs using it go about adding/customizing behavior for it, or is it more like a set of utils/libs that you can use in your own project?

You write jsx for frontend ui, the frame will do server side rendering to generate pages based on your routes. Data access is pluggable. Most projects these days use graphql.

I’ve been using a pattern for building our application that I think is very “framework-like”, that I also think fits in well with Clojure’s unique philosophy.

It is based on the domain representation described here.

It works like a data-driven framework, as creating an application using the framework mostly consists of specifying domain entities using plain clojure data.

All the core elements of the application is described as domain entities, and behaviour is implemented mostly using multimethods. For example, a route would be specified as

{ :dm.entity-type/name = :task-feed.media-management/image
  :modules.server.core/route-handler-type = :task-feed.media-management/image
  :modules.server.core/methods = { :get { :parameters { :query [ :map [ :src-url :string ] [ :image-args :string ] ] } } }
  :modules.server.core/route-name = :task-feed.media-management/image
  :modules.server.core/path = "/task-feed.media-management/image"}

A data migration might be specified as:

{  :dm.entity-type/name = :task-feed.content/migrate-crop-data
   :data.entity.migration/date = 2022-05-11
   :data.entity.migration/type = :task-feed.content/migrate-crop-data}

A datomic attribute that stores large html content in a separate storage system is specified as:

{  :dm.attribute/ref-typed? = true
   :form-field/data-load-type = :task-feed.content/text-content
   :field/form = { :form/type :form/html-editor, :form/label "Review short" }
   :modules.decisions.core/attr-type.save-type = :task-feed.content/text-content
   :modules.decisions.core/attr-type.type-key = :task-feed.content/text-content
   :dm.attribute/name = :reading.book/review-short
   :dm.attribute.ref-typed/type = { :dm.entity-type/name :content/Content }
   :modules.decisions.core/txdata-hooks = [ :task-feed.content/html-statistics ]
   :dm.attribute.ref-typed/many? = false
   :dm.schema/doc = ""
   :task-feed.content/content.mime-type = "text/html"}

A long running background process is specified as:

{ :dm.entity-type/name = :task-feed.pocket/pocket-articles-download
  :processing.task/type = :task-feed.pocket/pocket-articles-download
  :task-feed.processing/skip-on-dev? = true
}

Or a websocket:

{ :dm.entity-type/name = :remote-workers/worker-websocket
  :modules.server.core/route-handler-type = :modules.server.core/websocket-handler
  :modules.server.core/methods = { :get {}, :post {} }
  :modules.server.core/route-name = :remote-workers/worker-websocket
  :modules.server.core/path = "/chsk/remote-workers/worker-websocket"
  :modules.server.core/websocket? = true
  :modules.server.core/websocket-handler-type = :remote-workers/worker-websocket
}

The framework part then comes in when the application is run. It uses all of these data specifications to define the behaviour of the application.

For example, it would wire up all the route domain entities. Run all the migration entities. Set up the datomic fields. Render the edit fields for the html content. Ensure the background processes are running.

I’ve found this pattern to be very useful, as it allows separation of the components of the application from the actual running of the application.

Because the whole application definition is stored inside a datascript db, it is also easy to introspect and change. It also works very well with a repl-driven workflow, as everything can be reloaded.

2 Likes