Skip to content

Commit 1a581eb

Browse files
committed
port first IDOM article to docs
1 parent d1a4903 commit 1a581eb

9 files changed

+1051
-11
lines changed

docs/source/_static/idom-flow-diagram.svg

+383
Loading
178 KB
Loading

docs/source/_static/mvc-flow-diagram.svg

+425
Loading
62.9 KB
Loading
+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
Architectural Patterns
2+
======================
3+
4+
Over the `past 5 years <NPM-trends>`__ front-end developers seem to have concluded that
5+
programs written with a declarative_ style or framework tend to be easier to understand
6+
and maintain than those done imperatively. Put more simply, mutable state in programs
7+
can quickly lead to unsustainable complexity. This trend is largely evidenced by the
8+
`rise <Frontend-Frameworks-Popularity>`_ of Javascript frameworks like Vue_ and React_
9+
which describe the logic of computations without explicitly stating their control flow.
10+
11+
.. _React: https://reactjs.org
12+
.. _NPM-trends: https://www.npmtrends.com/react-vs-angular-vs-vue
13+
.. _Vue: https://vuejs.org
14+
.. _Declarative: https://www.youtube.com/watch?v=yGh0bjzj4IQ
15+
.. _Frontend-Frameworks-Popularity: https://gist.github.com/tkrotoff/b1caa4c3a185629299ec234d2314e190
16+
17+
.. image:: _static/npm-download-trends.png
18+
19+
So what does this have to do with Python and IDOM? Well, because browsers are the de
20+
facto "operating system of the internet", even back-end languages like Python have had
21+
to figure out clever ways to integrate with them. While standard REST_ APIs are well
22+
suited to applications built using HTML templates, modern browser users expect a higher
23+
degree of interactivity than this alone can achieve.
24+
25+
.. _REST: https://en.wikipedia.org/wiki/Representational_state_transfer
26+
27+
A variety of Python packages have since been created to help solve this problem:
28+
29+
- IPyWidgets_ - Adds interactive widgets to `Jupyter Notebooks`_
30+
- Dash_ - Allows data scientists to produces enterprise-ready analytic apps
31+
- Streamlit_ - Turns simple Python scripts into interactive dashboards
32+
- Bokeh_ - An interactive visualization library for modern web browsers
33+
34+
.. _IPyWidgets: https://github.com/jupyter-widgets/ipywidgets
35+
.. _Jupyter Notebooks: https://jupyter.org/
36+
.. _Dash: https://plotly.com/dash/
37+
.. _Streamlit: https://www.streamlit.io/
38+
.. _Bokeh: https://docs.bokeh.org/
39+
40+
However they each have drawbacks that can make them difficult to use.
41+
42+
3. **Restrictive ecosystems** - UI components developed for one framework cannot be
43+
easily ported to any of the others because their APIs are either too complex,
44+
undocumented, or are structurally inaccesible.
45+
46+
1. **Imperative paradigm** - IPyWidgets and Bokeh have not embraced the same declarative
47+
design principles pioneered by front-end developers. Streamlit and Dash on the
48+
otherhand, are declarative, but fall short of the features provided by React or Vue.
49+
50+
1. **Limited layouts** - At their initial inception, the developers of these libraries
51+
were driven by the visualization needs of data scientists so the ability to create
52+
complex UI layouts may not have been a primary engineering goal.
53+
54+
As a result, IDOM was developed to help solve these problems.
55+
56+
57+
Ecosystem Independence
58+
----------------------
59+
60+
IDOM has a flexible set of :ref:`core abstractions <Core Concepts>` that allow it to
61+
interface with its peers. At the time of writing Jupyter, Dash, and Bokeh (via Panel)
62+
are supported, while Streamlit is in the works:
63+
64+
- idom-jupyter_ (try it now with Binder_)
65+
- idom-dash_
66+
- `IDOM in Panel`_
67+
68+
.. _Panel: https://panel.holoviz.org/Comparisons.html#comparing-panel-and-bokeh
69+
.. _idom-jupyter: https://github.com/idom-team/idom-jupyter
70+
.. _Binder: https://mybinder.org/v2/gh/idom-team/idom-jupyter/main?filepath=notebooks%2Fintroduction.ipynb
71+
.. _idom-dash: https://github.com/idom-team/idom-dash
72+
.. _IDOM in Panel: https://panel.holoviz.org/reference/panes/IDOM.html#panes-gallery-idom
73+
74+
By providing well defined interfaces and straighforward protocols, IDOM makes it easy to
75+
swap out any part of the stack with an alternate implementation if you want to. For
76+
example, if you need a different web server for your application, IDOM already has
77+
several options to choose from or, use as blueprints to create your own:
78+
79+
- :ref:`Sanic <Sanic Servers>`
80+
- :ref:`FastAPI <FastAPI Servers>`
81+
- :ref:`Tornado <Tornado Servers>`
82+
- :ref:`Flask <Flask Servers>`
83+
84+
You can even target your usage of IDOM in your production-grade applications with IDOM's
85+
Javascript `React client library <idom-client-react>`_. Just install it in your
86+
front-end app and connect to a back-end websocket that's serving up IDOM models. This
87+
documentation acts as a prime example for this targeted usage - most of the page is
88+
static HTML, but embedded in it are :ref:`interactive examples <examples>` that feature
89+
live views being served from a web socket:
90+
91+
.. _idom-client-react: https://github.com/idom-team/idom/tree/main/src/idom/client/packages/idom-client-react
92+
93+
.. image:: _static/live-examples-in-docs.gif
94+
95+
96+
Declarative Components
97+
----------------------
98+
99+
IDOM, by adopting the :ref:`Hook <Life Cycle Hooks>` design pattern from React_,
100+
inherits many of its aesthetic and functional characteristics. For those unfamiliar with
101+
hooks, user interfaces are composed of basic HTML elements that are constructed and
102+
returned by special functions called "components". Then, through the magic of hooks,
103+
those component functions can be made to have state. Consider the component below which
104+
displays a basic representation of an AND-gate:
105+
106+
.. example:: simple_and_gate
107+
108+
Note that the code never explicitely describes how to evolve the frontend view when
109+
events occur. Instead, it declares that, given a particular state, this is how the view
110+
should look. It's then IDOM's responsibility to figure out how to bring that declaration
111+
into being. This behavior of defining outcomes without stating the means by which to
112+
achieve them is what makes components in IDOM and React "declarative". For comparison, a
113+
hypothetical, and a more imperative approach to defining the same interface might look
114+
similar to the following:
115+
116+
.. code-block::
117+
118+
layout = Layout()
119+
120+
121+
def make_and_gate():
122+
state = {"input_1": False, "input_2": False}
123+
output_text = html.pre()
124+
update_output_text(output_text, state)
125+
126+
def toggle_input(index):
127+
state[f"input_{index}"] = not state[f"input_{index}"]
128+
update_output_text(output_text, state)
129+
130+
return html.div(
131+
html.input({"type": "checkbox", "onClick": lambda event: toggle_input(1)}),
132+
html.input({"type": "checkbox", "onClick": lambda event: toggle_input(2)}),
133+
output_text,
134+
)
135+
136+
137+
def update_output_text(text, state):
138+
text.update(
139+
children="{input_1} AND {input_2} = {output}".format(
140+
input_1=state["input_1"],
141+
input_2=state["input_2"],
142+
output=state["input_1"] and state["input_2"],
143+
)
144+
)
145+
146+
147+
layout.add_element(make_and_gate())
148+
layout.run()
149+
150+
In this imperative incarnation there are several disadvantages:
151+
152+
1. **Refactoring is difficult** - Functions are much more specialized to their
153+
particular usages in ``make_and_gate`` and thus cannot be easily generalized. By
154+
comparison, ``use_toggle`` from the declarative implementation could be applicable to
155+
any scenario where boolean indicators are toggled on and off.
156+
157+
2. **No clear static relations** - There is no one section of code through which to
158+
discern the basic structure and behaviors of the view. This issue is exemplified by
159+
the fact that we must call ``update_output_text`` from two different locations. Once
160+
in the body of ``make_and_gate`` and again in the body of the callback
161+
``toggle_input``. This means that, to understand what the ``output_text`` might
162+
contain, we must also understand all the business logic that surrounds it.
163+
164+
3. **Referential links cause complexity** - To evolve the view, various callbacks must
165+
hold references to all the elements that they will update. At the outset this makes
166+
writing programs difficult since elements must be passed up and down the call stack
167+
wherever they are needed. Considered further though, it also means that a function
168+
layers down in the call stack can accidentally or intentionally impact the behavior
169+
of ostensibly unrelated parts of the program.
170+
171+
172+
Communication Scheme
173+
--------------------
174+
175+
To communicate between its back-end Python server and Javascript client, IDOM uses
176+
something called a Virtual Document Object Model (:ref:`VDOM <VDOM Mimetype>`) to
177+
construct a representation of the view. The VDOM is constructed on the Python side by
178+
components. Then, as it evolves, IDOM's layout computes VDOM-diffs and wires them to its
179+
Javascript client where it is ultimately displayed:
180+
181+
.. image:: _static/idom-flow-diagram.svg
182+
183+
By contrast, IDOM's peers take an approach that aligns fairly closely with the
184+
Model-View-Controller_ design pattern - the controller lives server-side (though not
185+
always), the model is what's synchronized between the server and client, and the view is
186+
run client-side in Javascript. To draw it out might look something like this:
187+
188+
.. image:: _static/mvc-flow-diagram.svg
189+
190+
.. _Model-View-Controller: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
191+
192+
193+
Javascript Integration
194+
----------------------
195+
196+
If you're thinking critically about IDOM's use of a virtual DOM, you may have thought...
197+
198+
Isn't wiring a virtual representation of the view to the client, even if its diffed,
199+
expensive?
200+
201+
And yes, while the performance of IDOM is sufficient for most use cases, there are
202+
inevitably scenarios where this could be an issue. Thankfully though, just like its
203+
peers, IDOM makes it possible to seemlesly integrate :ref:`Custom Javascript Components`.
204+
They can be custom built for your use case, or you can just leverage the existing
205+
Javascript ecosystem without any extra work:
206+
207+
.. example:: material_ui_slider

