From 6cb2ab95bc21bc226f103445fa6a4da75a22c9e3 Mon Sep 17 00:00:00 2001 From: Darren Bane Date: Wed, 3 Mar 2021 22:51:58 +0000 Subject: Checkpointing before I dive into FFI --- cbasic.lisp | 4 ++ doc/breaking_rules.md | 109 +++++++++++++++++++++++--------------------------- loot.lsp | 2 +- xdr.lsp | 1 + 4 files changed, 55 insertions(+), 61 deletions(-) diff --git a/cbasic.lisp b/cbasic.lisp index 0f2d63c..2ddc5b4 100644 --- a/cbasic.lisp +++ b/cbasic.lisp @@ -5,6 +5,10 @@ ;;; https://github.com/Henry/BuddKaminInterpreters and *maybe* ;;; https://oleksandrmanzyuk.wordpress.com/2014/06/18/from-object-algebras-to-finally-tagless-interpreters-2/ ;;; +;;; It might be worth reading the example in "Beautiful Racket" for +;;; the extensions suggested on the OCaml page, +;;; or more likely switch to COMAL. +;;; ;;; A BASIC interpreter already exists at ;;; https://gitlab.com/com-informatimago/com-informatimago/-/tree/master/small-cl-pgms/basic ;;; but it is idiomatic CL, diff --git a/doc/breaking_rules.md b/doc/breaking_rules.md index db4f28c..31a1857 100644 --- a/doc/breaking_rules.md +++ b/doc/breaking_rules.md @@ -1,15 +1,12 @@ title: Breaking the Rules: Refining Prototypes Into Products author: Darren Bane -copyright: 2020 Darren Bane, CC BY-SA +copyright: 2021 Darren Bane, CC BY-SA # Abstract Recommendations for a process to refine prototypes into production-quality code are made. -*TODO*: Q: re-cast much of this document as Architecture -Decision Records? A: N - # Introduction The conventional wisdom is that prototypes should be discarded once the lessons @@ -19,8 +16,6 @@ In the spirit of [1] I argue that improvements in development tools have invalidated this. -*TODO*: case study - # Previous Work There is a long history of recommending prototyping as a way to construct @@ -28,16 +23,17 @@ systems. I would personally recommend [2] and [3]. -*NB*: I am almost certainly re-inventing a SmallTalk wheel. However I +The SmallTalk community probably pioneered this development process. +However I argue that Lisp's combination of imperative & OO is an easier sell to industry whereas pure OO as in SmallTalk (or logic programming as in -Prolog) is still niche. +Prolog) is, perhaps undeservedly, more niche. -A closely related are is that of "specification animation", +A closely related area is that of "specification animation", quickly writing an implementation of some subset of a formal specification in for example Z or VDM. -Prolog is a common choice for this, but I choose Lisp instead. +Prolog is a common choice for this. However, as stated in the introduction, I differ in arguing that it is possible to *refine* a prototype into a product. @@ -54,9 +50,7 @@ without earning money. ## Design Decisions -The programming language chosen is a particular style of Common Lisp. -For readability my -[ISLisp-like subset of CL](bane.20.cdr15.md) should be followed where practical[5]. +The programming language chosen is ISLisp Reasons for this decision include: * Contrary to a lot of other languages, Lisp is fairly paradigm-agnostic. @@ -64,13 +58,13 @@ Reasons for this decision include: * The imperative and object-oriented paradigms are commonly taught, used in industry, and have a small "impedence mismatch" to current hardware. -* The existence of quicklisp. - Popularity is not really a reason for choosing Common Lisp over ISLisp, - but slotting into quicklisp *is*. +* The possible migration path of running under + [core-lisp](https://github.com/ruricolist/core-lisp) + and using the quicklisp libraries. Detailed implementations, libraries, etc. are as follows: -* The SBCL compiler. +* The EISL compiler. * Avoid multi-threading at this stage, event-driven should do the job. * Not sure if this is relevant for a prototype, but you could do simple multi-user @@ -83,41 +77,43 @@ it could be difficult to satisfy expectations. ### Dependencies -For the prototyping phase, -you should *really* limit yourself to the ISLisp subset. -If absolutely necessary you can choose some of the libraries mentioned in the "Productisation" section below. +Counterintuitively, I chose ISLisp partly *because* it imposes limits in the prototyping phase. +Standard UNIX libraries like curses, xdr and dbm can still be used from compiled code using the FFI. ## Coding standards Even though this is a prototype, attention should be paid to basic craftsmanship. -* Divide the system into packages, using the subset of CL that is - supported by OpenLisp. - This can start from just "section headers" with lots of semicolons. -* Write docstrings for at least each public fun, class and package. - There are good guidelines in the Elisp manual, but for now one sentence will suffice. -* Use `declare` - to check the types of parameters in public interfaces (see below). +* Divide the system conceptually into packages. + This can be as simple as "section headers" with lots of semicolons. +* Write comments for at least each public fun, class and package. + There are guidelines in the Elisp manual, but for now one sentence will suffice. +* Dynamically + check the types of parameters in public interfaces (see below). * Indent all the source code using Emacs. * Some minimal documentation, at least an overview like in [README driven development](https://tom.preston-werner.com/2010/08/23/readme-driven-development.html) and man (actually, [mdoc](https://manpages.bsd.lv/toc.html)) pages[7]. * Certain parts of a system justify greater detail for a *complete* specification. These are (newly-designed) network protocols and complex persistent data models. - For new protocols, use JSON-RPC as a base and follow the documentation style of LSP. + For new protocols, use XDR with or without RPC but generated from rpcgen .x files. Data models should be documented as commented SQL DDL. ### Run-time type-checking As stated above, -`declare` should be used for simple run-time type-checking of public functions. +`the` should be used for simple run-time type-checking of public functions. For example, the following: ```lisp (defun f (x) - (declare (fixnum x)) - (the fixnum (+ x 1))) + (the x) + (the (+ x 1))) ``` +`assure` might be better according to the standard, +but for now only `the` is used for inference +by the eisl compiler. + # Refinement to Production-Quality Software at the level of the previous section is quite usable. @@ -135,9 +131,10 @@ obviously at a maintenance cost. Ensure that the surrounding infrastructure is in place: * Configuration management. The prototype should already have been checked into git. -* Build. Write an ASDF description, and install as a local quicklisp package. +* Build. Split sections into different files, write simple Makefile. + *TODO*: copy elisp public/private convention? * Test. - Write FiveAM + Write *library/test.lsp* test cases. Extend the simple run-time type-checking to contracts where possible. * Track. Start using a defect tracking system. @@ -145,8 +142,9 @@ Ensure that the surrounding infrastructure is in place: Then, the following code & documentation improvements should be made: * Document the system more exhaustively -* Can use any of the "starred" libraries in quicklisp. -* Maybe "lparallel" to take advantage of all cores +* Can re-implement interfaces from the OpenLisp manual using UNIX libraries. +* Can port any of the `trivial-*` libraries from quicklisp. +* Maybe multi-process to take advantage of all cores Since we have a working prototype, it may make sense to write the documentation (and contracts, and tests) "bottom-up": @@ -169,7 +167,7 @@ PlantUML *should* be used where it can replace ad-hoc text. Documents should be stored under git in a "doc" subdirectory of the project. It is recommended to keep the separation between library and UI code, -e.g. for using ltk. +e.g. for using a GUI like Tk. The following can be added as sections to the README: @@ -194,52 +192,43 @@ However, some of this documentation is better in the source code: ```lisp (defun f (x) - (declare (fixnum x)) + (the x) (assert (precondition x)) (let ((res (+ x 1))) (assert (postcondition res)) - (the fixnum res))) + (the res))) ``` -`lisp-critic` can be used to perform static analysis of the codebase. -But it's not worth writing custom rules. +I'm not aware of any static analysis tool. ## Dependencies For productisation you may want to add more features. -Although the official ANSI standard is moribund, -"community standard" (i.e. starred) libraries are recommended on the -[Awesome-CL list](https://github.com/CodyReichert/awesome-cl), -or [CL portability layers](http://portability.cl/). +OpenLisp has idiomatic interfaces for several UNIX features in its manual, which could be re-implemented. +Also quicklisp (and as a second choice non-quicklisp) `trivial-*` libraries should be easy enough to port. Dependencies should be limited to these two initially. -Ltk can implement a GUI to replace the +Tk can implement a GUI to replace the prototype command-line or terminal-based UI, if it makes sense. -A second round of productisation, -which again may never actually be required, -could include: - -* Any of the `trivial-` libraries from the Awesome-CL list. The `trivial-` libraries may be *forked* and maintained locally. -* Any other `trivial-` libraries available in Quicklisp. -* Other libraries available in Quicklisp. +The order of preference is: +1. Any UNIX interface documented in the OpenLisp manual. +2. A port of any of the `trivial-` libraries from the Awesome-CL list. +3. A port of any other `trivial-` libraries available in Quicklisp. -Now it may also be worth taking on the complexity of a Web UI, -using HTMX and the platform.sh stack. -HTMX & ReST (following Fielding) seem simpler than single-page applications -(outside the very specific case of drawing on a canvas using ParenScript). +The complexity of a Web UI should be avoided in favour of simpler protocols like IRC, Gemini and maybe XMPP. ## Testing -Unit (FiveAM) tests grow in parallel with the module interface specs. +Unit tests grow in parallel with the module interface specs. Property-based testing would be nice here, but there doesn't seem to be a readily-available library. System tests grow in parallel with the requirements spec. -It's ok for system tests to use the same interfaces as the ltk code. +It's ok for system tests to use the same interfaces as the GUI code. All tests should be automated, except possibly for the UI/view layer. -Q: These scripts could be generated from a literate test plan? A: yes, probably one of the few places to use nw2md. +These scripts could be generated from a literate test plan, one of the places where it makes sense to use nw2md. As much of the testing work should be pushed "back" in the V model to contracts for the functions, following the pattern above. @@ -247,7 +236,7 @@ following the pattern above. # Conclusion A method for developing software from an incomplete understanding of the requirements is given. -It is hoped that this is more effective than most of what is currently-used. +It is hoped that this is more effective than a lot of what is currently-used. # References diff --git a/loot.lsp b/loot.lsp index 4dfb2c8..74378fc 100644 --- a/loot.lsp +++ b/loot.lsp @@ -38,4 +38,4 @@ (for ((n 0 (+ n 1))) ((> n 99)) (format (standard-output) "~A~%" (as-string (choose loot)))))) -(main) +;; (main) diff --git a/xdr.lsp b/xdr.lsp index b3cf1ad..ffae67f 100644 --- a/xdr.lsp +++ b/xdr.lsp @@ -1,4 +1,5 @@ ;;; Use (a subset of) CL and the xpc part of the "frpc" package from QuickLisp instead? +;;; No, use eisl and ../build/xdr-tests-1.0. (require "olunit") (defpackage #:xdr -- cgit 1.4.1-2-gfad0