Architecting and Documenting Software Solutions


Yves Cloutier
 

Patrick,

What approach(es) do you use when planning and documenting solutions to be implemented with stanza?

For example, typically one starts with business requirements, then functional requirements, then software specification document etc...

Often we see class diagrams and UML diagrams, which usually go hand in hand with OOP.

Since lbstanza is not OOP in the classical sense, and not purely functional in the classical sense, which approach do you use for planning out and modelling a solution?

In my day job I'm a business/system analyst and most of my work is documenting business requirement and other technical documentation that developers would use for implementation. 

So I'm curious to know what your approach to documentation is for solutions you develop in stanza ;)

Regards


 

Hi Yves,

Thanks for the awesome question! Achieving a flexible software architecture is something that we expressly designed Stanza for, and something I am personally very passionate about.

We strongly advocate adopting a layered software architecture when coding in Stanza. (Actually, we advocate a layered architecture when coding in any language, but Stanza was designed to make it easy.) In a layered architecture, developers partition their software into separate layers of abstraction. Each layer presents its own API, and is responsible for abstracting away some detail of the machine. The lowest layer will be concerned with how to shuffle bytes around in memory, and how to interface with external peripherals. The highest layer will expose almost no details of the machine, and instead be concerned only with business requirements. Each layer can access the API of the immediate layers below it, but may not "skip a layer".

Within an individual layer, developers will think about their software architecture as comparised of a set of 'things', along with defined relationships between those 'things'.

As an example, consider the architecture of a spreadsheet program (like Microsoft Excel).

A low-level layer may concern itself with details of file management and input devices. The 'things' that this layer manages are mice, keyboard, files, directories, etc. These would be modeled as types in Stanza:

  deftype Mouse
  deftype Keyboard
  deftype File
  deftype Directory

The relationships between these things are represented as functions that operate upon these types:

  defn get-x-position (m:Mouse) -> Int
  defn get-y-position (m:Mouse) -> Int
  defn make-directory (s:String) -> Directory
  defn contains? (d:Directory) -> File
  ...

Another layer may concern itself with the GUI event loop, and the details of how to draw widgets to the screen:

  deftype Request
  deftype RepaintWindowRequested <: Request
  deftype ResizeWindowRequested <: Request
  deftype ButtonClickRequested <: Request

  deftype Shape
  deftype Circle <: Shape
  deftype Rectangle <: Shape
  deftype Line <: Shape

  deftype Window
  deftype Button

  defn draw (w:Window, s:Shape) -> False
  defn handle-request (w:Window, r:Request) -> False
  defn intersects? (s1:Shape, s2:Shape) -> True|False
  ...

And the highest abstraction layer will concern itself with objects that the user directory deals with:

  deftype Project
  deftype Formula
  deftype SpreadsheetCell

  deftype Cell
  deftype NumberCell <: Cell
  deftype NameCell <: Cell
 
  defn evaluate (f:Formula) -> Int
  defn store (c:Cell, f:Formula) -> False
  defn clear (c:Cell) -> False
  defn make-project (name:String) -> Project
  defn save (p:Project) -> False
  ...

More importantly, notice what is not specified: the location of the functions. In a traditional OOP language, the location of each function is a big part of the program architecture. Should handle-request be a method in the Window class? Or should it be a method in the Request class? Or should it be a stand-alone function? Or should it be a method of a third RequestHandler class? Our belief is that these questions are ultimately not important, and serve only to detract from real architectural decisions: what are the things in the program, and what are the possible interactions between them.

An architectural document consists of an exhaustive listing of all the types in the program, and a description of all the interactions between those types. I used such a document to describe the implementation requirements of a just-in-time compiler in the graduate course that I taught, and it worked wonderfully. By the end of the course, we had thirty students successfully implement a just-in-time compiler from scratch, and ... they all behaved the same. So that we actually prepared a suite of benchmark programs and had the students compete against each other on the speed of their compiler.

Regards, Patrick


Yves Cloutier
 

Patrick, thanks so much for taking the time to explain a little bit about this part of the engineering process.  I found this very insightful.

What you say here is exactly the reason why I asked the question:

More importantly, notice what is not specified: the location of the functions. In a traditional OOP language, the location of each function is a big part of the program architecture. Should handle-request be a method in the Window class? Or should it be a method in the Request class? Or should it be a stand-alone function? Or should it be a method of a third RequestHandler class? Our belief is that these questions are ultimately not important, and serve only to detract from real architectural decisions: what are the things in the program, and what are the possible interactions between them.
I'm following some books on compiler construction. Some of them are quite old (pre-OOP), some of them do things with OOP, some of them do things using FP.  And seeing that stanza allows you to do things in any of these paradigms (though the OO being a bit different than most others) I found myself getting stuck deciding which approach to use, and how to structure things, Should I use a type here, should I use a bunch of variables to do this and stick them in a module, or should I put them together in a struct? 

Though not building a compiler, I'm working on a typesetting alternative to *TeX. The aim is to provide a higher-level of abstraction over low-level Groff/Troff.  Well I guess all phases of a compiler would need to be in there. A scanner, a parser and the code generator being the part that generates Groff/Troff source code as the target.

stanza having functions as first-class naturally led me to wonder about parser combinators I've seen and used in FP, the possibility of following a similar approach in Stanza, or whether there was any real advantage over recursive-descent and more traditional [Scan input] -> (Token List) -> [Parse Token List] -> (AST) -> [Generate Target Code].

As well looking a bit more at the macro documentation, I imagined a program taking a language spec or grammar and *generated* a corresponding scanner/parser at compile-time using macros.  But I'm really no there yet in my abilities. But I can imagine it!

Anyhow, thanks again for your insight.  The JIT compiler project seems very interesting.  Any chance we will see it as a course on Coursera or other MOOC any time in the future ? ;)

Regards