docs/source/core-concepts.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Core Concepts
22
=============
33

4-
This section covers core features of IDOM that are used in making
5-
interactive interfaces.
4+
This section covers core features of IDOM that are used in making interactive
5+
interfaces.
66

77

88
Pure Components
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import idom
2+
3+
4+
@idom.component
5+
def AndGate():
6+
input_1, toggle_1 = use_toggle()
7+
input_2, toggle_2 = use_toggle()
8+
return idom.html.div(
9+
idom.html.input({"type": "checkbox", "onClick": lambda event: toggle_1()}),
10+
idom.html.input({"type": "checkbox", "onClick": lambda event: toggle_2()}),
11+
idom.html.pre(f"{input_1} AND {input_2} = {input_1 and input_2}"),
12+
)
13+
14+
15+
def use_toggle():
16+
state, set_state = idom.hooks.use_state(False)
17+
18+
def toggle_state():
19+
set_state(lambda old_state: not old_state)
20+
21+
return state, toggle_state
22+
23+
24+
idom.run(AndGate)

docs/source/getting-started.rst

+8-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Since it's likely a lot to take in at once, we'll break it down piece by piece:
1515
:linenos:
1616

1717
The ``idom.component`` decorator creates a :ref:`Component <Stateful Components>`
18-
constructor which is "rendered" by the function below it. To create a Component instance
18+
constructor whose "renderer" is the function below it. To create a Component instance
1919
we call ``Slideshow()`` with the same arguments as its render function. The render
2020
function of a Component returns a data structure that depicts a user interface, or in
2121
more technical terms a Document Object Model (DOM). We call this structural
@@ -35,22 +35,22 @@ Calling a Hook inside a Component's render function (one decorated by ``idom.com
3535
adds some local state to it. IDOM will preserve the state added by Hooks between calls
3636
to the Component's render function.
3737

