After studying category theory and functional programming, I decided to put these concepts into practice by developing a simple web application. This project was inspired by the example in Chapter 11 of 'Programming in Haskell' by Graham Hutton. My goal was to take these theoretical programming ideas and see how they work in creating a real-world web application.
The key concepts of functional programming are:
Prioritize creating pure functions as much as possible. These are functions without side effects, making them simpler to test
Use composition of pure functions to construct more intricate functions
Use Functors, Applicatives, and Monads in scenarios that require pure functions, such as error handling or managing missing values, and when working with collections like trees and lists.
I wrote this application in Java, utilizing the Spring Boot framework. This decision was influenced by my previous work in developing enterprise applications with this technology stack. My objective was to investigate how functional programming (FP) concepts and techniques could be effectively applied in a real-world Java application. While Haskell, TypeScript, or React JS were potential alternatives, my goal was to create a project that was more than just a theoretical example, but rather one that had practical relevance to my professional work.
I also wanted to explore a few ideas beyond the typical development toolkit. I've been in search of a rendering framework that is both strongly typed and easily composable. The current state of the industry for Spring Boot is to use Thymeleaf as the template framework. I used Thymeleaf, and several other template libraries, in several projects but I see several drawbacks:
Separate HTML file: Initially easy to write but becomes challenging to maintain
No layout template model: Requires importing a separate library, adding more XML elements and attributes
Not strongly typed: Becomes error-prone over time.
This led me to explore Java-based DSLs (Domain-Specific Languages). Initially, I tried htmlflow, but eventually, I decided on J2HTML. I was looking for a commercial, predefined Admin Layout that simplifies development. This approach ensures that essential widgets, like forms, buttons, and alerts, are readily available, without the need for a deep dive into CSS/JS. Lately, I've been using Core UI (coreui.io), which is built on the Bootstrap framework but offers additional widgets ideal for medium-complexity enterprise applications. For my demos, the free version of Core UI suffices, and for full-scale applications, I use the pro version. Its extension of widgets is useful for developing enterprise applications of moderate complexity.
Design of the Game Engine
The Game Engine closely follows the [PiH] design. The Java classes correspond to Haskell types:
Player an enum in Java, a type in Haskell. A bit more involved in Java because there is a conversion from an enum to char which is implicit in Haskell. Another interesting point discovered is that Java enums implement Comparable with the natural order based on the order in the source code, which is similar to Haskell
Grid is a class in Java and a type in Haskell. Most operations convert nicely from Haskell to Java. I used streams instead whenever possible
All public operations in the Grid class are pure-function, with the convention that the object itself (this) is the first parameter in all operations.
GameNode, GameTree are classes in Java. There are no Haskell types for it but probably should for clarity. The public methods of these classes are pure functions.
Design of the User Interface
The user interface relies on Spring MVC. Controllers process requests, interact with services like our Game Engine and prepare views.
I used high-level views based on page templates, each with specific panels. For example, the T3PageTemplate renders the grid in a two-panels layout. This layout includes navigation links, user info, a right column card, and associated JS scripts.
A page in the application is a dynamic ensemble of various elements – widgets, text, images, and links – all coming together to present a rich and complex dataset. Consider, for instance, the page that renders the grid. It's not just about displaying the grid; it also incorporates the brand, user information (whether the user is authenticated or not), and a user-dependent menu. You might imagine a class, GridPageView, with a render method that takes in all the necessary data for display. This method would then orchestrate various sub-views like HeaderView, SideBarView, and GridCardView, each with their components like UserView, GridView, MenuView. However, a challenge arises with this structure: it necessitates defining methods with extensive arguments. Moreover, if a nested widget requires a new data piece, it calls for modifying all the methods upstream, making the process cumbersome and less efficient.
Conclusions
I started this project aiming to apply category theory and functional programming concepts to developing web applications in Java. Did we meet these goals?
Application Completion and Functionality: I successfully built a working Tic-Tac-Toe application. It closely follows the logic of its Haskell counterpart, achieving our primary objective
Use of Functional Programming Concepts: The outcome here is somewhat mixed. I incorporated a significant number of pure functions, primarily as Java static methods or non-static methods not tied to shared objects. This strategy resulted in classes having many small static functions, an approach not typical in Java where non-static methods are often preferred for their override-ability in subclasses. This method minimized the use of subclassing, aligning with functional programming principles
Class Design and Methodology: Ultimately, our classes consisted either of static methods or instance methods when implementing an interface. This distinction highlights the functional programming approach within the Java framework
Constraints and Differences Relative to Haskell: There are features in Haskell, like currying – the conversion of a function with multiple parameters into a series of single-argument functions – that Java does not directly support.
For my next project, I plan to investigate further into several areas highlighted by this exercise:
I'm going to examine the concept of 'View' as a Functor. In this context, for any business object X, a View<X> would be its representation in the user interface. I'm interested in seeing how compositions in the business domain translate into UI compositions
Another direction: considering the preference for aggregation over inheritance, I'll explore how the design pattern “Builder” is used to create intricate UI components.