tag:blogger.com,1999:blog-102441902024-02-28T10:38:10.020-05:00ToolchainMike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.comBlogger26125tag:blogger.com,1999:blog-10244190.post-6058152290969201072010-08-21T16:40:00.004-04:002010-08-22T10:45:22.088-04:00Python, NumPy and matplotlib - Visualize Matrix Rotations<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div>I am using Python, NumPy and matplotlib to experiment with, and visualize 3-D graphics techniques. The first step was figuring out how to generate polar plots with matplotlib and to determine the domain and range of spherical to Cartesian coordinate conversions.<br />
<br />
The next step is to rotate a point in space using matrices, and then to explore the concept of gimbal lock. I will probably tackle gimbal lock in a future posting. Before delving into 3-D rotations, I needed to figure out how to extend my plotting knowledge.<br />
<br />
I cleaned up the code from the previous posting, moving the spherical to Cartesian coordinates conversion code to a separate module - <b>sphcoords</b>. sphcoords also includes a function that takes spherical coordinates and returns a 1x3 NumPy matrix that represents a point in space.<br />
<code><br />
import numpy as np<br />
from numpy import matrix<br />
<br />
def x_cart(incl_sph_coords, azim_sph_coords):<br />
return np.sin(incl_sph_coords) * np.cos(azim_sph_coords)<br />
<br />
def y_cart(incl_sph_coords, azim_sph_coords):<br />
return np.sin(incl_sph_coords) * np.sin(azim_sph_coords)<br />
<br />
def z_cart(incl_sph_coords):<br />
return np.cos(incl_sph_coords)<br />
<br />
def sph2cart(incl_sph_coord, azim_sph_coord):<br />
return np.matrix([<br />
x_cart(incl_sph_coord, azim_sph_coord),<br />
y_cart(incl_sph_coord, azim_sph_coord),<br />
z_cart(incl_sph_coord)])</code><br />
<br />
Below is an example of <b>sphcoords</b> function, <b>sph2cart</b> which generates a 1x3 matrix containing Cartesian coordinates. In this case, spherical coordinates of 30 degrees inclination and 180 degrees azimuth are converted.<br />
<br />
<code>poi = sphcoords.sph2cart(np.pi/6, -np.pi+(10*azi_incr))</code><br />
<br />
By using matplotlib's subplot capability along with spherical to Cartesian coordinate conversions, I was able to depict a 3-D spherical model of space. A single red "point of interest" will be rotated using rotational matrices.<br />
<br />
The initial position of the red point is arbitrary. The red point will be initially rotated about the z axis. The rotated position of the red point will remain along the point's original circle of inclination. This is what I was hoping to illustrate with the cyan-colored annotations.<br />
<br />
Before going further, let me apologize for my inability to generate circles. The elliptical, squashed figures in my plots will be circular after I figure out how to set fixed aspect ratios.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img border="0" height="425" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbM9v0rUI/AAAAAAAAAGY/_yPJ9Ge9vEY/s640/img1a.png" style="margin-left: auto; margin-right: auto;" width="640" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The Initial Position of the Red Point of Interest</td></tr>
</tbody></table><div class="separator" style="clear: both; text-align: center;"><br />
</div>Here are the rotational matrices that were implemented in module <b>rotmat </b>using the NumPy Matrix. <br />
<br />
<pre><span style="font-size: x-small;"><span style="font-size: small;">import numpy as np
from numpy import matrix
def x_rot(rads):
return np.matrix([
[1,0,0],
[0, np.cos(rads), -np.sin(rads)],
[0, np.sin(rads), np.cos(rads)]])
def y_rot(rads):
return np.matrix([
[np.cos(rads), 0, np.sin(rads)],
[0, 1, 0],
[-np.sin(rads), 0, np.cos(rads)]])
def z_rot(rads):
return np.matrix([
[np.cos(rads), -np.sin(rads), 0],
[np.sin(rads), np.cos(rads), 0],
[0,0,1]]) </span></span></pre><pre><span style="font-size: x-small;"><span style="font-size: small;"> </span></span></pre><pre><span style="font-size: x-small;"><span style="font-size: small;"> </span></span></pre>The 1x3 matrix containing the point of interest's (POI) Cartesian coordinates is rotated about the z axis thusly:<br />
<br />
<code>poi = poi*rotmat.z_rot(-5*azi_incr)</code><br />
<br />
The plot below shows the POI rotated 90 degrees about the z axis. The cyan annotations show the range of the next rotation which is about the x axis. The POI will be rotated 30 degrees about the x axis. The most interesting update will be on the upper right plot that will show the the POI jumping from the yellow 30 degrees circle to the black circle at 60 degrees.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbbq2xQbI/AAAAAAAAAGg/QOc0ZaGZENI/s1600/img2a.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="425" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbbq2xQbI/AAAAAAAAAGg/QOc0ZaGZENI/s640/img2a.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After Rotating the POI about the Z axis.</td><td class="tr-caption" style="text-align: center;"><br />
</td><td class="tr-caption" style="text-align: center;"><br />
</td><td class="tr-caption" style="text-align: center;"><br />
</td></tr>
</tbody></table><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbbq2xQbI/AAAAAAAAAGg/QOc0ZaGZENI/s1600/img2a.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a><a href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbbq2xQbI/AAAAAAAAAGg/QOc0ZaGZENI/s1600/img2a.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a><a href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbbq2xQbI/AAAAAAAAAGg/QOc0ZaGZENI/s1600/img2a.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a><a href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/THAbbq2xQbI/AAAAAAAAAGg/QOc0ZaGZENI/s1600/img2a.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/_7BAP_W8Mqyo/THAbtBndikI/AAAAAAAAAGo/2C1Ytvb8EP0/s1600/img3a.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="422" src="http://4.bp.blogspot.com/_7BAP_W8Mqyo/THAbtBndikI/AAAAAAAAAGo/2C1Ytvb8EP0/s640/img3a.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After Rotating the POI about the X axis.</td></tr>
</tbody></table><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><code> </code></tbody></table><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/_7BAP_W8Mqyo/THAb3Bf8ZFI/AAAAAAAAAGw/IxdYKJUf5iw/s1600/img4.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="425" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/THAb3Bf8ZFI/AAAAAAAAAGw/IxdYKJUf5iw/s640/img4.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The final position of the POI after rotating about each Euler Axis.</td></tr>
<code></code></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: left;"><tbody><code> </code></tbody></table><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: left;"><tbody><code> </code></tbody></table><code><br />
import numpy as np<br />
import matplotlib.pylab as plt<br />
from numpy import matrix<br />
import rotmat<br />
import sphcoords<br />
<br />
<br />
def generate_plot(inclin, azi, poi):<br />
# plot x-y plane<br />
plt.subplot(221)<br />
plt.plot(<br />
sphcoords.x_cart(inclin[0],azi[0]), <br />
sphcoords.y_cart(inclin[0],azi[0]), "g-o",<br />
sphcoords.x_cart(inclin[1],azi[1]),<br />
sphcoords.y_cart(inclin[1],azi[1]), "y-o",<br />
sphcoords.x_cart(inclin[2],azi[2]),<br />
sphcoords.y_cart(inclin[2],azi[2]), "y-o",<br />
sphcoords.x_cart(inclin[3],azi[3]),<br />
sphcoords.y_cart(inclin[3],azi[3]), "k-o",<br />
sphcoords.x_cart(inclin[4],azi[4]),<br />
sphcoords.y_cart(inclin[4],azi[4]), "k-o",<br />
sphcoords.x_cart(inclin[5],azi[5]),<br />
sphcoords.y_cart(inclin[5],azi[5]), "b-o",<br />
sphcoords.x_cart(inclin[6],azi[6]),<br />
sphcoords.y_cart(inclin[6],azi[6]), "b-o",<br />
poi[0,0], poi[0,1], "r-o")<br />
plt.grid(True)<br />
plt.ylabel('Y Axis')<br />
plt.xlabel('X Axis')<br />
<br />
# plot y-z plane<br />
plt.subplot(222)<br />
plt.plot(<br />
sphcoords.z_cart(inclin[0]), <br />
sphcoords.y_cart(inclin[0],azi[0]), "g-o",<br />
sphcoords.z_cart(inclin[1]),<br />
sphcoords.y_cart(inclin[1],azi[1]), "y-o",<br />
sphcoords.z_cart(inclin[2]),<br />
sphcoords.y_cart(inclin[2],azi[2]), "y-o",<br />
sphcoords.z_cart(inclin[3]),<br />
sphcoords.y_cart(inclin[3],azi[3]), "k-o",<br />
sphcoords.z_cart(inclin[4]),<br />
sphcoords.y_cart(inclin[4],azi[4]), "k-o",<br />
sphcoords.z_cart(inclin[5]),<br />
sphcoords.y_cart(inclin[5],azi[6]), "b-o",<br />
sphcoords.z_cart(inclin[6]),<br />
sphcoords.y_cart(inclin[6],azi[6]), "b-o",<br />
poi[0,2], poi[0,1], "r-o")<br />
plt.xlabel('Z Axis')<br />
plt.grid(True)<br />
<br />
#plot x-z plane<br />
plt.subplot(223)<br />
plt.plot(<br />
sphcoords.x_cart(inclin[0],azi[0]),<br />
sphcoords.z_cart(inclin[0]),"g-o", <br />
sphcoords.x_cart(inclin[1],azi[1]),<br />
sphcoords.z_cart(inclin[1]),"y-o",<br />
sphcoords.x_cart(inclin[2],azi[2]),<br />
sphcoords.z_cart(inclin[2]),"y-o",<br />
sphcoords.x_cart(inclin[3],azi[3]),<br />
sphcoords.z_cart(inclin[3]),"k-o",<br />
sphcoords.x_cart(inclin[4],azi[4]),<br />
sphcoords.z_cart(inclin[4]),"k-o",<br />
sphcoords.x_cart(inclin[5],azi[5]),<br />
sphcoords.z_cart(inclin[5]),"b-o",<br />
sphcoords.x_cart(inclin[6],azi[6]),<br />
sphcoords.z_cart(inclin[6]),"b-o",<br />
poi[0,0], poi[0,2], "r-o")<br />
plt.ylabel('Z Axis')<br />
plt.grid(True)<br />
plt.show()<br />
<br />
if __name__ == '__main__':<br />
inclin = []<br />
azi = []<br />
<br />
inclin_points = range(0,20)<br />
azi_incr = (2 * np.pi)/float(20)<br />
azi_points = np.arange(-np.pi, np.pi, azi_incr) <br />
<br />
#90 deg inclination<br />
inclin.append([np.pi/2 for i in inclin_points])<br />
#30,150 deg inclinations<br />
inclin.append([np.pi/6 for i in inclin_points])<br />
inclin.append([np.pi-np.pi/6 for i in inclin_points])<br />
#60,120 deg inclinations<br />
inclin.append([np.pi/3 for i in inclin_points])<br />
inclin.append([np.pi-np.pi/3 for i in inclin_points])<br />
# poles<br />
inclin.append([0])<br />
inclin.append([np.pi]) <br />
<br />
azi.append(azi_points)<br />
azi.append(azi_points)<br />
azi.append(azi_points)<br />
azi.append(azi_points)<br />
azi.append(azi_points)<br />
azi.append([np.pi])<br />
azi.append([np.pi])<br />
<br />
poi = sphcoords.sph2cart(np.pi/6, -np.pi+(10*azi_incr))<br />
poi = poi*rotmat.z_rot(-5*azi_incr)<br />
poi = poi*rotmat.x_rot(np.pi/6)<br />
poi = poi*rotmat.y_rot(np.pi/2)<br />
generate_plot(inclin, azi, poi)</code><br />
<code> </code>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com5tag:blogger.com,1999:blog-10244190.post-48323255150891863062010-08-15T17:53:00.004-04:002010-08-22T10:47:56.819-04:00Spherical to Cartesian Coords - Thanks Python!Game and graphics programming requires the modeling of three-dimensional geometries in software. This stuff is relatively simple in theory but easy to screw up and difficult to debug. <br />
<br />
The math used in 3-D geometrical programming has its limitations and special cases. One example is the <a href="http://en.wikipedia.org/wiki/Gimbal_lock">gimbal lock</a> problem that occurs when <a href="http://en.wikipedia.org/wiki/Rotation_matrix">rotating a body using matrices and Euler angles</a>.<br />
<br />
Another problem that I recently stumbled across occurred when trying to find the intersection point of a line and plane. If the line is pointing away from the plane, the intersection point ends up being behind the line's start point. Essentially, if we shoot an arrow directly away from a target, it will still hit the target, but with the back of the arrow.<br />
<br />
The problem I describe above with the intersection point, line and plane is not really a problem at all. The special case is easily detected while computing the result and the code can respond as required.<br />
<br />
The real issue is that we must have a complete understanding of the mathematical techniques we use in our software.<br />
<br />
There are two ways to understand math, one is to use analysis, the other is intuition/visualization. Analysis is the stronger of the two methods and includes things such as proofs. Intuition, visualization and experimentation is the approach that I take.<br />
<br />
My latest interest is <a href="http://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions">converting spherical coordinates into Cartesian coordinates</a>. I can easily plug and chug using the standard formulas, but after learning a few hard lessons, I know I had better gain some understanding.<br />
<br />
When it comes to three-dimensional programming, it is not as easy to visualize 3-D space as one would imagine. I needed some help to visualize this stuff.<br />
<br />
Locations on earth are identified using spherical coordinates, using the intersection of two angles, an angle of inclination (latitude) and an angle of azimuth (longitude). In my investigation, I would like to consider inclination angles from 0 to 180 degrees, azimuths from 0 to 360 degrees and a spherical radius of one.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_7BAP_W8Mqyo/TGhWbC0pBmI/AAAAAAAAAFs/l-niG-DAq8U/s1600/image2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="425" src="http://4.bp.blogspot.com/_7BAP_W8Mqyo/TGhWbC0pBmI/AAAAAAAAAFs/l-niG-DAq8U/s640/image2.png" width="640" /></a></div><br />
The plot simplifies the problem by reducing three-dimensions to a two-dimensional display with circles of common inclination. This is essentially a polar-plot. Each circle of common inclination (latitude) represents two inclinations, the inclination (northern hemisphere) and 180 - inclination (southern hemisphere).<br />
<br />
As a two-dimensional polar plot, the problem is solved using the azimuth angle, and the distance from the origin to the proper circle of common inclination.<br />
<br />
It appears from this work, that spherical to cartesian coordinate conversions is a simple and safe process (with the exception of when the inclination angle is 0 or 180 degrees, of course).<br />
<br />
<code><br />
import numpy as np<br />
import matplotlib.pylab as plt<br />
<br />
def x(inclin, azi):<br />
""" compute the x cartesian coord """<br />
return np.sin(inclin) * np.cos(azi)<br />
<br />
def y(inclin, azi):<br />
""" compute the y cartesian coord """<br />
return np.sin(inclin) * np.sin(azi)<br />
<br />
def z(inclin):<br />
""" compute the z cartesian coord """<br />
return np.cos(inclin)</code><br />
<br />
<code><br />
inclin = []<br />
inclin.append([np.pi/2 for i in range(0,20)])<br />
inclin.append([np.pi/3 for i in range(0,20)])<br />
inclin.append([np.pi/4 for i in range(0,20)])<br />
inclin.append([np.pi/6 for i in range(0,20)])<br />
inclin.append([np.pi/8 for i in range(0,20)])<br />
inclin.append([np.pi/12 for i in range(0,20)])<br />
inclin.append([np.pi/24 for i in range(0,20)])<br />
inclin.append([0 for i in range(0,20)])<br />
azi = np.arange(-np.pi, np.pi, (2 * np.pi)/float(20))<br />
<br />
plt.plot(<br />
x(inclin[0],azi), y(inclin[0],azi), "g-o",<br />
x(inclin[1],azi), y(inclin[1],azi), "r-o",<br />
x(inclin[2],azi), y(inclin[2],azi), "c-o",<br />
x(inclin[3],azi), y(inclin[3],azi), "m-o",<br />
x(inclin[4],azi), y(inclin[4],azi), "y-o",<br />
x(inclin[5],azi), y(inclin[5],azi), "k-o",<br />
x(inclin[6],azi), y(inclin[6],azi), "b-o",<br />
x(inclin[7],azi), y(inclin[7],azi), "w-o")<br />
plt.legend(<br />
('90', '60, 120', '45, 135', <br />
'30, 150', '23, 157', <br />
'15, 165', '8, 172', '0, 180'))<br />
plt.grid(True)<br />
plt.ylabel('Y Cartesian Coord')<br />
plt.title('Cartesian from Spherical')<br />
plt.xlabel('X Cartesian Coord')<br />
plt.show()<br />
<br />
</code><br />
<div class="separator" style="clear: both; text-align: center;"></div>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-15018671615538980012010-03-19T14:10:00.012-04:002010-08-22T10:51:56.870-04:00The Cult of Software Engineering AcademiaThis morning I was online checking-out a software architecture conference that I had attended before and was considering to attend this year. I was looking over the keynote speakers and found the bio of one particular speaker to be interesting.<br />
<br />
The speaker, a PhD and college professor is a leading authority on modern software development methodologies. Her focus is guiding organizations to institute the cultural changes required to adopt modern software development methods.<br />
<br />
Her bio included a quotable-quote or catch-phrase that went like this: "You can take a man out of the Stone Age, but you can't take the Stone Age out of the man".<br />
<br />
I was rocked-back by the arrogance of that statement. I am sure that she and I would be on the same page regarding software methods and the need for cultural change, but to publicly blast those who are not ready to accept your opinions as "less-evolved" is arrogant and absurd.<br />
<br />
Perhaps we are seeing the deficiencies in her culture. The culture of academia. A culture accustomed to feeding bright, fresh, hungry minds. A culture accustomed to unquestionable omnipotence over its audience.<br />
<br />
This culture of education has attempted to recast itself into a "culture of change", targeting the software industry. But in the process of recasting itself, it has done little in the way of introspection.<br />
<br />
Perhaps the most important skills fostered by working software professionals include communication, collaboration and negotiation. Influence does not come easy and neither does experience. Both have to be earned.<br />
<br />
Veteran software professionals have learned to listen as well as to speak, to accept criticism and to consider the ideas and opinions of others. Veteran software professionals would feel negatively about a one-way monologue that targets topics so close to the practice of their art.<br />
<br />
I am not suggesting that "change agents" engage in conversation as a way to "handle" an audience, using dialog merely as a soft-skill. Instead I would hope that any discussion would be an honest, open, two-way exchange of ideas.<br />
<br />
Of course, this may require the "change agent" to closely engage with her audience. In effect, to lose some of her elite status in order to gain the acceptance of her ideas.<br />
<br />
To be a participant instead of a prescriber.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-71837087061766044642010-03-13T23:10:00.009-05:002010-08-15T16:03:38.046-04:00Write Software Requirements For The Love Of It<span style="font-size: 100%;">Yeah sure. Right. Who am I kidding, writing software requirements kind of stinks. But right now</span>, I cannot imagine a better use of my time.<br />
<br />
<span style="font-weight: bold;">Writing Good Software Requirements is Hard</span><br />
<br />
Writing good requirements is a least as hard as writing good code. One key attribute of a good software requirement is that it must be unambiguous. The English language is notoriously ambiguous. The legal profession, political debate and religious holy wars are based on differing interpretations of the written word. Conversely, software programming languages are designed to be unambiguous because they have to be interpreted or parsed by a machine. Perhaps this is why programmers love to code - the machine detects and points out errors, or better yet, validates our efforts by correctly performing the operations that we have specified.<br />
<br />
This brings takes us to another key attribute of a good software requirement, the requirement must be correct. This is obvious but the critical point is that the requirement must be perceived as correct by all of the software system's stakeholders. The point of the SRS is to capture a commonly agreed upon view of how the software system will function. The act of generating the SRS can end up being a process of socialization and team building. (Right now most programmers are saying "Have fun, I will be in the lab writing code".)<br />
<br />
Looking at some job postings, they all seem to seek programming language expertise and experience. Bah! Programming is natural, joyous and simple compared to writing requirements. Writing requirements is like being a salmon, battling its way up stream to spawn and die. Well, maybe its not that bad, but its not as gratifying as writing code.<br />
<br />
<span style="font-weight: bold;">The Greatest Software Requirements Pitfall</span><br />
<br />
Specifying software requirements that includes design and implementation details will lead you and your project to your doom.<br />
<br />
It is actually difficult, unnatural, and odd to specify system requirements in way that is bereft of design and implementation. For one thing, as software developers, we are primarily problem solvers and solution creators. We are chomping on the bit to design, and implement. Secondly, we are comfortable talking in terms of the software system itself. Specifying software functions without including implementation details is not easy.<br />
<br />
Writing software requirements that includes design and implementation is a trap. Essentially, if the implementation is used to define requirements, the implementation itself must be controlled. This leads to an explosion of requirements documentation. It becomes difficult to make even simple changes to the system without stakeholder consensus and documentation maintenance.<br />
<br />
<span style="font-weight: bold;">A Software Requirements Fallacy</span><br />
<br />
There is an old software engineering joke, one developer says to the other, "You start coding while I go and get the requirements". At this point all learned software engineers slap their knees and laugh because its funny to think that some developers would start building a system before they knew what the system was required to do.<br />
<br />
I believe this is line of thought is incorrect. Software requirements are not necessary to start software system development. Software requirements are paramount when trying to figure out if software development is complete.<br />
<br />
Initial development can begin with some basic knowledge of the intended system. Initial development, proof-of-concepts, and prototypes will foster an understanding of the domain, encourage communications and team-building.<br />
<br />
Essentially, I am suggesting that the act of software requirements specification does not have to imply the use of the <a href="http://en.wikipedia.org/wiki/Waterfall_model">waterfall methodology</a>. I am suggesting that software requirements specification works well with iterative and incremental methods.<br />
<br />
Unfortunately I cannot speak to how Agile methodologies factor in software requirements. I would bet that most include some form of functional description document. Perhaps the system is developed in increments that leads to some collective consensus of the intended systems functions.<br />
<br />
<span style="font-weight: bold;">A Software Requirements Truth</span><br />
<br />
An SRS is simply a checklist of functions that a software system must perform.<br />
<br />
Ultimately, the effectiveness and usability of the software system, as perceived by its end-users, is primarily driven by the talent, dedication and expertise of the software development team. This is because the one, unwritten requirement levied on all software systems should be:<br />
<br />
The software system shall not stink.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-79664687737249726332009-01-28T13:42:00.058-05:002009-02-04T20:05:16.830-05:00Friqing Out with Python ClosuresYou have to love snow days. After spending an hour shoveling snow this morning, I fired up my lap-top for a guilt-free afternoon of tinkering around with some code. I opened up some Python code that I had coded last year when I was discovering functional programming with Erlang and <a href="http://mikepetry.blogspot.com/2008/01/thinking-in-erlang-coding-in-everything.html">relating it to languages that I was familiar with</a>.<br /><br /><a href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/SYCzj0skpXI/AAAAAAAAADY/MVE6HGmbvm4/s1600-h/python-logo.gif"><img id="BLOGGER_PHOTO_ID_5296430589810091378" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; WIDTH: 200px; CURSOR: hand; HEIGHT: 67px" alt="" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/SYCzj0skpXI/AAAAAAAAADY/MVE6HGmbvm4/s200/python-logo.gif" border="0" /></a>Python is a great language for exploration. Back in the old days I did a lot of programming using C++ and Microsoft's <a href="http://en.wikipedia.org/wiki/Component_Object_Model">Component Object Model (COM)</a>. C++ COM is a fairly complex technology so I found it preferable to prototype object-oriented designs using Java. The Java language's interface mechanism was a good analog for COM's interface-based programming paradigm. Java was a sexy thing compared to COM and Java was the lingua-franca of object oriented design during this period.<br /><br />Then I discovered Python and found that it featured a familar approach to object orientation and it looked like it would support rapid prototyping and concept exploration.<br /><br />Python proved to be a excellent language, but it did not serve well as a prototypical language for C++ applications. In the C++ language, data types are everything. Most of the advanced features of the language, such as templates and classes, provide ways to manage functionality across data types. The C++ compiler is the ultimate master and the programmer is subjected to its rule of type.<br /><br />Python cannot be used to prototype solutions to C++ problems because the problems do not appear in Python.<br /><br />Most circa 2000 C++ programmer were ill-equipped to absorb Python. To absorb Python meant to be influenced and changed by Python - to refute idioms previously held sacrosanct.<br /><br />To close the loop, a next logical step would be to gain perspective by relating Python to functional programming languages and not just to Java and C++.<br /><br /><strong>My Python Data Structures Project</strong><br /><br />I go through a cycle <a href="http://mikepetry.blogspot.com/2005/03/last-night-i-dreamt-of-trees.html">every now and again </a>that involves Python and data structures. A good data structure to code is the <a href="http://en.wikipedia.org/wiki/Priority_queue">Priority Queue</a>. The binary heap-based Priority Queue features a tree data structure, implemented using an array and clever array index manipulations. A <a href="http://en.wikipedia.org/wiki/Binary_heap">binary min heap</a> is shown below. Essential a binary heap must always be correctly ordered, children must always be greater than its parents (in the case of the binary min heap).<br /><br /><img class="gl_photo" alt="Add Image" src="http://www.blogger.com/img/blank.gif" border="0" /><br /><a href="http://upload.wikimedia.org/wikipedia/commons/6/69/Min-heap.png"><img style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 442px; CURSOR: hand; HEIGHT: 286px; TEXT-ALIGN: center" alt="" src="http://upload.wikimedia.org/wikipedia/commons/6/69/Min-heap.png" border="0" /></a><br />The binary heap is stored in a simple array. Algorithms maintain the relations between array elements as shown below. One exception is that it is easier to implement the array-based binary heap if the first element is stored at index 1 in the array.<br /><br /><br /><br /><img id="BLOGGER_PHOTO_ID_5296448162670288050" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 200px; CURSOR: hand; HEIGHT: 50px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_7BAP_W8Mqyo/SYDDiss7nLI/AAAAAAAAADg/0Xv2kWjWY_E/s200/370px-Binary_tree_in_array_svg.png" border="0" /><br /><br /><p>One reason to code up a Priority Queue is that it is a building-block data structure with multiple uses. The Priority Queue conceptually fits well into an abstract data type (ADT) such as a C++/Java/Python class. In fact there are Priority Queue ADTs in all three languages. My recent investigations into functional programming led me to create a Priority Queue using lexical closures. My original class-based Priority Queue was named priq. Thus the functional-closure based version became friq. And indeed it is a friq.</p><p><strong>The friq</strong></p><img id="BLOGGER_PHOTO_ID_5296503620440710114" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 156px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/SYD1-w_wW-I/AAAAAAAAAEo/eSJvmSn-jk0/s400/friq.png" border="0" /><br /><br />The screen shot above shows the definition of friq which is function with a single parameter <strong>lt</strong>. Parameter <strong>lt</strong> is a comparison "less-than" function which is easier demonstrated than explained. It will be demonstrated in the next section. Note that friq is a function definition that encloses other function definitions, and a rather strange nested list <strong>heef.</strong> The functionality exported by friq is done with its return statement (see below).<br /><br /><br /><br /><img id="BLOGGER_PHOTO_ID_5296507238688905186" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 144px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/SYD5RYBWV-I/AAAAAAAAAFI/5je6_4rQPj4/s400/enq.png" border="0" /> <img id="BLOGGER_PHOTO_ID_5296507447268658258" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 397px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_7BAP_W8Mqyo/SYD5dhCozFI/AAAAAAAAAFQ/kV6jXiybqVU/s400/dq.png" border="0" />The functions <strong>enqueue</strong> and <strong>dequeue</strong> shown above are the two functions, enclosed by friq, that are exported to the outside world using friq's return statement.<br /><br /><br /><img id="BLOGGER_PHOTO_ID_5296504349235375218" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 386px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/SYD2pL98bHI/AAAAAAAAAE4/MhjzK14yEWU/s400/down_heap.png" border="0" /><br /><img id="BLOGGER_PHOTO_ID_5296504790131218818" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 250px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/SYD3C2btGYI/AAAAAAAAAFA/k4phO_REluw/s400/up_heap.png" border="0" /><br /><div>Functions <strong>down_heap</strong> and <strong>up_heap</strong> implement the heavy lifting algorithms for the priority queue and are not exported to the outside world.<br /></div><div><br /><p><br /></p><img id="BLOGGER_PHOTO_ID_5296512187308778962" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 121px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_7BAP_W8Mqyo/SYD9xbERJdI/AAAAAAAAAFY/9BFbhgZG2rY/s400/return.png" border="0" />Finally we have the enclosing function <strong>friq</strong> returning a tuple containing the functions we wish to export.<br /><br /><p><strong>Demonstrating the friq</strong></p><p>The following code demonstrates usage of the friq. A list <strong>d </strong>is loaded with some random integers. The <strong>friq</strong> is invoked returning a tuple containing a function to enqueue values and another for dequeueing. List <strong>d </strong>is iterated and its values are enqueued. Then the <strong>friq</strong> is drained by dequeueing.</p><p></p><img id="BLOGGER_PHOTO_ID_5296493684304735138" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 396px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_7BAP_W8Mqyo/SYDs8aAVa6I/AAAAAAAAAEA/9ptx9RxAa7s/s400/Test+2.png" border="0" /></div><br /><br /><br />The usage of <strong>friq</strong> appears reasonably simple and straighforward. Note <strong>friq's lt</strong> parameter being passed a Python lambda function. The results are seen below.<br /><br /><br /><br /><p></p><img id="BLOGGER_PHOTO_ID_5296494974750075794" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 283px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/SYDuHhSeq5I/AAAAAAAAAEI/LOcyttyB5-w/s400/Test+2+Results.png" border="0" /><br /><br />Next we have a slightly more complex demonstration in which a tuple containing a number and a string is enqueued. Note that the code that handles dequeueing has to do extra work to expand the tuple. Also note that the lambda function is slightly more complex in order to handle the tuple. The index 0 signifies that the sort will be on the tuple's number.<br /><p><img id="BLOGGER_PHOTO_ID_5296495384420825906" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 374px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/SYDufXbmOzI/AAAAAAAAAEQ/9aHWyZ0S5jc/s400/Test+3a.png" border="0" /></p><br /><br />The next demonstration is identifical to the previous with the exception that the lamda function uses an index of 1 which signifies that the sort will be on the tuple's string.<br /><br /><br /><img id="BLOGGER_PHOTO_ID_5296495747620744754" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 222px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/SYDu0gdNLjI/AAAAAAAAAEY/KdRVdoo3E8c/s400/Test+3b.png" border="0" /><br />And finally, a display of the results of both demonstrations that involve the (number, str) tuple.<br /><br /><br /><img id="BLOGGER_PHOTO_ID_5296496110093736866" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_7BAP_W8Mqyo/SYDvJmxgG6I/AAAAAAAAAEg/KUbNnIBxQos/s400/Test+3+Results.png" border="0" /><br /><br />Here is the complete listing of friq.py<br /><br /><pre style="BORDER-RIGHT: black 1px solid; PADDING-RIGHT: 0.8em; BORDER-TOP: black 1px solid; PADDING-LEFT: 0.8em; FONT-SIZE: 0.8em; PADDING-BOTTOM: 0.8em; MARGIN: 0px; BORDER-LEFT: black 1px solid; PADDING-TOP: 0px; BORDER-BOTTOM: black 1px solid"><br /><strong>friq.py</strong><br /><br />#! /usr/local/bin/python<br /><br />def friq(lt):<br /> """ <br /> function friq that returns lexical closure function objects. <br /> param lt - a 'less than' function or lambda<br /> """<br /> heef = [[None]]<br /><br /> def enqueue(x):<br /> """<br /> enqueue - visible function <br /> Add to the priority queue<br /> """<br /> heap = heef[0]<br /> heap.append(x)<br /> up_heap(heap)<br /> <br /> def dequeue():<br /> """ <br /> dequeue - visible function <br /> Get the highest priority elemement<br /> """<br /> value = None<br /> heap = heef[0]<br /> if lenf() > 0:<br /> # The dequeue value is taken from the 2nd element in the array.<br /> # The first element is not used to ease index arithmetic.<br /> value = heap[1]<br /> if lenf() > 1:<br /> # The last element value is copied to the first position.<br /> heap[1] = heap[-1]<br /> # The last value is deleted to reduce the size of the pq by one.<br /> del heap[-1]<br /> if lenf() > 1:<br /> # The val at heap[1] is moved down heap to maintain order.<br /> down_heap(heap)<br /> return value<br /><br /> def heap():<br /> """ <br /> heap - non-visible function <br /> Returns the underlying array-based binary heap.<br /> Can be made visible for debug. <br /> """<br /> return heef[0]<br /><br /> def lenf():<br /> """ <br /> lenf - non-visible function<br /> Returns the size of the underlying array-based binary heap.<br /> Can be made visible for debug. <br /> """<br /> return len(heef[0]) - 1<br /><br /> def down_heap(heap):<br /> """ <br /> down-heap - non-visible function <br /> The first element is moved down heap as required<br /> to satisfy heap order.<br /> """<br /> # px is an index to a bi heap parent<br /> # cx is an index to a bi heap child <br /> px = 1<br /> v = heap[px]<br /> while px <= (len(heap)-1)//2:<br /> # calc the index of the child<br /> cx = px + px<br /> # find the index of the min of the two children <br /> # (if there are two).<br /> if cx < len(heap) - 1 and \<br /> lt(heap[cx + 1], heap[cx]):<br /> cx = cx + 1<br /> # make the comparison - if v is higher pri - we are done.<br /> if lt(v, heap[cx]):<br /> break;<br /> else:<br /> # move the childs value to the parent<br /> heap[px] = heap[cx]<br /> # make the parent index that of the child.<br /> px = cx <br /> # finally the v is set to the current parent<br /> heap[px] = v<br /><br /> def up_heap(heap):<br /> """ <br /> up-heap - non-visible function<br /> The last element is moved up heap to satisfy the heap order.<br /> """<br /> # cx//2 idenifies cx's parent in the bi heap.<br /> cx = len(heap) - 1<br /> v = heap[cx]<br /> while cx//2 > 0 and lt(v, heap[cx//2]): <br /> heap[cx] = heap[cx//2]<br /> cx = cx//2<br /> heap[cx] = v<br /><br /> # the enclosing function returning functions that are to be visible<br /> return (enqueue, dequeue)<br /><br /></pre>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com6tag:blogger.com,1999:blog-10244190.post-76375038043965713292008-06-07T15:55:00.011-04:002010-08-22T12:58:20.979-04:00Hell is Other People's CodeMy current assignment has me analyzing code for an embedded system that been under development for 15 years.<br />
<br />
There is a lot of money invested in the development of this software.<br />
<br />
After 15 years of blood, sweat, and tears, the embedded system now needs modern hardware. But what to do about the very expensive software? Start over or devise some way to migrate the code base so that it may execute on the new hardware?<br />
<br />
The idea of throwing away 15 years of software development is hard to swallow - so my task is to come up with a reuse/migration strategy.<br />
<br />
But wait, there is more.<br />
1. The software is very brittle and cannot accommodate modification.<br />
2. Even basic maintenance activities are becoming a risky proposition.<br />
3. The software is monolithic with no obvious means of splitting functionality into subsystems.<br />
<br />
A secondary goal of the system modernization is to partition the software to increase robustness, maintainability and modifiability.<br />
<br />
So I begin to look at the code. And to be honest, I was humbled by its cleanliness. The code was well commented, and consistent naming conventions and styles were applied throughout. It was the code of experienced software developers.<br />
<br />
So let us take a step back. The tool I am employing in my analysis is to judge the competence of the engineers that created this troubled software. I think it is fair to carefully judge software developer competence as an analysis tool - but often there is more to the story than what meets the analyst's eye. For example most software systems are developed under the duress of schedule constraints - "trying to pack ten pounds of crap into a five pound bag". But I digress - back to my story.<br />
<br />
The "coding in the small" looks good but I had already suspected that the problem existed in the design, or more specifically, how the software modules interact with one another. Let's put the software designer under the microscope.<br />
<br />
Now the problem became very evident, many software modules where highly dependent on many other software modules. To be specific, it was easy to find software modules that were dependent on 50 other modules. It was impossible to create a informative diagram showing the dependencies in any type of meaningful way. I also searched for a more specific way to classify or present this problem to management but this also escaped me. This software simply was a rats nest of overly coupled modules that would continue to challenge our efforts to meet our goals.<br />
<br />
So now I have indicted the software designer, hopefully he has made a clean get-a-way after 15 years. But now I need a motive - how could a software engineer let this happen? Lets travel back in time to 15 years ago and see what was going on. I remember those days and I remember that object-oriented techniques where still a little esoteric to the average developer. Many developers failed to make the leap to OO and many projects suffered through initial forays into OO. The main problem with OO in those days was in the development of frameworks and architectures. OO gave us the ability to define code modules that mapped to objects in our domain but the ability to create the executive sections of our software were often not understood and under-designed.<br />
<br />
None the less, our indicted software designer still should have used the most basic and powerful software design technique, the idea of layering. But no, under analysis, the software has no structure.<br />
<br />
The trial of the software designer continues. In defense of the designer, the software has been segmented into a multitude of modules that model the domain - but somehow this act of decomposition has created a mess. Let me introduce exhibit A, the programming language.<br />
<br />
The system was programmed in the original version of <a href="http://en.wikipedia.org/wiki/Ada_%28programming_language%29">Ada</a>, the programming language developed for the U.S. Department of Defense in the late 1970's. Ada has been upgraded several times with the most notable upgrade coming in 1995 and termed Ada95, the original Ada is usually called Ada83.<br />
<br />
Ada was specifically created to solve the so-called <a href="http://en.wikipedia.org/wiki/Software_crisis">software crisis</a> that plagued the development of large software systems. Yet here was a large software system in crisis, developed using Ada.<br />
<br />
So now I will turn the glare of the spotlight away from the software designer and place it on this unpopular programming language. Now I must confess that I am not an "Ada man". I have worked on Ada projects for short terms but I have never become enamored with the language, although I know a few who have.<br />
<br />
I looked-up a few of my (with all due respect) "grey haired" colleagues to borrow some manuals on Ada83, not Ada95 because I wanted to know what Ada developers were thinking back during the early days of the system's development. My Ada-savvy co-workers were eager to supply me with the manuals and to share a few thoughts with me.<br />
<br />
Now Ada83 is not object-oriented but instead is object-based. Actually Ada83 is a procedural programming language similar to C. One main difference is that Ada includes the concept of a "package". The Ada package abstracts the "file module" and has some very powerful and useful capabilities. It is important to note that the Ada package is not a type like a Java or C++ class is a type. A package is used to define types, functions and variables - much like a class, but with the distinction that only one instance of a package exists in the program. A variable declared within a package is equivalent to a class-wide variable in Java or C++ - i.e a static variable.<br />
<br />
My object-oriented mind now saw the embedded software system as ~500 singleton software classes - with public variables - mostly interconnected to one another. Could it be that the system's engineers did not really understand the object-based, procedural nature of the Ada language and used the Ada package as some type of brain-dead object-oriented class instead of a very powerful file module abstraction?<br />
<br />
So let's be clear, in the object-based Ada language, the object is data defined, instantiated and processed by functions in the package, and not the package itself.<br />
<br />
Interestingly, it all goes back to some of my previous musing of how <a href="http://mikepetry.blogspot.com/2007/12/state-stinks.html">State Stinks!</a> Certainly the most stinkiest form of state would be static, globally accessible state.<br />
<br />
I could go on but I have said all that I need to say about this matter. And I release the incarcerated designer from his cell, we all have skeletons in our closet and besides my job is not to find out what and why but how. How to fix this software and move it into the future. So far I am the one who is falling short of his goals.<br />
<br />
But such is life in the hell of other people's code.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com4tag:blogger.com,1999:blog-10244190.post-56602449454029949342008-02-22T22:52:00.056-05:002008-02-26T12:48:38.907-05:00Bottom-Up Software Design is for SheepConsidering the devisive issues of our day: Vim vs Emacs, Ruby vs Python, .Net vs Java, Mac vs PC, and REST vs SOAP, there are plenty of opportunities to spark a holy war. Lets consider another polarizing issue:<br /><br /><blockquote>Is it better to design and develop software from the top-down or from the bottom-up? </blockquote>I am fortunate to have the opportunity to work across a wide variety projects and I am always curious to discover how my newly met colleagues are inclined regarding design. This evening I came across a <a href="http://duartes.org/gustavo/blog/post/2008/02/20/Richard-Feynman-Challenger-Disaster-Software-Engineering.aspx">compelling blog</a>, by Gustavo Duarte, that took a stance opposed to my own. Here is my take on the issue.<span style="font-weight: bold;"><br /><br />Design by decomposition is the quintessential technique of the engineer<br /></span><br />One of Gustavo's main points is that Feynman believed that software engineering has much in common with the other engineering disciplines. Considering this point, the most important concept in engineering is modularization. Most products and devices are made up of replaceable parts. Modularization is achieved by decomposing a system into sub-systems. System decomposition is top-down design.<br /><br />Certainly we can agree that some of the first decisions we might make include the selection of a platform (Linux, Windows), technology stack (LAMP, Java, .NET), or language/framework (Ruby on Rails, Django).<br /><br />Our choices will determine the composition of our system, how functionality is divided among system components, and usage of key patterns (e.g Model-View-Controller).<br /><br />Do we even realize that we are doing top-down design?<br /><br /><blockquote>Perhaps the availability of powerful platforms and frameworks is removing the importance of the top-down design perspective from our collective conscience. </blockquote>None-the-less, software system design by decomposition into sub-systems implies a top-down approach to design.<br /><br /><span style="font-weight: bold;">The Space Shuttle Challenger was not a victim of top-down design.</span><br /><br />Gustavo's blog walks us through some evidence that suggests that Dr. Feynman believed that top-down design doomed Challenger. The Challenger Disaster illustrates that a likely point of system failure is at sub-system interfaces. I don't see this as an indictment against top-down design.<br /><br />For a analysis of the mulitude of events that led to the Challenger disaster, I recommend the book <a href="http://http//www.amazon.com/No-Downlink-Dramatic-Narrative-Challenger/dp/0374120366">NO DOWNLINK</a>. Essentially, the Challenger and her crew where the victims of pork-barrel politics, post-space race budget cuts, and the <a href="http://jmi.sagepub.com/cgi/content/abstract/11/3/282">Challenger Syndrome</a>.<br /><br />Poor design choices such as the use of Solid Rocket Boosters (SRBs) and then the segmenting of the SRBs to aid in transportability where driven more by budgetary and political concerns then by faulty engineering practices.<br /><br /><span style="font-weight: bold;">Defending up-front software design</span><br /><br />Another point made by Gustavo is that "Big up-front design is foolish". The word "Big" implies a non-incremental approach. Up-front design is not a foolish idea. However developing software in a non-incremental fashion (i.e. the waterfall method) is a bad idea.<br /><br /><blockquote>Top-down software design leads to effectivly modularized code that fosters many good things such as reuse, testability, and maintainability.</blockquote>Gustavo drags UML into this discussion and I agree with some of his points. The idea of designers churning out UML blue-prints to throw "over the wall" to "implementors" has no appeal to me and in fact, simple does not work.<br /><br />UML is not implicitly evil and the use of UML does not have to imply some type of bureaucratic software factory where coder slaves toil away, mindlessly "implementing" designs.<br /><br />UML is just a tool that lets you work on your system at a high level. UML is just a white board.<br /><br /><blockquote>Using UML to create diagrams (which are essentially pictures) to foster communication is a beautiful thing.</blockquote>To summarize, UML should be used to open a new communication channel, never to replace face-to-face discussion.<br /><br /><span style="font-weight: bold;">Risk</span><br /><br />The main problem with up-front design from a developer's perspective is that it doesn't address many of the underlying risks to the success of the project. The term "risk" is a nice way to articulate that feeling you get in your stomach when you know you are doomed.<br /><br />The best way to handle "risk" is to start working on the risky parts of the system as soon as possible, so that you have plenty of time to handle all the foo bar.<br /><br /><blockquote>Risk is what leads developers to favor the bottom-up approach. This is because the risky sections of a system exist deep within the system and at sub-system interfaces such as APIs.</blockquote>Up-front design may add risk because some design decisions are based on assumptions that can only be validated with working code.<br /><br /><span style="font-weight: bold;">Managing Risk</span><br /><br />All forms of engineering require lab testing, research and development and prototyping to determine how elements of a system will actually perform and software is no different.<br /><br /><blockquote><p>Risk-reduction prototyping is a technique used to mitigate risks, validate design assumptions, and to develop techniques that will be used during the production of a software system.</p></blockquote><br /><span style="font-weight: bold;">Conclusion</span><br /><br />As software developers, we are also software users. We use frameworks and tool-sets that allow us to focus on the specific application that we are creating. If we don't maintain the ability to design software from the top down, we function more as users and less as developers. We become in effect, sheep walking the well-worn path instead of being being the pioneering trail-blazers we had hoped to be.<br /><br />I believe that a pragmatic combination of both top-down (up-front design) and bottom-up (risk-reduction prototyping) techniques will greatly increase a software project's chances for success.<br /><br />Dr. Feynman believed that software engineer has much in common with the other engineering disciplines. This is certaintly true, but 'how much' is less certain. Thinking of software engineering as just another engineering discipline comes with its own pitfalls.<br /><br />What is your take on this topic?<br /><script>reddit_url='http://mikepetry.blogspot.com/2008/02/top-down-or-bottom-up.html'</script><br /><script>reddit_title='Bottom-Up Software Design is for Sheep'</script><br /><script src="http://reddit.com/button.js?t=1" type="text/javascript"><br /></script>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com2tag:blogger.com,1999:blog-10244190.post-10482730701115446212008-01-10T11:06:00.000-05:002008-01-13T23:03:22.094-05:00Erlang and Tail Recursion Part II - Still Living, Still Dreaming<p>In a <a href="http://mikepetry.blogspot.com/2007/12/erlang-tail-recursion-and-living-dream.html">previous post</a>, I described how I experienced tail recursion and tail call optimization first hand. I have learned to accept that the Erlang compiler can detect when recursion can be replaced with iteration, preventing stack growth and eventual overflow (which means "hard crash"). The idea is that developers can stay within the language's functional "state-less" paradigm and let the compiler do the messy work.</p><p>I spent some time dissecting a <a href="http://www.builderau.com.au/program/soa/Modelling-graphs-with-processes-in-Erlang/0,339024614,339283345,00.htm">very interesting Erlang example </a>that I found on the web at <a href="http://www.builderau.com.au/">builder.au</a>. There are a lot of interesting nuggets in this example, requiring a little time with the <a href="http://erlang.org/doc/reference_manual/part_frame.html">Erlang reference manuals</a>.</p><p>However there was one, seemingly innocuous passage of code, that made me realize that I had a few more lessons to learn regarding Tail (or more commonly, Last) Call Optimization (LCO).</p><p><em>Note that while the code is from builder.au, the comments are mine.</em></p><br /><pre style="border: solid 1px #000;font-size:.9em;margin-top:0;padding:0 .8em .8em;"><br /><strong>Function searchNode</strong><br /><br />searchNode(Name, LinkDict) -><br /> receive<br /> {link, ToName, ToPid} -><br /> % Tail recursion that will be Last Call Optimized<br /> searchNode(Name, dict:store(ToName, ToPid, LinkDict));<br /> {search, Path} -> <br /> Free = notIn(Name, Path),<br /> if<br /> Free -><br /> lists:foreach(<br /> fun(X)-> <br /> {_, Pid} = X,<br /> Pid ! {search, Path++[Name]}<br /> end, <br /> dict:to_list(LinkDict));<br /> true -> true<br /> end,<br /> % Tail recursion that will be Last Call Optimized<br /> searchNode(Name, LinkDict);<br /> {endPoint} -><br /> % The {endPoint} message identifies that the node<br /> % 'embodied' by thisfunction is the 'target' of <br /> % the search, requiring different message handling, <br /> % and therefore a separate function 'last'.<br /> % But this is not tail recursion?<br /> last(Name, LinkDict)<br /> end.<br /></pre><p><br />Function <strong>searchNode</strong> is the function that both represents a node in a graph and an Erlang process. Once this function is initially invoked, it awaits a message from another process. Upon receiving and handling a message, it invokes itself 'tail recursively' to continue as part of the program and graph.</p><p>Except when it receives the <strong>endPoint</strong> message. In this case another function is invoked, the <strong>last</strong> function. Message <strong>endPoint</strong> is sent during the initial phase of a search, to the node that is the target of the search. In this case the node does not propagate the search but identifies when the search has completed. Essentially the "endPoint" node/process requires different behavior, therefore control is transferred to a different function.</p><p>So what happens when the search is complete? Does <strong>last</strong> return and if so, wouldn't <strong>searchNode</strong> return - ending the process/node it represents?</p><p><em>Note that while the code is from builder.au, the comments are mine.</em></p><br /><pre style="border: solid 1px #000;font-size:.9em;margin-top:0;padding:0 .8em .8em;"><br /><strong>Function last</strong><br /><br />last(Name, LinkDict) -><br /> receive<br /> {link, ToName, ToPid} -> <br /> % Tail recursion that will be LCO<br /> last(Name, dict:store(ToName, ToPid, LinkDict));<br /> {search, Path} -><br /> % This is the message of interest for this <br /> % function. This indicates that this node was <br /> % found by the search, displays the results and -<br /> % returns control back to searchNode!<br /> io:format("Found path: ~w~n", [Path++[Name]]),<br /> searchNode(Name, LinkDict);<br /> {endPoint} -><br /> % Probably just handles the off-chance corner-case <br /> % in which a node is identified as the target for a <br /> % search while in the process of being currently <br /> % searched. This may not work correctly.<br /> searchNode(Name, LinkDict)<br /> end.<br /></pre><p>Interestingly, when <strong>last</strong> has achieved its purpose it invokes function <strong>searchNode</strong>, returning control back to the process/node's primary function.</p><p>To my imperative mind this is just wrong</p><p>Think about the crazy call stack we are creating that will never unwind, eventually crashing the program. Perhaps this is handled by Last Call Optimization?</p><p>Actually the answer was right at my finger-tips, in <a href="http://erlang.org/download/erlang-book-part1.pdf">Concurrent Programming in Erlang Part I</a> (search on LCO).</p><p><strong>Essentially LCO handles functions that are mutually recursive as well as the classic 'tail recursion' case.</strong></p><p>Some final thoughts.</p><p>This example is interesting to me because it shows a situation that requires a node (or some type of entity) be assigned a special status. For example, in imperative languages we may choose to set a flag. In Erlang we wish to remain state-less, it is improper and probably very difficult to set a flag. <strong>So instead, we request a different behaviour, which is exactly what we wanted in the first place.</strong></p>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com2tag:blogger.com,1999:blog-10244190.post-85373582482242414332008-01-08T17:52:00.000-05:002008-01-09T08:30:33.207-05:00Thinking in Erlang - Coding in Everything ElseWith apologies to <a href="http://www.amazon.com/s/ref=nb_ss_gw/104-9068190-1557549?url=search-alias%3Daps&field-keywords=Bruce+Eckel&x=0&y=0">Bruce Eckel.</a><br/><br /><p><a href="http://erlang.org/download/erlang-book-part1.pdf">Concurrent Programming in Erlang Part I</a> uses a Binary Tree implementation to demonstrate Erlang's tuple data type.</p><br /><pre style="border: 1px solid black; margin:0;padding: 0 .8em .8em; font-size: 0.9em;"><br /><strong>Erlang</strong><br /><br />insert(Key, Value, nil) -><br /> {Key, Value, nil, nil};<br />insert(Key, Value, {Key,_,Smaller, Bigger}) -><br /> {Key, Value, Smaller, Bigger}; <br />insert(Key, Value, {Key1,V, Smaller, Bigger}) when Key < Key1 -><br /> {Key1, V, <strong>insert(Key, Value, Smaller)</strong>, Bigger};<br />insert(Key, Value, {Key1, V, Smaller, Bigger}) when Key > Key1 -><br /> {Key1, V, Smaller, insert(Key, Value, Bigger)}.<br /></pre><br /><p>I was intrigued by this elegant approach to the Binary Tree.</p><p>Erlang's tuple data type collects the elements that make up a node including the node's key, value, and the references to its children nodes.</p><p>The first clause of the insert function is the "base case" that actually creates a new node. The second clause handles the case when the key is found and the value is simply replaced. Clauses three and four handle the cases that require traversal down through the tree.</p><p>Notice how the recursion is so wonderfully encoded. This is much cleaner and intuitive than the imperative solutions that I have worked with.</p><br /><pre style="border: 1px solid black; padding: 0 .8em .8em; font-size: 0.9em;"><br /><strong>Python</strong><br /><br />def insert(key, value, tree):<br /> if tree == None:<br /> return (key, value, None, None)<br /> (key1, v1, smaller, bigger) = tree <br /> if key == key1: <br /> return (key1, value, smaller, bigger)<br /> if key1 > key: <br /> return (key1, v1, <strong>insert(key, value, smaller)</strong>, bigger)<br /> return (key1, v1, smaller, insert(key, value, bigger)) <br /></pre><br /><p>I would guess that this elegant Binary Tree implementation is common to functional programming languages in general. Python has some functional language capability and has a tuple data type. I was able to implement the "elegant" Binary Tree using Python.</p><br /><pre style="border: 1px solid black; padding: 0 .8em .8em; font-size: 0.9em;"><br /><strong>JavaScript</strong><br /><br />function insert(key, value, tree) { <br /> if(tree == null)<br /> return {key:key, value:value, smaller:null, bigger:null};<br /> if(tree.key == key){<br /> tree.value = value;<br /> return tree;<br /> }<br /> else if(key < tree.key)<br /> return {key:tree.key, value:tree.value, <br /> smaller:<strong>insert(key, value, tree.smaller)</strong>,<br /> bigger:tree.bigger};<br /> else<br /> return {key:tree.key, value:tree.value, <br /> smaller:tree.smaller, <br /> bigger:insert(key, value, tree.bigger)};<br />}<br /></pre><br /><p>I have read that JavaScript is classified as a functional programming language and its object data type looks syntactically similar to Erlang's tuple. I was able to implement the "elegant" Binary Tree using JavaScript. For more fun, I created an interactive Binary Tree web page.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-25364551211161436452007-12-26T18:12:00.000-05:002007-12-28T18:24:04.281-05:00Erlang and the Towers of HanoiMy brilliant, 15 year-old son is teaching himself how to program Ruby using Chris Pine's wonderful book, <a href="http://www.amazon.com/Learn-Program-Pragmatic-Programmers-Chris/dp/0976694042">Learn to Program</a>. The book devotes an entire chapter to recursion and I spent an enjoyable evening with my son as we dissected an example program. Pine's example is reminiscent of the Towers of Hanoi, a puzzle that my son has been able to solve since he was 7. So I decided to code up this classic, first in Python and then in Erlang.<br /><br /><span style="font-weight: bold;">Towers of Hanoi - Erlang Style<br /></span><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_7BAP_W8Mqyo/R3Llx2ehEwI/AAAAAAAAABo/5AuvluZ1Gjg/s1600-h/process_towers.PNG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_7BAP_W8Mqyo/R3Llx2ehEwI/AAAAAAAAABo/5AuvluZ1Gjg/s400/process_towers.PNG" alt="" id="BLOGGER_PHOTO_ID_5148429968637760258" border="0" /></a><br />My son could not believe that this function would solve the puzzle! I used a pencil and graph paper to illustrate an example with 4 plates and was once again amazed by the sheer beauty of this algorithm. One can only marvel as too how someone was able to discover this elegant solution.<br />It was my son who pointed out the "depth first" nature of the algorithm. The program recurses to a depth determined by the number of plates and then retreats back to the top of the stack of function calls, moving plates as it goes. Each retreat up the stack results in another incursion down to the depth determined by the number of plates. This continues until the original call returns and the program completes.<br /><br />Essentially the process of recursion lays out a data structure of sorts that establishes a sequence of plate moves.<br /><br />Notice the line in the code above: <span style="font-weight: bold;">managerPid ! {self(), A,C}. </span>This line of code is used to move a plate. The movement of the plate is handled by a separate process that represents a device or maybe even a person responsible for moving the plates.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_7BAP_W8Mqyo/R3Lmw2ehExI/AAAAAAAAABw/nR6u8h-yzpo/s1600-h/manage_towers.PNG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_7BAP_W8Mqyo/R3Lmw2ehExI/AAAAAAAAABw/nR6u8h-yzpo/s400/manage_towers.PNG" alt="" id="BLOGGER_PHOTO_ID_5148431050969518866" border="0" /></a><br />Function manage_towers, in the code illustration above, executes within the second process and writes the tower movements to the console.<br /><br />I had some philosophical concerns with the multi-process approach to the Towers of Hanoi. Recursion and distributed computing are two distinct and orthogonal solutions that are based on the same idea, the <a href="http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm">divide and conquer algorithm</a>. However the solution does have some pragmatic appeal to me and it all begins with the immutability of Erlang's variables.<br />The thought is that the Towers of Hanoi program cannot actually move the plates, the plates would have to be moved by some entity apart from the program. This allows philosophical head room for both immutable variables and asynchronous calls to a process that records the plate movements.<br /><br />As I watched the console display the plate movements, my mind reeled as I thought of the program descending and ascending through the call stack, briefly pausing to issue asynchronous messages of instruction to its sister process.<br /><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7BAP_W8Mqyo/R3LoqWehEyI/AAAAAAAAAB4/LxuMLwOkYig/s1600-h/start_towers.PNG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/R3LoqWehEyI/AAAAAAAAAB4/LxuMLwOkYig/s400/start_towers.PNG" alt="" id="BLOGGER_PHOTO_ID_5148433138323624738" border="0" /></a><br />The Towers of Hanoi program is initiated by the code illustration shown above.<br /><br />Notice that when the call to <span style="font-weight: bold;">process_towers</span> returns, the tower manager process (<span style="font-weight: bold;">managerPid</span>) is sent a message that indicates that the program has <span style="font-weight: bold;">finished</span>.<br /><br /><span style="font-weight: bold;">managerPid ! {self(), finished}</span><br /><br />The main process (thread) of the program then blocks at the <span style="font-weight: bold;">receive</span> statement, awaiting acknowledgment from the tower manager process. Essentially, the main process is deferring termination until the tower manager process completes.<br /><br />As you can imagine, the <span style="font-weight: bold;">process_towers</span> function computes the solution far quicker than the console can update its display. At some point, the main process waits until the tower manager handles its backlog of messages, in the order in which they arrived.<br /><br />This is accomplished using Erlang's Mail Box, which probably is implemented as a thread-safe queue.<br /><br />Many of the popular languages provide thread-safe queue classes but Erlang's Mail Box is built-in, readily available and seamless.<br /><br />But does it work robustly? I can say yes because of my carelessness.<br /><br />I tested the program with 10 plates and then decided to really test it with 20 plates. I sat there for a second before I realized what I had done. I believe Towers has an exponential growth rate, therefore 10 plates required 1000 plate movements and 20 plates requires a million plate movements.<br /><br />I let the program run, all day and into the evening. My son saw the program running and asked if I had mistakenly created an infinite loop.<br /><br />I ran the concept by him, "If we have 4 plates, it takes 16 moves, 5 plates takes 32 moves and 20 plates takes ...". "One million moves", he immediately replied. "But a million is not a big number for a computer", he said.<br /><br />We could roughly count the number of plate moves being written to the console and determined that approximately 7 moves where displayed per second.<br />Therefore it would take about two days for the program to run to completion.<br /><br />I went to sleep that night, thinking about the many thousands of messages queuing-up in the Tower Manager's Mail Box.<br /><br />I awoke the next morning to find the program dutifully displaying plate movements. I shut the program down - satisfied that Erlang's messaging system met its challenge.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com2tag:blogger.com,1999:blog-10244190.post-27564324707103603272007-12-15T00:13:00.000-05:002008-01-13T23:10:29.889-05:00Erlang, Scalability - The Next Wave<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/R2NoJ2ehErI/AAAAAAAAABA/4va0ZqF9AD0/s1600-h/nim_icon9.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/R2NoJ2ehErI/AAAAAAAAABA/4va0ZqF9AD0/s320/nim_icon9.jpg" alt="" id="BLOGGER_PHOTO_ID_5144069717838860978" border="0" /></a><br />I had a great time watching the TV series <a href="http://en.wikipedia.org/wiki/Surface_%28TV_series%29">Surface </a> on the SciFi Channel, with my son who is 10. In the final episode, Marine Biologist Dr. Laura Daugherty warns us that Tsunamis waves come in sets.<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7BAP_W8Mqyo/R2OA4WehEuI/AAAAAAAAABY/J65hgoRkwD8/s1600-h/Lake+Bell.jpg"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_7BAP_W8Mqyo/R2OA4WehEuI/AAAAAAAAABY/J65hgoRkwD8/s320/Lake+Bell.jpg" alt="" id="BLOGGER_PHOTO_ID_5144096904981844706" border="0" /></a><br />The first wave of the Erlang Tsunami was <a href="http://mikepetry.blogspot.com/2007/12/erlang-eureka.html">realizing</a> Erlang's ability to harness the potential of multi-core CPU's to perform computations, by using concurrently executing threads and processes.<br /><br />I had not recovered from the first wave when I was hit by the next wave in the set. The second wave is Erlang's ability to scale. Usually, <a href="http://en.wikipedia.org/wiki/Scalability">scalability</a> is used as characteristic of an architecture, system or technology. I don't recall if I have seen scalability used to characterize a programming language. Certainly, most programming languages provide features that can be used to develop scalable systems - but what would it take to make the language inherently scalable?<br /><pre style="border:1px solid #000;margin-top:0;font-size:.9em;padding:0 .8em .8em;"><br />loop(State) -><br /> receive<br /> {call, From, Request} -><br /> {Result, State2} = handle_call(Request, State),<br /> From ! Result,<br /> loop(State2);<br /> end.<br /></pre><br />Function <strong>loop(State)</strong> waits at the <strong>receive</strong> statement for a message that matches tuple <strong>{call, From, Request}</strong>. When a matching message is received. it is handled by the statement that calls the aptly named function, <strong>handle_call</strong>. <strong>From</strong> is messaged with the <strong>Result</strong> returned by function <strong>handle_call</strong>.<br /><br />So, the received message includes information that identifies the operation to perform, the return address, and perhaps some parameters. This operation is "stateless" because the state required to process the request is provided with the request and is not maintained by loop. This is how <a href="http://en.wikipedia.org/wiki/REST">REST</a> works.<br /><br />A key-word used in the last couple of paragraphs is "message". Note that the only coupling between sender and receiver in this <a href="http://en.wikipedia.org/wiki/Message_passing#Examples_of_Message_passing_style">message passing </a>approach is the "shape" of the data passed (e.g. the tuple {call, From, Request}). This results in a very dynamic, and decoupled approach.<br /><br />Erlang is highly scalable because its stateless, message-passing approach is suitable for all levels of distributed computing. It matters not if the distribution is among threads in one process or among hosts arrayed around the world. Erlang programmers use the same syntax, patterns and idioms, regardless of the level of distribution.<br /><br />This is true scalability.<br /><br />The Tsunami metaphor might have seemed like an over-statement, hopefully I have proved otherwise, but let us consider one final point. The Erlang programmer's skill scales. If a programmer can develop programs using concurrency and Erlang, it matters little if the program runs stand-alone on one box, or is widely distributed across the internet.<br />Do you feel the wave?<br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7BAP_W8Mqyo/R2N-x2ehEtI/AAAAAAAAABQ/xT4PWj9p7Kw/s1600-h/nim_borg_icon.jpg"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_7BAP_W8Mqyo/R2N-x2ehEtI/AAAAAAAAABQ/xT4PWj9p7Kw/s400/nim_borg_icon.jpg" alt="" id="BLOGGER_PHOTO_ID_5144094594289439442" border="0" /></a></pre>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com2tag:blogger.com,1999:blog-10244190.post-83823310492570660822007-12-12T22:23:00.000-05:002007-12-28T06:37:41.593-05:00Erlang, Tail Recursion, and Living the Dream<a href="http://mikepetry.blogspot.com/2007/12/erlang-eureka.html">Erlang Eureka!<span style="font-weight: bold;"> </span></a>describes a moment of discovery as the narrator walks through a Erlang code example. Function <span style="font-weight: bold;">loop</span> is an example of a function that runs within its own thread, awaits messages from other threads, and then acts on received messages. Note that function <span style="font-weight: bold;">loop</span> is a loop only in the sense that <span style="font-weight: bold;">loop</span> repeats by calling itself.<br /><span style="font-weight: bold;"></span><pre><span style="font-weight: bold;">loop(Module, State) -></span><br />receive<br />{call, From, Request} -><br /> {Result, <span style="color: rgb(255, 0, 0);">State2</span>} = Module:handle_call(Request, State),<br /> From ! {Module, Result},<br /> <span style="font-weight: bold;">loop(Module, <span style="color: rgb(255, 0, 0);">State2</span>);</span><br />{cast, Request} -><br /> <span style="color: rgb(255, 0, 0);">State2</span> = Module:handle_cast(Request, State),<br /> <span style="font-weight: bold;">loop(Module, <span style="color: rgb(255, 0, 0);">State2</span>)</span><br />end.</pre>Loops, such as <span style="font-style: italic;">for</span> and <span style="font-style: italic;">while</span> loops, are a considered to be iterative. Function <span style="font-weight: bold;">loop</span> is a different beast in that it uses <a href="http://triton.towson.edu/%7Eakayabas/COSC455_Spring2000/Recursion_Iteration.htm">recursion</a> to mimic the iterative loop mechanism. Function loop uses a type of recursion that is called "tail recursion". Essentially if a function returns the return value of a recursive call, it is tail recursive.<br /><br />In languages such as Java, C, C++, C# and Ada (imperative languages), tail recursion is frowned upon because it risks overflowing the stack Tail recursion can be easily replaced with an iterative approach, a process called <span style="font-style: italic;">tail call optimization</span>.<br /><br />On the other hand, functional languages such as Erlang, Lisp, Scheme, and F# make use of tail recursion as we can see from the code example.<br /><br />Why?<br /><br />1. Iteration by recursion is required because<span style="font-weight: bold;"> variables may be assigned only once</span> in most functional languages, including Erlang. This "assign once" feature is one of the key reasons that Erlang can manage state well in the face of concurrency. Notice how <span style="font-weight: bold;">State</span> is updated within <span style="font-weight: bold;">loop</span> and stored in <span style="font-weight: bold;">State2</span> which is passed by parameter to the tail recursive call to <span style="font-weight: bold;">loop</span>.<br /><br />2. Functional programming language compilers, such as Erlang, perform tail call optimization. Functional language programmers will actually structure code to achieve tail recursion (pretty different). See <a href="http://prog21.dadgum.com/1.html">A Deeper Look at Tail Recursion</a>.<br /><br />I also would like to credit Jomo Fisher's <a href="http://blogs.msdn.com/jomo_fisher/archive/2007/09/19/adventures-in-f-tail-recursion-in-three-languages.aspx">Adventures in F# -- Tail Recursion in Three Languages</a> blog .<br /><br />Its funny how I set off to research this topic. After experimenting with Erlang over a weekend and <a href="http://mikepetry.blogspot.com/2007/12/erlang-eureka.html">blogging about it</a>, I went to work on Monday and actually thought about implementing a loop using recursion, <span style="font-weight: bold;">for about a microsecond</span>. My fingers stopped so fast that they left skid marks on the keyboard. Could you imagine implementing an application's main loop using recursion and several seconds into operation the stack overflows, the application crashes, and there you sit feeling very dumb?<br /><br />With a bolt of lightening, a clear perspective of the differences between imperative and functional programming languages lay before me.<br /><br />It is great when your work is interesting and your work-place is your laboratory - that is why I am Living the Dream.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-40006056649548970612007-12-11T20:12:00.000-05:002007-12-11T20:17:57.443-05:00Metrics-Based Software Management - A Hands On Approach<object height="355" width="425"><param name="movie" value="http://www.youtube.com/v/JIASiZQR3nI&rel=1&border=0"><param name="wmode" value="transparent"><embed src="http://www.youtube.com/v/JIASiZQR3nI&rel=1&border=0" type="application/x-shockwave-flash" wmode="transparent" height="355" width="425"></embed></object>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-6217880047628871542007-12-06T18:42:00.000-05:002007-12-28T06:38:31.921-05:00Erlang Eureka!<a href="http://mikepetry.blogspot.com/2007/12/state-stinks-part-ii.html">State Stinks! Part II</a> discusses that managing the operational control of an application is problematic when the application is multi-threaded. Multi-threaded applications consist of concurrently operating execution paths that share common state and data.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://erlang.org/"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/R1iWJdjfS9I/AAAAAAAAAAk/qlzlVvD1vsg/s320/erlang.gif" alt="" id="BLOGGER_PHOTO_ID_5141024063939890130" border="0" /></a><a href="http://www.moserware.com/">Jeff Moser</a> suggested that I look into the <a href="http://erlang.org/">Erlang</a> programming language. One of his <a href="http://www.moserware.com/2007/11/attack-of-mutations-or-why-we-do-we.html">blog postings</a> led me to an excellent <a href="http://channel9.msdn.com/showpost.aspx?postid=351659">interview</a> with Joe Armstrong, the principal inventor of Erlang. Joe is an engaging individual and has many interesting things to say. Joe prefers "concurrent orientation" over "object orientation" and he makes a compelling point. Some of my past <a href="http://mikepetry.blogspot.com/2005_02_01_archive.html">posts</a> reflect my opinion that strongly favors object orientation. I also <a href="http://mikepetry.blogspot.com/2005/03/temporal-anamolies.html">ruminated</a> on threads and the odd pairing of concurrency and object orientation. Basically this is the kind of stuff that peaks my interest so I proceeded to <a href="http://erlang.org/">download</a> Erlang.<br /><br />First of all, everything went well, the Erlang web site is well organized and full of resources. Erlang has been around since 1991 and is a very well documented, mature programming language.<br /><br />I started working my way through the tutorials, looking forward to that "eureka" moment when I would see Erlang's secret sauce. To begin with, Erlang's functional approach with (immutable) variables that can only be bound once, is well chronicled. Other features of the language such as dynamic-typing combined with assignment by pattern-matching makes for a feature-rich, expressive language that you just have to try for yourself.<br /><br />My eureka moment?<br /><pre><br />init(Module) -><br /> register(Module, self(),<br /> State = Module:init(),<br /><span style="font-weight: bold;"> loop</span>(Module, State).<br /><br /><span style="font-weight: bold;">loop(Module, State) -></span><br />receive<br /> {call, From, Request} -><br /> {Result, <span style="color: rgb(255, 0, 0);">State2</span>} = Module:handle_call(Request, State),<br /> From ! {Module, Result},<br /> <span style="font-weight: bold;">loop(Module, <span style="color: rgb(255, 0, 0);">State2</span>);</span><br /> {cast, Request} -><br /> <span style="color: rgb(255, 0, 0);">State2</span> = Module:handle_cast(Request, State),<br /> <span style="font-weight: bold;">loop(Module, <span style="color: rgb(255, 0, 0);">State2</span>)</span><br />end.<br /></pre>Note the function <span style="font-weight: bold;">loop(Module, State)</span><span>. Erlang variables are capitalized. <span style="font-weight: bold;">loop</span> has two parameters <span style="font-weight: bold;">Module</span> and <span style="font-weight: bold;">State</span>. I won't discuss <span style="font-weight: bold;">Module</span>, think of it as a file containing a group of helper functions.<br /><br /><span style="font-weight: bold;">State</span> is an immutable value containing our application's operational state.<br />First, the <span style="font-weight: bold;">receive</span> statement blocks the thread that <span style="font-weight: bold;">loop</span> is executing and awaits a message (from another thread) that contains data that matches one of the two tuples (variables enclosed in curly braces) either <span style="font-weight: bold;">{call, From, Request}</span>, or <span style="font-weight: bold;">{cast, Request}</span>.<br /><br />If the message matches <span style="font-weight: bold;">{call, From, Request}</span>, the next three lines in the code will execute. First, the <span style="font-weight: bold;">Request</span> is delegated to one of the <span style="font-weight: bold;">handle_call</span> helper function in <span style="font-weight: bold;">Module</span>. Note that the immutable State is passed along. The updated state is returned to variable <span style="font-weight: bold;">State2</span>.<br /><br />Next, the calling thread identified by <span style="font-weight: bold;">From</span> is messaged (<span style="font-weight: bold;">!</span>) with data <span style="font-weight: bold;">{Module, Result}</span>.<br /><br />Finally, <span style="font-weight: bold;">loop</span> is called for its next iteration with the updated state present in the <span style="font-weight: bold;">State2</span> variable.<br /><br />It is important to note that the above code fragment is not pseudo-code. The code demonstrates sending and receiving messages between threads and how messages are used to share state.<br /><br />At the top of this post I mentioned that an application's threads share common state. Erlang threads do not directly share common state but must share state as immutable variables passed using messages. Erlang calls threads "processes" because they do not share state.<br /><br />Eureka! - Erlang's secret sauce isn't very secret, just saucy. <span style="font-weight: bold;">State is maintained via very simple and explicit operations that are designed to scale up into large, robust systems.</span> The code fragment did not show any detail about State/State2 and how State was updated (e.g. inside the <span style="font-weight: bold;">Module:handle_call</span> function).<br /><br />Note that Erlang doesn't remove the need to manage state, in fact, state within a Erlang application may be very complex if it uses multiple "processes" to handle computations.<br /><br />Erlang allows us to use its full toolkit to simply, safely and explicitly work with the data used to maintain the operational state of a software application.<br /></span>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com2tag:blogger.com,1999:blog-10244190.post-6448527748119992462007-12-04T10:27:00.001-05:002007-12-04T23:14:24.654-05:00State Stinks! Part II<a href="http://http//mikepetry.blogspot.com/2007/12/state-stinks.html">State Stinks!</a> alright. My previous rant should have sufficed but I haven't succeeded in getting problems with state off of my chest! What could have I forgotten that would require its own posting! I had forgotten to mention the most worrisome of all forms of state - a form of state that I will refer to as uber-state.<br /><br />To recap my <a href="http://mikepetry.blogspot.com/2007/12/state-stinks.html">previous post</a>, I tried to illustrate how much of a software developer's life is spent managing and maintaining state. I also pointed out that many of our problems are self-inflicted, we trade a few gray hairs to optimize performance - some times just out of habit. All of these problems are intensified in multi-threaded applications. In short, state maintenance limits what we can accomplish with multi-threaded applications.<br /><br />Uber-State - What we need is a supreme form of state that lives above normal application-maintained state. Some ancient guy said "give me a long enough pole and a fulcrum out in space and I can move the world" (or something to that effect). What we need is a fulcrum out in space. Easy enough, our application's supreme entity is the operating system. What we need is state that is managed by the OS that we can use as leverage points to assert control of our application. Right? Well this is what we current use to wrangle multi-threaded applications. Operating systems provide objects that we can use to synchronize thread operations and they work as designed.<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7BAP_W8Mqyo/R1Ykk9jfS6I/AAAAAAAAAAM/OBhj6WJlnq4/s1600-h/Finite_state_machine_example_with_comments.gif"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 106px; height: 180px;" src="http://3.bp.blogspot.com/_7BAP_W8Mqyo/R1Ykk9jfS6I/AAAAAAAAAAM/OBhj6WJlnq4/s200/Finite_state_machine_example_with_comments.gif" alt="" id="BLOGGER_PHOTO_ID_5140336242107304866" border="0" /></a><br />But even Uber-State Stinks! - I am not trying to be difficult here! Let me explain. Operating System thread synchronization objects are very simple and are typically referred to as "primitives". To really harness the power of multi-threaded applications in a multi-core processor world, I need complete control of the operational state of my application. I don't just need to synchronize threads, I need to orchestrate my application. Uber-State you say (actually I said it) - horsefeathers!<br /><br /><br /><br />I had mentioned in my <a href="http://mikepetry.blogspot.com/2007/12/state-stinks.html">previous post</a> that naive OO designs typically ignore an application's operational state, treating state like a second-class citizen.<br /><br />Using OS thread synchronization primitives has the same effect. Our applications rely on big-brother to adjucate and control access to state - leaving our application's state machine in disarray.<br /><br />State doesn't have to stink - State needs to be first class citizen in our applications and work the way we would expect!<br /><br />So where am I going with this? To begin with, let it be known that I do not only mean to prescribe to others with my postings, but I also wish to elicit suggestions. A former colleague, <a href="http://www.moserware.com/">Jeff Moser,</a> suggested that I look into Erlang. I have listened to his suggestion and my findings are fuel for my next posting to be entitled "Erlang Eureka!".<br /><br /><br /><a href="http://http//mikepetry.blogspot.com/2007/12/state-stinks.html"><span style="font-weight: bold;"></span></a>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-12611870088893506012007-12-02T12:23:00.000-05:002007-12-03T17:03:18.758-05:00State Stinks!Sorry, I am not referring to your favorite institution of higher learning (although they may indeed stink). I am referring to the data utilized by the software application that you are responsible for creating and/or maintaining.<br /><br />Data is not necessarily state - Typically we think of application data as a raw material of sorts. This material must be processed, shifted, sorted, stored, transformed, accessed, and so on.<br /><br />States of existence - Another form of data is the data used to manage the operation of the software application itself. This is typically the kind of data we associate with "state". In this case, we are talking about states of existence, the dynamic behavior of the application, the software application as a machine.<br /><br />State as a second-hand citizen - I am referring to Object-Oriented design. You see it all the time, the essence of OO design is the class hierarchy. A team models the application domain into classes and begins to code. The problem - the application's engine is not considered. The code that defines the behavior of an application ends up scattered across event-handling routines, forever a breeding ground for bugs and maintenance head-aches.<br /><br />State as a Sin - This is a dramatic way of saying that we use state as an optimization. Artificial state is a good term for caches and the like. This type of state is typically misused and comes with a price, we have to maintain this state to ensure that it is fresh. Once again we have stumbled across a breeding ground for problems.<br /><br />Smart Pointers - Yeah right! I know what Scott Meyers says, "use smart pointers". I have used smart pointers for years. My question is why do I need to use smart pointers in a properly designed C++ application? Why not ensure that a C++ class wraps all dynamic memory concerns to begin with and let the copy constructors, assignment operators, and destructors do their jobs? The only reason I can think of is because of performance concerns. Due to a need to optimize, we now are responsible for managing a computers memory resources, and we do so in way that subverts the intent of C++ auto-scoping classes and destructor semantics. And we call it "smart".<br /><br />State Maintenance is Like Pounding Sand - Did I mention concurrency?<br /><br />Multi-Core Processors and Concurrency - The carrot and the stick. We humans can be stubborn. We may not always change for a carrot or a stick, but the carrot and the stick combination can be hard to refuse.<br />Carrot - Multi-Core processors. Think super computer.<br />Stick - Each core in the multi-core processor must be driven by its own thread or process.<br />Carrot + Stick = We must learn how to program computers for multiple concurrent processes/threads.<br /><br />Stop Pounding Sand - So we need to rethink application development. We will write applications that are not optimized from the perspective of one thread of execution, but will blaze across multi-core processors. We will employ programming technologies that makes state a first class citizen and sinful state a bad memory.<br /><br />This is all Good - Why? Because State Stinks! State maintenance is boring, tedious, wasteful and problematic.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-5809144604478752882007-11-21T15:00:00.000-05:002007-12-03T18:31:34.526-05:00Grr++ ..Its been about two years since I've done any serious C++ however my current interests (networking) have led me back to my software development roots.<br /><br />I have been working through W. Richard Steven's UNIX Network Programming book, coding up the examples on my laptop running Centos Linux. Not only am I becoming more knowledgeable in network programming, I am also stretching my skills into the Unix world - getting some work with gcc, g++, gdb, vim, make, as well as some shell scripting.<br /><br />Usually when learning from a computer programming book, you have the option to download the example source code, but I think it is beneficial to type in the code. This said, I only want to type in the code once! Essentially I want to create libraries that I can reuse as I work through the book and perhaps I may wind up with stuff that I can reuse in the future.<br /><br />I enjoy W. Richard Steven's classic UNIX C code, but I am compelled to wrap the C functions in C++ classes, and here I am, back in my old stomping grounds.<br /><br /><span style="font-weight: bold;">Now instead of diligently working through Steven's book, I am writing C++ wrapper classes.</span><br /><br />As I remember, this was a common theme during my past self studies. I always wanted to devise some system but I usually spent my time creating libraries and tools. The work on libraries probably bore fruit in my professional life but it seemed like I never wrote that next killer app in my spare time.<br /><br />Of course I discovered Java, C#, Perl, and Python, all of which featured powerful libraries. C++ began to drift off of the radar.<br /><br />But now I am back. So now what?<br />To begin with, I like C++. Experience has shown me that GC and reference-counted memory management schemes are not a free lunch. I have seen horrendous memory leaks in GC systems due to things like circular references.<br /><br />The ability to create full objects on the stack and then let nature's memory manager do its thing actually seems powerful.<br /><br />So where does C++ stink?<br />Smell 1 - Libraries - I want XPath and XML Dom, Regular Expressions, Data Access and Concurrency libraries at my finger tips because I have been spoiled.<br /><br />Smell 2 - Tight coupling with C. This is both a blessing and a curse. The blessing is that (dare I say) all system API's are written for the C programming language (my examples being Win32 and POSIX). Therefore C++ libraries are easily created to wrap these low-level API's.<br /><br />I think much of the bad rap C++ got was when it was used as an abstraction layer above C. Take MFC (please take it!) for example, I wonder how many developers swore off C++ after a few years with that crappy framework. I also wonder how many developers learned how to write crappy C++ code after a few years with that framework. Java seemed like a plush, polished gem after MFC.<br /><br />Also as an aside, the scripting languages did a much (I mean MUCH) better job of abstracting away the C system-level APIs.<br /><br />So here I am a few years down the road and supposedly wiser, stupidly writing crappy C++ libraries around C code and feeling like I am wasting my time (which I surely am).<br /><br />So C++ is not necessarily a bad language, but I need library and framework support.<br /><br />Probably the only downside is that I need to assemble my own toolkit. First of all there is Boost which appears to take STL to the next level. There is ACE which provides an abstraction layer around the system APIs, particularly in the realm of networking and concurrency. And there are other sources of libraries, notably Apache which has C++ versions of its Xerces XML libary.<br /><br />No worries and maybe I can find some type of library to write along the way (old habits die hard)!Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com2tag:blogger.com,1999:blog-10244190.post-1149619295617955742006-06-06T14:36:00.000-04:002006-06-06T16:18:28.033-04:00I am just a metaphor (so give me a break)Sometimes people would ask me what I do for a living. My typical response would be "Software Engineer" or "Software Developer". Invariably the questioning party would give me a blank look and then say "Ooh, you are a computer programmer".<br />Yes I am a computer programmer. But lately I have been working as an architect. Now when I give "Software Architect" as my job title, I just get the blank look.<br />Architects are people that design sky-scrapers, what does this have to do with software? "Well, I am primarily concerned with the high-level design of software applications", I might say.<br />"Ooh, you are a computer programmer" comes the predictable response.<br />Yes I am a computer programmer, but now I am much more, I have become a metaphor, a figure of speech.<br />Actually I harbor no ill feelings towards the trusty metaphor. It seems like the more colorful and metaphorical the metaphor, the better. In celebration of my friend the metaphor, I think I will post the occasional blog that catalogues some of the metaphors that have served me well.<br /><br />Metaphor 1: If I am an architect, other computer programmers may be cabinet makers.<br />Cabinet makers are among the most crafty of the tradesman and thier skill is evident. One may make the claim that the cabinet maker is at the pinnacle of the building trade. But an extremely talented cabinet maker may not be equipped to design a building. Some software developers delight us with thier wizardry as they extract data streams from serial ports, handily twiddle bits through the ether(net) and such. But when they scale up there endeavours it becomes a mess - spaghetti wizardry. Unstructured, insane but yet genius.<br />I shall now metaphorically refer to these developers as cabinet makers. As an architect, it is my job to scope out where the cabinets need to go. I like to dabble with the occasional cabinet, but as an architect I am increasing focusing my efforts to the big picture.<br /><br />Metaphor 2: Software is a machine.<br />Many times you hear about how the software industry is immature because of quality issues. Mechanical systems off-loaded complexity to electronics and electronics off-loaded complexity to software. When we find another place to dump this complexity, then the software industry will become mature. The point is that software is a very efficient way to create a very complex machine. This machine could be created in hardware, imagine Babbage with a CNC Mill/Lathe! If software statements where represented using moving, mechanical parts, a software app would approach the complexity of an aircraft carrier.<br />So a software app is a very complex machine that is comprised of multiple moving parts.<br />Lets plan accordingly.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-1128359717938973622005-10-03T10:51:00.000-05:002005-12-23T12:39:42.673-05:00Bruce on Threading TerminologyBruce Eckel posted on <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=129349">Threading Terminology</a> over at artima.com. The beauty of this post is following along with Bruce as he intuitively wrestles with Java 1.5 threads as a service to his <u>Thinking in Java</u> readers. As with most popular blogs, the discussion that follows is interesting. A common thought is that despite the power and clarity of the Java 1.5 threading model, the same pitfalls remain.<br /><br /><strong>Multithreaded Applications: asynchrony - a blessing or curse?</strong><br /><strong></strong><br />We have the power to create a new thread in our code. We create a thread and send it on a mission. Our new thread is J.E.B. Stuart and his task is to find the Union's flank and then to report immediately. Meanwhile the main thread can continue its march towards Washington. At this point, we have two elements operating asynchronously, but eventually the main thread may have to wait for Stuart's report. At this point the strategy begings to unravel, the main thread has to halt its advance and await notification from Stuart.<br />The wait proceeds, now the main thread may have to choose between retiring from the field or proceeding on to battle. One problem is that the main thread still has a responsibilty to Stuart. Stuart cannot be left orphaned precariously near the Union's lines. In any case Stuart must be ordered to terminate his patrol.<br />The essence of multithreaded apps is indepence and asynchrony, yet the reality is that lines of communication must be maintained between threads and protocols must be established to handle the situations that will arise.<br /><br /><strong>Synchrony - the beating heart of time</strong><br /><strong></strong><br />Many of the elements of a threading service are synchronization objects such as Mutexes, Semaphores, Critical Sections, Signals, Events, and Monitors. With a few simple lines of code, we loose a fury upon the world, and then write reams of code trying to control this bastard child. Ultimately the synchronity of single threadedness is revealed as more powerful than the asynchronous multithread. It is the only way to subdue the creature.<br /><br /><strong>Multiprocess or Multithread</strong><br /><br />Multithreaded programs have been identfied as more performant and more scalable than their multiprocess counterparts. A good example is process spawning web-servers, such as CGI servers, vs. threaded servers such as Mod Perl. One very brilliant blogger whose URL I cannot recall (I will update this blog when I remember), suggests that multiprocess is far more robust than multithreaded. I especially enjoyed his term for hard-to-find multithreaded bugs - "heisen bugs".<br />However, if one process is dependent on the state of another process, synchronicity issues will exist. What about multimachine distributed systems and such? Message Queue applications are used to synch up the constituents.<br /><br /><strong>In conclusion - good, bad, indifferent or just reality.</strong><br /><br />It is goodness that programming languages and platforms continue to put a fine point on thier support for multithreaded programs. Yet multithreaded support needs to exist in the viscera of applications that need threads. It is not a problem for language designers, it is a problem for application designers. It is a problem for <strong>very experienced</strong> application designers.<br /><br /><br /><strong></strong>Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-1120253883632063992005-07-01T16:25:00.000-05:002005-08-12T20:44:30.596-05:00Optimization Patterns<em>This following is a reply to the post <a href="http://www.javajunkies.org/index.pl?lastnode_id=4371&node_id=4180">The Limits of the MVC Design Pattern.</a> </em><br />It seems like the "Limit of the MVC Design Pattern" is that it defies optimization.<br />It is no surprise that it would be difficult to optimize a general solution such as an architectural framework or design pattern. Maybe we are stumbling across the need for "Optimization Patterns".<br />One example of an Optimization Pattern is refactoring, which is optimizing the structure of code.<br />One argument against the need to optimize, is that performance is a shrinking concern. Sometimes it seems brain-dead to continually reload data-structures instead of caching, but reloading and requerying may have no effect on the bottom line - which is the "user experience".<br />New evidence suggests that software performance may be very important. Stored data is growing at incredible rates, rates that even surpass Moore's Law. This is good news for us geeks since complex solutions involving caches, multiple threads and efficient algorithms will be in demand.<br />So MVC promotes reuse and decoupling which are good things, but what patterns promote optimization? One answer may be the Inversion of Control Pattern (IoC). Briefly, IoC is used in frameworks because it facilitates the design of configurable systems. Therefore "Optimization Patterns" really are "Flexibility Patterns". <br />Systems will have to be designed with extension and modification points to support changes not yet imagined.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-1111933544366864932005-03-27T08:34:00.000-05:002005-04-04T16:08:27.110-05:00Last night I dreamt of trees...<em>of data, trees of reality.</em><br /><br />To begin, yesterday I spent a good part of the day working on my latest project which combines learning Python and brushing up on some basic data structures, such as Binary Trees. Maybe "brushing up" isn't a good way to describe my intentions. I am not satisfied with knowing that I can code up a Binary Tree whenever I care to. What I am looking for is a visceral, deep, intuitive understanding.<br />I coded up an AVL Tree which is a balanced Binary Tree. In order to test the tree, I had to come up with some specific values to insert and then determine the resulting tree. This work was done, not on a computer, but with pen and paper. After several hours of sketching trees, I was suddenly struck with the limitation of the Binary Tree. As the tree is traversed from root to leaf, branches of the tree are eliminated from consideration. All that matters are the options that lay ahead. This is what makes Binary Trees so powerful for searching, but not so great for modeling activities. In real life, branches that connect nodes of existence come and go, seemingly at random. Nodes are connected and removed due to past-events and probablities. We seem to have the ability to skip around the tree of reality, to teleport amongst its various nodes.<br />But reality is a Binary Tree. Consider the effect of time on reality.<br />Time delimits and defines reality. Choices are made, paths are taken. We don't have to concern ourselves with options on paths we are not on. Reality is a Binary Tree, could you imagine otherwise? The choices we make resolve to a path, which in turn resolves to a line. The time-line.<br />I looked at the smallest of Binary Trees, three nodes, one of which was the parent with its two children, and saw infinite possibilities. I looked at a main branch of a large tree and, even with all of its sub-trees to consider, all that came to mind was the removal of possibility, the refinement of the real.<br />So I went to sleep last night and I dreamt of trees. Elements of life, connected by branches now seen.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com1tag:blogger.com,1999:blog-10244190.post-1109772636497600192005-03-02T08:32:00.000-05:002005-03-28T00:31:06.633-05:00Temporal AnamoliesMy favorite StarTrek NG plots involve "temporal anamolies" in which the time-line is disrupted and the crew of the Enterprise is plunged into the grips of an alternate reality. In real life, temporal anamolies do exist, in multi-threaded software applications.<br />The most fundamental building-block of computer algorithms is the sequential execution of code. Code is always executed sequentially for each "unit of execution" in a computer progam. A unit of execution in a computer program is known as a thread.<br />A thread is a time-line in code, a unit of reality if you will. Maybe this is why programming with multiple threads seems so odd to me.<br />Operations on threads involve things like putting threads to sleep, which is equivalent to suspending time. Threads are "blocked" which is like having time run into a log-jam. A use for "blocking" is to make a thread wait so that it may "join" with another thread (time-line).<br />Note that other forms of reality such as state and data (which is like matter), are accessible by all threads in a program. Therefore a protocol has to exist between threads to ensure that time-lines don't corrupt matter. For example, you would not want to go back in time and marry your parent (sorry for the visual).<br />Multi-threaded programs have thier own set of common problems. One is "dead-lock" when time-lines attempt to resolve conflicts over matter access and effectively block each other out. The other problem is known as a "race-condition" which is where one time-line is dependent on the actions of another time-line.<br />I blogged about object-orientation in <a href="http://mikepetry.blogspot.com/2005/02/hammer-of-gods.html">Hammer Of The Gods</a> where I made the case that every "thing" can be classified as an object. None of the OO languages I use, effectively model threads. Instances of objects have scoped life-times. Objects are just "matter" that exist on the time-line. Threads are the time-line. Threads transcend objects.<br />I have two dark visions regarding threads. One vision is of a thread, endlessly spinning through time, its time-line eternal. All context and state of interest long-since extinguished. This happens, which is why the Windows Task Manager is a handy tool. The seconds it takes to launch the tool and shut-down the errant task are many eternities to a lost thread.<br />The other vision is nightmare involving the use of garbage-collected or reference-count-based memory management. I see a thread, drifting through an empty, irrelevant universe, holding reference to an irrelevant object. Partners in the void. Neither able to release one another from futility. Neither able to redeem the others existence.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-1109213894080390572005-02-23T20:41:00.000-05:002005-03-02T14:16:47.640-05:00Hammer Of The GodsObjects abound us. There are objects that move, objects that are fixed. Objects that reside within other objects and objects that contain other objects. Objects crawl, eat, defecate, breed and die on the surface of other objects. Objects revolve in systems around other massive objects. What about thoughts, concepts, feelings? What about light, gravity, water, the wind? We can consider these objects as well.<br />So everything <em>is-an</em> Object (reminds me of a very popular programming language). Of course the term object is just an extremely general way to <em>class</em>ify things. Software design involves the discovery and <em>class</em>ification of objects that reside within a domain, and the creation of objects that serve to frame and facilitate the domain objects.<br />Object-Oriented Design is a tool and as a tool it has a very specific function. There is an old cliche, "<em>if you have a hammer, everything looks like a nail</em>"<em>. </em>But everything is an object, therefore everything <em>is-a</em> nail, in this sense.<br />Certainly if we name an element in our domain or system, we are speaking of objects. A very powerful naming technique is the use of metaphor. Successful metaphors surround the geekosystem. We don't invent new words, we <em>overload</em> the ones we already have. Words like 'file', 'folder', 'menu', 'icon'. Objects, every one of them. We even overload verbs, for example, 'browse', 'click', 'surf'. One new verb that comes to mind is to 'google'. One 'googles' when at Google. Google is a service (metaphorically speaking), a service is an object.<br />'Naming' and the use of metaphor is a very important capability for software developers. I think we tend to underestimate the power of well-crafted names and metaphors. The word 'object', with regards to software development, is a great name. The term was coined in the 1960s and is still effective, relevant, and a source of mystery.<br />The power in the name 'object' comes from the abstract, vague and nebulous nature of the name. By under-specifying what an object is, by allowing the term 'object' to breath, we give 'objects' great capability. So lets go the whole nine-yards and say that everything is an object. Its true anyway.<br />Its important to be able to live in the abstract, but every now and again we need to touch concrete. The typical way to teach OO is to provide concrete examples and to provide simple tenets such as 'an object is data and the methods that work with the data'. Objects live along side electron-clouds and Heisenberg. Too much concrete - too much uncertainty. I think most of the break-throughs in OO came from the early pioneers (SmallTalkers) who didn't have rules and concrete. They just created. And speaking of clouds, kind of makes you wonder if Grady Booch was on to something with his form of OO design notation. At least he gave Heisenberg his due.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-1108567176759705992005-02-16T09:01:00.000-05:002005-02-18T14:13:30.483-05:00Graphs and the Space-Time ContinuumI was given a small hammer when I was just a wee lad. With hammer in hand, I wandered my home searching for something to fix. Later, my father had to retrace my steps with wood-filler and paint, repairing various door-frames, sills and mouldings. I don't do much with hammers anymore, the tools of my trade are software technologies. When I learn something new, its like getting a new tool and I go in search of an application. This approach is little non-optimal but it is how I learn. My latest interest are the data-structures known as graphs. Like most software engineers, I learned about graphs in school. Graphs are connected networks of nodes. One of the key points is that the connectivity of nodes is established by having nodes refer to other nodes.<br />I have never worked with anyone who has ever had the need to create a graph, including the Binary Search Tree. Today, most languages have built-in data-structures such as lists, hash-tables, and vectors. Data is modeled and stored in a database. It is easy to consign the graph to the realm of academia and the exotic. As others discount the graph, I may find a way to use it to my advantage, to make it a permanent part of my toolkit.<br />To begin, I close my textbook and strap myself into my rocket and launch to low earth orbit. Now adrift in vacuum, away from code examples and technical detail, I once again view the graph. Ignoring the obvious, such as computer networks, air travel routes, and state-machines, what do I see? <strong>Anything that interacts with anything probably is a graph. </strong>One of my favorite tools is Object-Oriented design. Interacting objects are more exciting to me than functional flow. <strong>Interacting objects are nothing more than nodes in a graph.</strong><br />Can I enhance my ability to model the world with OO by thinking in terms of graphs? Will graphs add a new perspective to my ability to analyze?<br />Next I dock with my star-ship and warp out of the solar-system. I subject the graph to various experiments. One thing to note is that a graph can exist in three dimensions. Perfect examples are Tinker Toys and Connects. But 3-D graphs can be squashed flat and still exist in 2-D. Therefore it is easy to surmise that graphs of many dimensions exist and can be represented in just two dimensions. <strong>Infinity in two dimensions.</strong><br />Now I launch the graph at the event-horizon of a black-hole. As the graph compresses, the intent of each node become distorted. Yet the relationships remain. The differences between each node becomes less as they merge to a singularity and become one. One node associated with itself. With trepidation, I leave the void, with the memories of that brave graph emblazened in my mind. I return home, reopen my textbook and begin my journey anew.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0tag:blogger.com,1999:blog-10244190.post-1106867321443859042005-01-27T17:40:00.000-05:002005-03-01T12:04:19.006-05:00Techie or ManagerI drove into work the other day listening to meditative incantations. As I drove, I tried to see the world as a child. Water-towers became docked space ships and streetlights become uni-podded aliens attempting to disintegrate me. Beside the fact that I am weird, why would I do this? No I didn't smoke anything. Its an exercise I like to do when I am doing software design. I like to keep imaginitive, and open. This is important because software is soft. Software developers create thier own realities. Maybe a little insanity is good for those whose create alternate realities.<br />Anyway, I arrived at work, mentally prepared, but not for what was waiting for me. A note summons me to my bosses office - kind of formal and all. I immediately itemized all my misdeeds of the past week or so. Nothing that I couldn't defend, or so I thought. Well to cut to the chase, I was promoted, to Lead Software Engineer.<br />My heart sank. To begin with, my mentor, leader, and collaborator of 6 years was moving on. He was the interpersonal hub and arbitrator between the disparate elements of the project. He also had to do all the crappy admin stuff. He never really got to do much software design or code. I sort of led the software design and coding efforts as one of the project's Alpha Geeks.<br />As I thought about it I realized that I really had it made. I get to play with computers for a living. I get to devise, conceptualize and realize. I don't wanna be no manager. I am already living my dream. So I said 'No'. I told my manager that developing software is my passion and I meant it. You can't argue with that. I also mentioned that she could count on me while transitioning in a new Lead.<br />My heart rose like the mid-day sun.<br />I had made the right choice.Mike Petryhttp://www.blogger.com/profile/00900707625184132791noreply@blogger.com0