question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Cell.js Long Term Plan and Design Decisions

See original GitHub issue

What is Cell, really?

Here’s what Cell is, in one sentence:

Cell is a function that constructs a decentralized structure of autonomous HTML nodes from a blueprint.

But this post is about the future of Cell, so just for the sake of discussion let me switch out the “HTML nodes” part with a more generic term “objects”:

Cell is a function that constructs a decentralized structure of autonomous objects from a blueprint.

Let’s step back and think about this for a moment. An HTML node is nothing more than a special purpose object that has its own distinct properties. And all Cell does is create autonomous versions of these objects using the Web API.

If we dive further into this line of thinking, theoretically we should be able to do the same for any kind of object. So… does this mean we can apply Cell.js on any other types of objects today?

Not today. But I think it should be possible. We just need to successfully decouple the DOM part out from the core. And when that becomes possible, we should be able to build not just frontend web apps, but all kinds of other applications using the cell approach, such as:

  • microservices
  • artificial neural networks
  • decentralized applications
  • blockchain
  • autonomous applications that evolve on their own without human intervention, even after we die
  • etc.

The point is, Cell potentially can evolve into a general purpose “autonomous engine” that can be injected into any object natively, just like what it does today with the DOM.

How does this idea translate to the current implementation?

The reason I mention all this is because every aspect of the library implementation and design was influenced by this potential factor. Let me explain.

Attribute Naming Conventions

1. $type

I went through multiple iterations for this attribute before deciding on $type. Here are some I tried: $tag, $node, $element, $el, $e, $c.

  • The reason $tag didn’t work was because it’s too specific to the HTML context. We can’t, for example, use it to describe a custom class for building microservices.
  • $node and $element are less explicit than $tag, but they still have a relatively concrete meaning.
  • Shorter names like $el, $e and $c may be shorter, but they fail because they’re not readable. Ideally anyone with no prior experience with Cell should be able to take a quick glance at a Cell app code and kind of understand what’s going on.

In the end I chose $type because it is an abstract term. It can be applied to pretty much any context, and intuitive enough that anyone would get it with no prior knowledge.

The only case I’m not fully satisfied is when using it to describe <input type='text'> because it looks like {$type: "input", type: "text"} (two "type"s), but really this is the only case and I think the benefit outweighs the cost.

2. $components

I went through multiple options before deciding on $components. The strongest contender for this attribute was $children.

The reason I dropped $children was because the term implies that there’s a top-down tree-like hierarchy.

This is true for the DOM tree, but it may not be the case for other types of potential usage. For example a different implementation in a parallel universe may have a “previous/next” relationship instead of a “parent/child” relationship. It could also be a many-to-many relationship instead of a tree structure.

$components on the other hand is more abstract. The term can also imply an ownership relationship but is not as explicit. It’s used to describe an encapsulated environment and doesn’t necessarily imply a parent/child relationship.

This is why I picked $components.

3. $cell

This attribute is critical for keeping cells completely autonomous.

The “$cell” attribute is used to indicate that its parent object should turn into an HTML node.

3.1. Why not merge $cell and $type?

Some people raise the question: “why not just merge $cell and $type to describe the type of a node? That way we get rid of one attribute and it’s cleaner”.

Well it’s a bit complicated. The $cell attribute is used as an explicit marker for cell construction. For example let’s say we have

Li = {
  $type: "li",
  $text: "Item"
}
OrderedList = {
  $cell: true,
  $type: "ol",
  $components: [Li, Li, Li]
}

Because the OrderedList is the only object that contains the $cell attribute, that’s the only one that turns into an HTML node.

The end result looks like this:

<ol>
  <li>Item</li>
  <li>Item</li>
  <li>Item</li>
</ol>

Granted, Li does eventually get referenced inside $components so they do get incorporated into the DOM as one of OrderedList’s components, but that’s exactly the point.

By NOT including the $cell attribute, we are able to define all these “cell candidates” such as Li without having them automatically added to the DOM. The $cell attribute lets us dynamically introduce cells when we need them.

To make the point clear, let’s see what happens if we did have the $cell attribute on the Li variable, like this:

Li = {
  $cell: true,
  $type: "li",
  $text: "Item"
}
OrderedList = {
  $cell: true,
  $type: "ol",
  $components: [Li, Li, Li]
}

The end result would have been:

<li>Item</li>
<ol>
  <li>Item</li>
  <li>Item</li>
  <li>Item</li>
</ol>

which is not what we are expecting.

This is why we need an explicit marker ($cell) to indicate whether an object needs to be turned into an HTML element or not. In many cases we don’t want them to be immediately added but dynamically added later, sort of like how we use HTML templates.

If you have a better idea to get around all these issues simultaneously, please feel free to share. It’s always best to have less attributes than more.

