Web programming is functional programming
Everyone who has long programmed for both worlds, that is he has developed local applications and programmed for web, will tell you that web programming is very different from classical application development. And it's not just the programming language. You can develop both local and web applications using the same Java, Python or even C++. The difference lies in the medium of the web. It defines a much different deployment and runtime environment. It implements a different service model. It imposes different application architecture. It calls for a different programmer mentality because the very philosophy of the web is miles away from the traditional programming school. And here with the web programming paradigm we have something interesting to recognize. Very obvious actually but shadowed into the background by the march of the modern powerful programming paradigms and drowned by the fanfares of new and shining technologies and tools.
"What's that?" - will you ask.
Let us see.
We'll start by looking at the lifecycle of a typical local application and compare it with that of a typical web application. I will be using the term "lifecycle" to refer to a period of application activity, its runtime, not in the sense of the project lifecycle with its different development and maintenance stages.
What is the lifecycle of a typical local application like? A user starts the application, it gets loaded into memory and now it is running. It can react to user input, load files from disk or send some data over network. It can interact with the other software or hardware components, invoke other services or react to an external invocation. It can gather data, accumulate it and process it in some way. In short, all those things a local application would do while it's up and running.
How does the lifecycle of a typical web application look like? A user clicks a link in his browser. The browser sends a request to a web server, then receives a response and shows it back to the user. Quite simple actually. But the application is not running in the browser on the user machine. It's running on the web server. And there, the application is not running continuously. The server only briefly brings the application into life to accomplish the task of processing the request and preparing a reply. It happens in a blink of an eye. The application is not running between requests. It is invoked for a short time and shut down immediately after it's done with the request. And the application is not even entirely loaded into memory, only the parts of it really necessary for the task at hand. You could say a web application has an instantaneous lifecycle. Only its runtime environment that is the web server is running continuously. There are sure workarounds like session and data serialization to help keep data between the application lifetimes and to mimic the stateful operation like certain frameworks such as ASP.NET WebForms or JSF do, but still, these are just technical tricks and actually a story for another time. We shall focus on usual web applications focused around processing requests. And those web applications enjoy a very brief runtime existence.
For a development of a local application, a number of techniques would prove useful. Object-oriented approach to structure the problem domain and help deal with its complexity. Certain design patterns for an effective and an elegant solution. Layered architecture to split code responsibilities and minimize dependencies. These are all classical best practices which have proven efficient in development of local applications.
Using them in web development is a different story entirely. We actively use object-oriented approach to define a domain model, we adopt certain design patterns and we implement the layered architecture. They improve code quality, add reusability, structure the application in conceptual modules. It all works and belongs to the everyday routine. Even so, I can't often help but notice that these things seem to be redundant, an oversolution to really small problems. At times I just look at the smart and sophisticated code and catch myself on the random thought to be willing to strip it all away and just do things straight.
What's straight? Think of a typical routine as an application is processing a request. What happens there, in major steps? The application receives the user input, validates it, transforms it into a domain-specific format and passes it to an SQL code which puts it into the database. What happens next? The code reads some other data from the database, reformats it to be user-friendly then serves back a piece of HTML. That's it. That's what happens under the hood of most web applications. No fancy objects living their lives, sending and receiving messages and intelligently interacting among themselves to achieve a higher behavior. No, just some data travelling between the user and the database, back and forth. It's data-flow one-on-one. And the code that implements that is essentially performing as functional code, regardless of the style it was conceived to bear.
This discussion touches the subject: Is functional programming relevant to web development? As one user eloquently put it:
"Functional programming matches web apps very well. The web app receives a HTTP request and produces a HTML result. This could be considered a function from requests to pages."
And I would add the following. The code which implements this function bears essentially the functional style. Instead of true objects keeping the application state in memory and cooperating to implement the application logic, we've got a database to keep the state of the application and the entire code is basically a large and complex function that arranges for the specified dataflow to reach its destinations, either the database or the user.
What lesson can we take from this? It's not always good to fanatically force the object-oriented style and the complex architecture into web applications. They would not necessary profit from this approach but could instead get overcomplicated and inefficient in terms of performance and later maintenance. One must always weigh the pros and cons of this approach on a per-project basis.
And when a programmer codes a web application and suddenly it bears the imprint of a functional programming style, he should not get immediately criticized and ridiculed. Maybe he did it on purpose. Or maybe it was the unconscious manifestation of the subtle understanding that web programming is functional by nature.
OO PHP smells hype, but need to follow the mainstream.
Thanks for the article, bookmarked on delicious!
I could feel it with pain when working in Java in bad designed web application,
and with joy when working with functional languages.
An interesting way to manage state, while still being functional is continuation based framework. There is one in Plt-scheme (now called racket). There is Seaside too, but obviously it is less functional, because it is SmallTalk.
I experienced with Haskell and Yesod and found it very interesting, but I am not yet confortable with it.
Another way of doing is the Sinatra way in Ruby (or Flask in Python). Not really functional, but a lot simpler than most framework.
While I agree with you for CRUD application, I'm still thinking for application that do have business logic.
I'm not satisfied with working with semi-structured business model. Which happen when you start from a CRUD design and have to add logic later.
I'm not satisfied with putting the logic in the database. If SQL, it is a big NO. If MongoDB or CouchDB, better, but I don't like the idea.
How would you do?
@Jean-Baptiste Potonnier: Thanks for the new term for me - "continuation-based framework". Didn't know this state-preserving concept had an official name. Anyway, from my experience with the WebForms framework (this should also fall into this classification) you definitely could and probably should build up an object-oriented architecture encompassing domain objects and business rules. These frameworks actually turn what you would call web sites into web applications. For a site-type application you could stay with functional style, but for an application-type project an object-oriented architecture might give you an edge. But again, it all depends on a project, its size and its function.
“究竟是什么?”–你会问。
我们就来看看。
我们从看看典型的本地应用程序的生命周期开始,拿它跟典型的Web应用程序的相比较。我将会一直使用“生命周期”这个词来表示应用程序的活动周期,运行时的,指的不是包括不同开发和维护阶段的项目周期。
一个典型的本地应用程序的生命周期是什么样子的?一个用户启动这个程序,程序被加载到内存里,开始运行。它能对用户的输入起反应,从磁盘上读取文件,或通过网络传输数据。它能跟其它的软件或硬件进行交互,调用其它服务,或响应一个外部调用。它可以收集数据,累计数据,以某种方式处理这些数据。简言之,这些都是一个本地应用程序启动后,运行时能做的事情。
一个典型的Web应用程序的生命周期是什么样的呢?用户在浏览器里点击一个链接。浏览器向Web服务器发送一个请求,然后会接收到响应信息,把它展示给用户。非常的简单。但应用程序并非是运行在用户机器上的浏览器中的。它运行在Web服务器中。在那里,应用并不是持续的运行。服务器只是短暂使应用苏醒来完成处理请求的任务,准备好要回复的东西。这些事情发生在眨眼之间。在两个请求之间应用并不处于运行状态。它只是被调用很短的一段时间,当完成请求服务后会立即停止运行。而且应用也不会被整个的加载到内存里,只会加载对于目前的任务真正有必要的部分。你可以认为Web应用程序只有一个瞬时的生命期。只有它的运行所在的环境,也就是Web服务器在持续的运行。事实上会有一些变通的策略,例如Session和数据序列化,来帮助在应用程序的生命期之间保持数据,来模仿有状态的操作,例如某些框架,像ASP.NET的WebForms或JSP就是这样做的,但毕竟,这都是些技术上的技巧,跟本文所讨论的问题不相干。我们关注的是普通的Web应用,关注它们处理请求的过程。这些Web应用程序都只享有一个非常短暂的运行存在状态。
对于开发本地应用程序,有很多技术被证明非常有用。面向对象的方法可以用来构造问题环境,帮助降低问题的复杂性。一些设计模式能体现出一种高效的,而且优雅的设计方案。分层的架构把代码责任分离,最小化各层的依赖性。这些全是典型的最佳方案,它们在开发本地应用程序的过程中被充分的证实过。
而在Web应用程序里对这些技术方案的使用却是另外一种不同的情形了。我们积极的使用面向对象的方法来定义我们的业务模型,我们采用各种设计模式,我们实现分层架构。这些帮助我们提高代码质量,增加复用性,把应用程序组织成概念上的各个模块。这些都很有效,是我们最常用的技术路线。但即使如此,我还是忍不住要提醒你,这些东西看起来有些多余,有大炮打蚊子的感觉。时不时我会盯着这些精巧而且深思熟虑的代码,一种无由的想法会袭上我的心头:干嘛不把这些全都剥离掉,直接做要做的事情呢?
什么是直接做?想想一个应用处理一个请求的典型处理方式。在主要步骤里发生了什么?应用程序接收用户输入,校验它,把它转换成业务领域相关的格式,把它传进一个SQL语句里,保持到数据库里。下面又发生了什么?程序从数据库里读出一些数据,格式化信息,使之能够被用户识别,以一段HTML的形式返回给用户使用。就是这样。这就是大多数Web应用程序在其幕后所做的事情。在它们的运行期里没有什么特别的对象,它们发送和接收消息,智能的在其内部交互运作来实现高层的行为。不,只是一些数据在用户和数据库之间旅行,送出去,返回来。就是一个接一个的数据流。实现这些任务的程序代码本质上就像一个函数式程序,不管它们被构造出来的风格是什么样的。
有个讨论直指这个主题: Is functional programming relevant to web development? 其中一个雄辩的用户写道:
“函数式编程跟Web应用开发非常的匹配。Web应用接收一个HTTP请求,生成一个HTML返回结果。这应当被认做是一个从请求到页面的函数式功能。”
而我要补充下面的东西。实现这些功能的代码本质上反映的就是函数式的风格。我们并没有用真正的对象把应用程序的状态保存在内存里、用它们来实现应用逻辑操作,我们使用的是数据库来保存应用程序的状态,整个的代码基本上就是一个巨大的,复杂的函数式功能编码,它来管理特定数据流的走向:数据库或用户。
从这些讨论我们能得到什么?狂热的强制使用面向对象的风格、对Web应用使用复杂的架构未必总会有好处。你不一定就能从这种架构方式中获得有价值的好处,但从性能和日后维护的角度看,它们却能使你的应用过于复杂和效能低下。我们必须针对每个项目的各自情况来掂量采用某种方式的好处和坏处。
当一个程序员编写一个Web应用程序,如果突然代码中显示出了函数式编程风格的印记时,不要马上批评和嘲笑他。也许他是特意这样做的。也许这是一种敏锐的感觉到Web编程本身就是天生的函数式编程的潜意识表现。
if a = f(x) in the functional world then for the same x, a always takes on the same value. In a world with state, a can take on any value.
Calling a web page with the same input (post for example) and getting the same output (HTML) is functional