Appaserver's Core Principles

Functional declarative programming provides both air-tight execution and algorithm leverage. However, Appaserver is written in C -- an imperative programming language. To bridge the gap, you can approach functional programming in C by creating objects (structures) and operations (functions), but omitting variables. The goal is to write C code that reads like a rosetta stone between the CPU and the human.

To develop this skill, follow Myer's * discipline which ranked the idea of module cohesion and module coupling. Module cohesion is the degree of interaction within a model. Module coupling is the degree of interaction between modules.

Cohesion

The quality of a module's function determines the level of cohesion. From worst to best, here are the levels of cohesion:

  1. Coincidental Cohesion is a module that performs two or more unrelated functions. Evidence of this occurs when a function name cannot be defined.
  2. Logical Cohesion is a module that has available a series of functions, but only one of the functions is performed. This is the result of the case statement. Myers contends that this degrades readability, resulting in having to inspect the function to determine what is going to happen.
  3. Temporal Cohesion is a module that performs a function because it's time to. One example is perform_initialization(). Another example is stage_one(), stage_two(), ...
  4. Procedural Cohesion is a module that performs multiple functions, but only loosely related. An example would be the function read_part_number_and_update_employee_record(), where the employee record has no need for the part number.
  5. Communicational Cohesion is a module that performs multiple functions, but closely related. An example would be the function read_part_number_and_update_sales_record(), where the sales record would need the part number.
  6. Informational Cohesion is a module that performs multiple functions, and each function has its own entry point. However, all of the functions work on shared variables. The C++ Class works at this level.
  7. Functional Cohesion is a module that performs exactly one function. In the case of a module having multiple functions, then each function has its own entry point and works only on local variables. The goal is to be able to reuse the function in any context. As this ideal is achieved, imperative languages will execute as robustly as declarative languages.

Coupling

Modules interact with each other via function calls, passing information back and forth. The degree of information passing sets the level of coupling. From worst to best, here are the levels of coupling:

  1. Content Coupling is when a function modifies a variable local to another function.
  2. Common Coupling is when a function modifies a global variable.
  3. Control Coupling is when a function tells another function where to go. Instead, control should be what to return.
  4. Stamp Coupling is when a function receives an entire structure as a parameter. This is how the FILE abstract datatype behaves. (ADTs are the precursor to the C++ Class.) Whereas stamp coupling saves typing, flexibility is lost.
  5. Data Coupling is when each data element is presented in the parameter listing individually. This allows for greatest flexibility and readability.

The ideal function should have only input parameters and return a single object. However, for CPU efficiency, sometimes it is necessary to return multiple objects. If so, then pass the variables' addresses to the function starting at the first parameter position.

Appaserver's developers achieve functional cohesion and data coupling by constructing ADTs. Each object is represented by a data structure defined in a header file. The header file also defines the object's operations. The source code for the operations are contained in a .c file.

The syntax for an operation is either:

Datatype *ClassName_Verb( parameter list );

Or

Datatype *ClassName[_OptionalContext]_Datatype( parameter list );

For example:

/* person.h */
/* -------- */
#ifndef PERSON_H
#define PERSON_H

#include "list.h"

typedef struct
{
	char *full_name;
	char *street_address;
	char *zip_code;
	char *birth_date;
} PERSON;

int person_age(			char *current_date,
				char *birth_date );

/* Returns heap memory */
/* ------------------- */
LIST *person_zip_code_list(	char *zip_code );

/* Returns heap memory */
/* ------------------- */
LIST *person_list(		char *where );

/* Returns heap memory */
/* ------------------- */
PERSON *person_fetch(		char *full_name,
				char *street_address );

/* Returns heap memory */
/* ------------------- */
PERSON *person_new(		char *full_name,
				char *street_address );

/* Returns program memory */
/* ---------------------- */
char *person_select(		void );

/* Returns static memory */
/* --------------------- */
char *person_primary_where(	char *full_name,
				char *street_address );

/* Returns heap memory */
/* ------------------- */
PERSON *person_parse(		char *input_buffer );

#endif

/* person.c */
/* -------- */
#include <stdio.h>
#include "person.h"
#include "date.h"
#include "piece.h"
#include "list.h"

PERSON *person_new( char *full_name, char *street_address )
{
	PERSON *person;

	if ( ! ( person = (PERSON *)calloc( 1, sizeof( PERSON ) ) ) )
	{	
		fprintf(stderr,
		"ERROR in %s/%()/%d: cannot allocate memory.\n",
			__FILE__,
			__FUNCTION__,
			__LINE__ );
		return (PERSON *)0;
	}
	person->full_name = full_name;
	person->street_address = street_address;
	return person;
}

char *person_select( void )
{
	return "full_name, street_address, zip_code, birth_date";
}