38-
The ``use_state`` hook returns two values - the *current* state value and a function
38+
The ``use_state`` hook returns two values - the **current** state value and a function
3939
that let's you update that value. In the case of ``Slideshow`` the value of the
4040
``use_state`` hook determines which image is shown to the user, while its update
4141
function allow us to change it. The one required argument of ``use_state`` is the
42-
*initial* state value.
42+
**initial** state value.
4343

4444
.. literalinclude:: /examples/slideshow.py
4545
:lineno-start: 8
4646
:lines: 8,9
4747
:linenos:
4848

49-
The coroutine above will get added as an event handler to the resulting view. When it
50-
responds to an event it will use the update function returned by the ``use_state`` Hook
51-
to change which image is shown to the user. Calling the update function will schedule
52-
the Component to be re-rendered. That is, the Component's render function will be called
53-
again, and its new result will be displayed.
49+
The function above will get added as an event handler to the resulting view. When it
50+
responds to an event it will use ``set_state`` (the update function returned by the
51+
``use_state`` Hook) to change which image is shown to the user. Calling the update
52+
function will schedule the Component to be re-rendered. That is, the Component's render
53+
function will be called again, and its new result will be displayed.
5454

5555
.. note::
5656

docs/source/index.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ IDOM
1313

1414
.. toctree::
1515
:hidden:
16-
:caption: Advanced Usage
16+
:caption: Advanced Topics
1717

1818
core-concepts
1919
javascript-components
20+
architectural-patterns
2021
specifications
2122

2223
.. toctree::

0 commit comments

Comments
 (0)