I haven't dug into Clojure enough, but I'm curious why you think a conventional MVC-style framework wouldn't be a good fit (not that the article is making this argument).
I haven't dug into Clojure enough, but I'm curious why you think a conventional MVC-style framework wouldn't be a good fit (not that the article is making this argument).
In most languages, related data is generally represented by some manner of closed record, struct or object. In Clojure, this data is represented by open maps instead.
This distinction may seem minor or even irrelevant, but in my view it's the key difference between Clojure and other languages, and the reason why the MVC pattern has never quite fit comfortably in Clojure.
In a language that uses closed records, complex data tends to align itself in terms of fixed hierarchies. Think of how the models in Rails are typically laid out. You have a few dozen models, with a handful of fields each, arranged in some sort of static tree or graph.
In Clojure, data doesn't tend to align itself in the same way. Data doesn't need to be artificially grouped if there's no benefit in doing so; its structure can be much more amorphous.
There's a similar problem at the "view" end. REST tends to assume a hierarchy of resources, when that may not be the best way for the client to get the data it wants, or for the server to represent the data.
I'm curious, just because Clojure offers that flexibility to a developer when writing the logic in their app, why does this flexibility need to "interfere" (for lack of a better word) with the design of a web framework?
Is Clojure unique here? seems this is the norm for Javascript applications too, that are done mostly with a FP style, instead of open clojure maps you have open JS objects. Most JS SDKs and libraries I have work with is just passing JS objects between them and your code.
So you’re mostly rewarded by keeping object «types» seperate
For example, you don't have to store functions in objects in Javascript, but the language design encourages it. This is problematic because we can't pull data out of an object without considering the functions that rely on it.
Another issue is that keys are not unique. Two objects might contain an "id" key, for example, that identifies them in different ways. This is problematic if you want to arbitrarily merge objects together.
Idiomatic Javascript might look like this:
user.orders.total_cost()
But if we want to program in the same way as Clojure, we'd perhaps be writing something more like: Orders.total_cost(user["user/orders"]);
Not impossible, but certainly more inconvenient.The more you force rigid categorisations and behaviour on data, the harder it is to manipulate and organise in different ways. In-memory data is almost always ordered with a hierarchical structure, because that's what closed records gravitate toward. But if we take a look at databases, its rare to find any organised along strict hierarchical lines.
Don't exactly understand this part, if one of your objects has its unique identifier as "userID" and the other as "invoiceID" you can merge them without conflict or what I'm missing? "id" is indeed use widely in blog examples/tutorials and even books but it's not the norm for enterprise data.
I view Elixir as two languages in one: an actor language, and a functional programming language. Sometimes you gotta use the actor language (processes) to model behavior, but it's far better to leave that to the FP aspects.
The problem with using processes is there are a lot of concerns that only they can handle.
To be fair, I'm only a hobbyist Elixir dev. But there's lots of misinformation about how easy Elixir makes certain things. In my experience, it's not easy to build a process tree that factors in concurrency and synchronization, much less fault tolerance on top of that. Nevermind performance and gc isolation. Adding modelling on top of that just makes the job way harder.
The only real thing processes have in common with objects is they both hold state. Messages are kinda similar to methods if you squint hard enough, but there's more differences than similarities. Everything else listed above has nothing in common with any given object in your average object oriented programming language.
I kind of implied all of that, but I was specifically referring to the concept of modules and all its feature-sets, which together, in my opinion, does lead to a more OO like style of programming, especially structs, processes, behaviors and use, those things I feel are really similar to classes and inheritance. And there's also the dotted syntax for both map and struct access.
While yes, it's different than your common OO, in practice, you end up using these things similarly.
In Pheonix, simply put, almost all extension points relies on the __using__ macro, __MODULE__ compile time macro and use keyword. So it ends up very similar to an OOP framework, you basically extend classes, but you do so by using `use` instead of using `extend`, and a macro that copies over code and dynamically set the module name, instead of having an inherited lookup table.
How it's achieved is very different, but as a user of Pheonix, the ergonomics end up very similar.
Look at this:
defmodule TodoWeb.ItemsController do
use TodoWeb, :controller
alias Todo.Items
def index(conn, _params) do
items = Items.list_items()
render(conn, "index.html", items: items)
end
end
Yes, this isn't exactly a class, but like: package TodoWeb;
import Todo.Items;
class ItemsController extends TodoWebController
public index(conn, params) {
var items = Items.listItems();
this.render(conn, "index.html", items);
}
}
How similar is the user experience?And TodoWebController similary uses Phoenix.Controller, etc. It gives you a very similar feel to an inheritance tree, and pluging-in behavior in the framework by class extension.
Namespaces in Clojure really don't have this feel to them at all, now maybe you could shenaningan something similar with some similar macro convention over namespaces in Clojure, but it definitely isn't encouraged by the language or community the way it is in Elixir.
So in general, I feel modules in Elixir have a much closer feel to them to classes/objects in OO languages, and you end up using them similarly, where-as that's not the case for Clojure namespaces.