chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Chicken-users] Modeling a state machine


From: Matt Gushee
Subject: [Chicken-users] Modeling a state machine
Date: Mon, 3 Sep 2012 16:01:51 -0600

Hello, all--

I have a question that is not Chicken-specific, or even scheme-specific, but since it relates to a project I'm developing in Chicken Scheme, I hope you will humor me.

I have been working on a framework for interactive command-line programs. I have an initial version that works for my needs; I was thinking of releasing my work as an egg, but I now believe that the library is rather poorly designed. So I'm going to use it in its present form, but if I am going to release it to the public, I need to fundamentally redesign it. The basic problem is that there is a lot of special-case code to deal with some complex data types that are important for me, and possibly to others. Thus I think the code will be a nightmare to maintain; furthermore, it occurs to me that what this library provides is (or can be seen as) a form of finite state machine. Yet that model is not clearly expressed in the current code--I'd say there is no coherent architectural principle at all.

To be more specific, here's a very simple example of what you can do with the current version:

    (use text-menu)

    (interact
      (steps
         (first: (make-step 'first-name))
         (second: (make-step 'last-name))
         (third: (make-step 'phone required: #f))
         (fourth: (make-step 'email required: #f)))
      looping: #t)

    (get-all-data)

This is working code. If you run it, it presents prompts like "first-name: ", and record the input ... and since 'looping' is true, when you finish one the series once, you have a choice to start over or quit ... and afterwards a call to (get-all-data) retrieves all the data that was collected. Now, that 'make-step' function can be customized in various ways via keyword arguments ... you can specify a customized prompt, a default value, validators, actions to perform based on the input, and a few other things. So you can imagine already that there is a lot of internal complexity. There are also several specialized 'make-step' functions for handling specialized data types, and a couple of those are quite complex indeed.

The general procedure for handling each prompt-and-input cycle looks like this (in pseudocode):

    show prompt
    input = get-input
    valid = validate input
    if not valid -> handle error
    perform action on input
    record input
    choose next step
 
But: there are these important exceptions I mentioned, which necessitate a process like this:

     show prompt
     raw input = get-input
-> input = preprocess input
     valid = validate input
     if not valid -> handle error
     perform action on input
     record input
     choose next step

Now, I see two big trouble spots here:

  1) the input and output of the preprocessing step necessitates modifications to some of the other steps,
      because although much of the data is just strings, there are other types that are more usable as lists
      or more complex structured types; and
  2) the invalid input handling is special case code; I imagine in a properly architected state machine it
      would just be another prompt-and-input cycle. 

I'll go into a bit more detail about the preprocessing problem below. But first, let me say it seems to me there are 3 basic approaches to this situation:

  a) provide specialized procedures for specialized data types; not too different from the current design;
  b) allow preprocessing functions to return arbitrary data types, and trust that application developers are
      smart enough to provide appropriate functions to work together with those preprocessors; or
  c) standardize all data -- i.e. everything is handled internally as a string, or as a list, etc.

Here are the two main examples of specialized data that are messing me up:

DATES
---------
I want to be able to handle dates in a flexible and user-friendly manner. Specifically:
* The user should be able to enter dates in any of several common formats: "2012-09-03," "9/3/2012", "Sept. 3, 2012," etc. (actually only ISO format and US-style MM/dd/YY[YY] styles are currently supported.
* It is possible to enter abbreviated dates, e.g. MM/dd/YY, or MM-dd, and have the date default to the current year, month, etc.
* You can specify a range of allowed years (default: 1-3000 AD)
* The preprocessor extracts the year, month, and day using irregex submatches, and returns a list of integers: '(Y M D).
* The validator checks the '(Y M D) using integer arithmetic, so that, for example, years outside the allowed range are rejected, dates > 31, or 30, or 29, or 28 are rejected, depending on the month.

ENUMS
---------
These are basically predefined (optionally extensible) lists of items for the user to choose from. So a [simplified] enum might look like:

'((copy . "Copy a file") (move . "Move a file") (delete . "Delete a file"))

This gives rise to a display something like this:

1) Copy a file
2) Move a file
3) Delete a file

And if the user enters "2", the preprocessor extracts from the enum the symbol for Item #2, which is 'move ... we don't want to use "2" internally, because it might not always have the same thing ... and then the validator really has nothing to do ... or the validator should be combined with the preprocessor? Either way, the data is not a string like most other data, which complicates matters. I suppose the enum *could* simply be list of strings, but I feel it is preferably to use the symbol internally, so we can do something like:

    (case file-op
       ((copy) (copy-action))
       ((move) (move-action))
       ((delete) (delete-action))
       (else #OOPS!))

....

So, hopefully that's enough info for you to understand the design challenge. As I said, I'm not sure if I will be releasing some form of this library or not, but even if I don't, I'd like to have a better understanding of how to design things like this. Thanks in advance for your thoughts!

--
Matt Gushee

reply via email to

[Prev in Thread] Current Thread [Next in Thread]