POSTS
Rethinking Web Frameworks in Python
Listening to @pragdave talk about Exlir’s pipes he was talking about how these two styles, while fundamentally the same, have vastly different readability:
"".join(sorted(list("cat")))
Try to explain that line of code to someone who doesn’t program. You start by telling them to just skip over everything until they hit the center, that’s the starting point. Then, you work you way back out, with each new function adding one more layer of functionality.
As programmers, we’ve taught ourselves how to read that way, but it isn’t natural. Consider this pseudo code:
"cat" | list | sorted | join
This code requires that you simply explain what |
does, then it goes naturally
from one step to the next to the next and the final result should be the joined
sorted string.
Seeing that code example got me thinking about some of the discussions I’ve had with new programmers as I explain how Django works. I start explaining the view, to which I’m almost always asked “ok, how does the request know what view to execute?” I follow this up by moving over to URL route configuration. After that’s explained, I’m asked “ok, so how do requests come in and get passed through that?” And this goes on, until we’re standing on top of 20 turtles looking down at the simple Hello World we wrote.
In that vein, what would a web framework look like that started with the premise that a regular, non-programmer should be able to read it. Here’s an idea:
def application(request):
request > get("/") > do_response()
request > get("/msg") > say_hello()
So, you define an application
function that takes a request
, that request
is then run through a get
function with a route, and if that matches it would
finally pass off to a final function that does something that would generate
the response.
To that end, I’ve hacked up this simple script that uses werkzeug to do a simple dispatch. The implementation is a little odd and would need to be cleaned up to actually be useful, but I think I could be on to something. Just imagine this syntax:
request > get("/admin") > require_login > display_admin()
At this point, require_login
can return early if you’re not logged, and
display_admin
could repeat the entire application
style and be “mounted” on
top of the /admin
route and respond to request.path
that is slightly
different.
request > get("/users/") > display_user_list()
request > get("/user/<id>/") > display_user()
request > post("/user/<id>/") > edit_user()
# or...
request > route("/user/<id>/", methods=["GET", "POST"]) > handle_user()
Any thoughts?