3.2. Why not just use a factory method to explicitly create cells, and get rid of the $cell attribute?

This feature used to exist but I took it out right before I released the library publicly. It used to look something like this:

Cell.$build({
  $type: "div",
  $text: "hi"
})

It’s not that I think having a factory method is a bad idea. In fact we can add it if we really need to.

The real reason I took it out was because I believe the main way to interact with the library should be the declarative approach. If something doesn’t work with the declarative approach, we should make it work instead of trying to solve that problem with a factory method, because that’s not solving the problem fundamentally.

Even if we find out that it’s impossible to have a 100% coverage with the declarative approach, this doesn’t mean we should completely switch to the factory method approach and get rid of the $cell attribute.

The primary approach to using Cell should always be the declarative approach (and maybe the factory method can be used in edge cases where this is really not possible)

Let me explain why with an example web app:

<html>
<script src="https://www.celljs.org/cell.js"></script>
<script src="https://walk.com/gene.js"></script>
<script src="https://eat.com/gene.js"></script>
<script src="https://socialize.com/gene.js"></script>
<script src="https://appearance.com/gene.js"></script>
<script src="https://inheritance.com/gene.js"></script>
<script src="https://think.com/gene.js"></script>
</html>

Each script is loaded from a different source and each has its own distinct feature. One may describe a certain behavior, one may describe the data, one may function as a catalyst that composes two other objects to create a new object with a completely different behavior. The point is, none of them explicitly depend on another.

And when they all come together, they somehow emerge into an app.

The important part is that these scripts have no knowldege of one another.

Why is this important? Let’s say we’re trying to build an emergent app that shapeshifts itself depending on context. Here’s how it could work (Please use your imagination because these types of apps don’t exist yet, which is what Cell was built for):

  1. The app checks the geolocation of a user
  2. Depending on the location, it queries some 3rd party service to see what kind of apps are available
  3. Depending on the response it receives, it queries another 3rd party service which responds with an adequate app template.
  4. The app also takes into account locally stored data that is private to only the user.
  5. All of these are composed together to construct a completely unique app. For example, if you’re in your neighborhood the app may turn into a restaurant review app, but if you’re away from home traveling, then it could turn into a travel app.
  6. The key to all this is that each of these modules is completely independent and has no knowledge of each other. These data/application are automatically resolved, delivered, and constructed from multiple different parties, in realtime.

In cases like this, you can’t use a factory method because factory method only works when you know what your app will look like when you’re writing your app. In this particular case you don’t know until everything is resolved and constructed.

This type of app may sound like a sci-fi story but it is indeed possible with Cell, and is what Cell was designed for. And to be able to write apps like this, we need a completely decentralized way of writing autonomous modules.

This is why I think we should be almost paranoid about getting the declarative approach to work, which is why the factory method is not the solution.

p.s. I’m not saying Cell is only for these weird cases. It’s a perfectly fine framework for building all the normal web apps we use every day. My point is we can do that AND much more if this framework was completely declarative.

Variables

Does Cell modify the DOM directly?

One of the strengths of Cell is the syntax is its intuitive syntax. To set a state using some framework API method like setState(), you simply assign it directly to the this context, like this:

button = {
  $type: "button",
  $text: "Click me",
  onclick: function(e){
    this.$text = "Clicked";
  }
}

A lot of people look at this usage and immediately think they’re directly storing everything on the HTML element. But rest assured, that’s not how it works. Cell keeps an internal proxy object that makes it look as though you’re directly accessing the DOM but in fact you’re only touching that proxy object.

Why is the “no prefix” option used for DOM attributes?

Cell has 3 classes of variables:

  • $ prefix variables: special purpose reserved variables
  • _ prefix variables: user defined local variables
  • no prefix variables: maps 1:1 to DOM attributes natively

Let me explain how this system came to be:

The reason Cell plays so well with any other web technology is because it does not mess with DOM attributes.

Many things can go wrong if we don’t do it this way because we have no control over these custom objects (in this case HTML elements) Cell attaches to. For example, if we decide to use the $ prefix to refer to the dom attributes, we can never be sure what this will result in in the future when the web standard changes or browser implementations change.

This can especially become a very serious issue when we consider the fact that someday Cell may evolve into a general purpose engine for constructing any kind of autonomous object. We need to minimize the complexity as much as possible if we want to achieve flexibility and extensibility.

Anyway, that’s why the native DOM attributes are treated as first citizens and no prefix is used to refer to them. Native attributes should be left untouched to reduce complexity.

What’s up with all the cell biology reference in the code?

If you don’t understand what this question means or if you haven’t read through the source code, you can ignore this. This is for those of you who have read through the code and thinking about tweaking the code (and hopefully contributing!)