PERSON *person_parse( char *input_buffer )
{
	char full_name[ 128 ];
	char street_address[ 128 ];
	char buffer[ 128 ];
	PERSON *person;

	if ( !input_buffer || !*input_buffer ) return (PERSON *)0;

	piece( full_name, SQL_DELIMITER, input_buffer, 0 );
	piece( street_address, SQL_DELIMITER, input_buffer, 1 );

	if ( ! ( person = person_new(
				strdup( full_name ),
				strdup( street_address ) ) ) )
	{
		return (PERSON *)0;
	}

	piece( buffer, SQL_DELIMITER, input_buffer, 2 );
	person->zip_code = strdup( buffer );

	piece( buffer, SQL_DELIMITER, input_buffer, 3 );
	person->birth_date = strdup( buffer );

	return person;
}

char *person_primary_where(	char *full_name,
				char *street_address )
{
	static char where[ 1024 ];
	char destination[ 512 ];

	sprintf( where,
		 "full_name = '%s' and street_address = '%s'",
		 escape_quote(	destination,
				full_name ),
		 street_address );

	return where;
}

PERSON *person_fetch(		char *full_name,
				char *street_address )
{
	char sys_string[ 1024 ];
	char input_buffer[ 1024 ];

	sprintf( sys_string,
		 "echo \"select %s from person where '%s';\" | sql",
		 /* ---------------------- */
		 /* Returns program memory */
		 /* ---------------------- */
		 person_select(),
		 /* --------------------- */
		 /* Returns static memory */
		 /* --------------------- */
		 person_primary_where(
			full_name,
			street_address ) );

	if ( !get_line( input_buffer, sys_string, 1024 )
	||   !*input_buffer )
	{
		fprintf( stderr,
			 "ERROR in %s/%s()/%s: fetch failed for [%s/%s]\n",
			 __FILE__,
			 __FUNCTION__,
			 __LINE__,
			 full_name,
			 street_address ); 

		return (PERSON *)0;
	}

	/* Returns heap memory */
	/* ------------------- */
	return person_parse( input_buffer );
}

LIST *person_list( char *where )
{
	char sys_string[ 1024 ];
	LIST *list;
	char input_buffer[ 1024 ];
	FILE *input_pipe;
	PERSON *person;

	sprintf( sys_string,
		 "echo \"select %s from %s where %s order by %s;\"	|"
		 "sql							",
		 /* ---------------------- */
		 /* Returns program memory */
		 /* ---------------------- */
		 person_select(),
		 "person",
		 where,
		 person_select() );

	/* Returns heap memory */
	/* ------------------- */
	if ( ! ( list = list_new() ) )
	{
		fprintf( stderr,
			 "ERROR in %s/%s()/%d: list_new() failed.\n",
			 __FILE__,
			 __FUNCTION__,
			 __LINE__ );

		return (LIST *)0;
	}

	input_pipe = popen( sys_string, "r" );

	while( get_line( input_buffer, input_pipe, 1024 ) )
	{
		/* Returns heap memory */
		/* ------------------- */
		if ( ! ( person = person_parse( input_buffer ) ) )
		{
			fprintf( stderr,
				 "ERROR in %s/%s/%d: person_parse(%s) failed\n",
				 __FILE__,
				 __FUNCTION__,
				 __LINE__,
				 input_buffer );

			pclose( input_pipe );
			return (LIST *)0;
		}

		list_append( list, person );
	}

	pclose( input_pipe );
	return list;
}

LIST *person_zip_code_list( char *zip_code )
{
	char where[ 512 ];

	sprintf( where, "zip_code = '%s'", zip_code );

	/* Returns heap memory */
	/* ------------------- */
	return person_list( where );
}

int person_age(			char *current_date,
				char *birth_date )
{
	if ( !current_date || !birth_date ) return -1;

	return date_subtract_years( current_date, birth_date );
}

/* main.c */
/* ------ */
#include <stdio.h>
#include "person.h"
#include "date.h"

int main( void )
{
	PERSON *person;

	/* Returns heap memory */
	/* ------------------- */
	if ( ( person = person_fetch( "Bill Gates", "1234 Seattle Ave" ) ) )
	{
		printf( "Got age = %d\n",
			person_age(
				date_current_date(),
				person->birth_date ) );
		return 0;
	}
	else
	{
		exit( 1 );
	}
}

* Stephen R. Schach; "Software Engineering"; 1990; pp. 219-233.

* W. P. Stevens, G. J. Myers, and L. L. Constantine; "Structured Design, IBM Systems Journal, Vol. 13"; 1974; pp. 115-139.

Home | About Us | PredictiveBooks | Appaserver | Cloudacus | Sudoku | Contact |