First thing you’ll notice when you look at the source code is there’s a lot of biology stuff going on (genotype, phenotype, gene, nucleus, etc.) instead of the typical programming keywords like model, view, controller, state, props, etc. we are familiar with.

Here’s why:

  1. Language shapes how we think
  2. If you are reading the source, I presume you are reading it because you want to understand it better.
  3. In that case, thinking about it from a biological perspective will help you understand it much better.
  4. Cell’s long term goal is to evolve into a more general purpose engine, so it doesn’t make sense to use concepts constrained to frontend web development.

But here’s the real reason: I named them this way out of 100% practical reasons. During development I got stuck multiple times. I found it very difficult to structure and visualize the codebase because cell’s architecture doesn’t really fit into any existing “web app framework” paradigm. For example, during the early days I used terms like “Model”, “View”, and “Controller”, and these terms got me almost halfway there, but after that they did more harm than good because a lot of the critical concepts in Cell were hard to categorize using web development concepts, and these web development way of thinking limited my way of thinking.

One day I decided to try to organize the code purely based on cell analogy, because after all, that’s where I got the inspiration from. And once I did that everything became crystal clear and I could finish the library.

Lastly, here’s a question: If you could pick from:

  1. building a web app framework that acts like a cell
  2. or a cell that can act like a web app framework

What would you build?

I think it’s much more powerful to try to build a simulated cell that can act like a web app framework (and more), because it can be applied to many different contexts. And to achieve this I think we need to think like a biologist instead of a computer scientist.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:16
  • Comments:18 (9 by maintainers)

github_iconTop GitHub Comments

19reactions
NxtChgcommented, Jun 21, 2017

This is so idiotic, I don’t even know where to begin!

You’ve created a tiny library, which core feature is adding some code to DOM elements to match JS variables.

And you’re gonna sacrifice real, practical usability for the sake of some delusional grandiose plans of world domination?!! With what?!! Your 500 lines of code?!!

What kind of mega-value will you add to all those things (AI networks, ffs?! Really?), which don’t even have any DOM elements?

What is your grand idea? “Using JSON to describe The Structure of The Universe”?..

Jesus… This is so delusional, you should go and do an MRI scan to see if you have some sort of a tumor that ate your brain…

Somebody, for the love of god, clone those 500 lines and do it right!

4reactions
leeoniyacommented, Jun 22, 2017

while i disagree with @NxtChg’s demeanor, i do generally agree with his feedback.

Generalization, categorization and structural uniformity has been a goal of all engineers ever. “Everything is a file” [1] (Unix), “Everything is an object” (OOP), “Everything is a function/immutable” (FP), “Everything is a tree”, etc…

While many classes of problems can be represented via these structures, it’s not always the case. A graph is not a tree, only i/o stuff (sockets, files, endpoints) can logically be grouped into “files”, a value is not a function, hardware is not immutable. Trees will have very sub-optimal performance in many use-cases that can technically look like trees if you squint hard enough.

An overly general declarative solution for all problems will be a pretty bad case of shitty “wtf” usability, shitty performance, or both due to the underlying truth that the abstractions mask. You will not be able to predict the needs of all domains into the future. At some point, the abstractions will simply cease to make sense, or become so deep as to be incomprehensibly translatable for the target audience.

Declarative solutions typically only work well when restricted to a given domain. You need imperative building blocks to really be generic. I think a good demo of this is e.g. S (FP) [2] and the derived surplus (UI) [3].

It’s amazing how awesome a domain-specific API can be and how unusable an overly-generic one can be.

https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well

[1] https://en.wikipedia.org/wiki/Everything_is_a_file [2] https://github.com/adamhaile/S [3] https://github.com/adamhaile/surplus

Read more comments on GitHub >

github_iconTop Results From Across the Web

Design Decisions for the First Embedded Analytics Open ...
For the last couple of years, we've been working on Cube.js, an analytics framework built specifically for customization and embedding.
Read more >
Design Strategy – A Guide to Strategic Thinking | Toptal
Strategic planning involves gathering data and deciding on a path that the business or project will take to achieve its goals. Strategic thinking...
Read more >
Four Architecture Choices for Application Development ... - IBM
Which application architecture model is best for you in the cloud era? Increasingly, businesses are going through a digital transformation ...
Read more >
Will JavaScript ever fix its bad design decisions? Why? - Quora
The thing is, I wouldn't say that JavaScript has many particularly bad design decisions. It ALLOWS weird and potentially dangerous things, but it's...
Read more >
BROADENING THE RANGE OF DESIGNS AND METHODS ...
taken into account when choosing designs or deciding on evaluation ... long term, embedded in a changing context with extended causal chains or...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found