The second alpha of the Minnow programming language was released recently. Minnow is a new programming language designed around the idea of actors and using a new datastructure building paradigm called ligomorphism.
While programming in Minnow every object is an actor which behaves like it is an independent thread. An actor may or may not actually be tied to an operating system thread at any given moment depending on how many CPU cores you have available. By managing its own notion of timeslices it is able to not overload the OS with too many threads while simultaneously letting you take advantage of every hardware thread available. All function calls between actors are asynchronous messages. Also, the programming language compiles to C, which then compiles to native code, so there is very little runtime overhead.
Using minnow (SVN, and working with the author) I was able to put together the following bit of code which generates a rendering of the mandelbrot fractal set.
The magic with this code, and Minnow itself, is that the code below can (in theory) automatically take advantage of up to 17 CPU’s because of how the work is divided in to images pieces. All without any threading constructs in the code, no locking, no thread safe queues, etc.
Of course, the system is still young and some bugs are still being worked out.
(Full disclosure: The author of minnow and I are cousins)
extern def log(x : double) : double // Make the c library log and abs functions available
extern def fabs(x : double) : double
feature Color
r : double
g : double
b : double
def Color(t_r:double, t_g:double, t_b:double)
r = t_r
g = t_g
b = t_b
end
def Color()
r = (0.0)
g = (0.0)
b = (0.0)
end
end
feature Point
x : int
y : int
def Point(t_x:int, t_y:int)
x = t_x
y = t_y
end
end
feature Pixel
color : Color
point : Point
def Pixel(t_point:Point, t_color:Color)
color = t_color
point = t_point
end
end
//technically this should be an 'isolated actor' because of the use of SDL.delay
isolated actor SDLWriter
m_width : int
m_height : int
m_pixels : int
screen : SDL.Surface
surface : SDL.Surface
def SDLWriter(t_width:int, t_height:int)
m_width = t_width
m_height = t_height
m_pixels = 0
screen = new SDL.Surface()
surface = new SDL.Surface()
screen.create_main_window(t_width, t_height, 32)
surface.create_surface(t_width, t_height, 32)
end
action AddPixel(p:Pixel)
surface.draw_pixel((p.color.r * 255.0).to_int(), (p.color.g * 255.0).to_int(), (p.color.b * 255.0).to_int(), p.point.x, p.point.y)
m_pixels += 1
SDL.clear_events()
if ((m_pixels/32)*32 == m_pixels)
screen.blit_surface(surface)
end
if (m_pixels == (m_width * m_height))
screen.blit_surface(surface)
while(true)
SDL.delay(100)
SDL.clear_events()
end
end
end
end
actor PixelCalc
m_tl : Point
m_br : Point
m_sdl : SDLWriter
m_width : int
m_height : int
m_scale : double
m_x : double
m_y : double
def PixelCalc(tl:Point, br:Point, sdl:SDLWriter, t_width:int, t_height:int)
m_tl = tl
m_br = br
m_sdl = sdl
m_width = t_width
m_height = t_height
m_x = 0.001643721971153
m_y = -0.822467633298876
m_scale = 0.0000000001
end
// This is where the color value of a particular pixel in the set is generated
def GetColor(xcoord:int, ycoord:int) : Color
xscaled:double = xcoord.to_double()/(m_width.to_double()/m_scale) + (m_x - (m_scale/2.0 ))
yscaled:double = ycoord.to_double()/(m_height.to_double()/m_scale) + (m_y - (m_scale/2.0 ))
x:double = xscaled
y:double = yscaled
iteration:int = 0
max_iteration:int = 2000
stop_iteration:int = max_iteration
while ( iteration < stop_iteration )
if ((x*x) + (y*y) > (2.0*2.0) && stop_iteration == max_iteration)
stop_iteration = iteration + 5
end
xtemp:double = (x*x) - (y*y) + xscaled
y = (2.0*x*y) + yscaled
x = xtemp
iteration += 1
end
if (iteration == max_iteration)
return new Color(0.0,0.0,0.0)
else
value:double = ((iteration + 1).to_double() - (log(log(fabs(x * y))))/log(2.0))
red:double = 0.0
green:double = 0.0
blue:double = 0.0
colorval:int = (value * 10.0).to_int()
if (colorval < 256)
red = (colorval.to_double())/256.0
else
colorband:int = ((colorval - 256) - (((colorval - 256) / 1024) * 1024))/256
mod256:int = colorval - ((colorval / 256) * 256)
if (colorband == 0)
red = 1.0
green = mod256.to_double() / 255.0
blue = 0.0
elseif (colorband == 1)
red = 1.0
green = 1.0
blue = mod256.to_double() / 255.0
elseif (colorband == 2)
red = 1.0
green = 1.0
blue = 256.0 - (mod256.to_double()/255.0)
else
red = 1.0
green = 256.0 - (mod256.to_double()/255.0)
blue = 0.0
end
end
return new Color(red, green, blue)
end
end
action calc()
y:int = m_tl.y
while (y < m_br.y)
x:int = m_tl.x
while (x < m_br.x)
m_sdl::AddPixel(new Pixel(new Point(x, y), GetColor(x,y)))
x += 1
end
y += 1
end
end
end
action main(args : Array[string])
w:int = 512
h:int = 512
sdlwriter:var = spawn SDLWriter(w, h)
calcs:var = new Array[PixelCalc]
x:int = 0
y:int = 0
div:int = 4
while (x < w)
y = 0
while (y < h)
p:var = spawn PixelCalc(new Point(x, y), new Point(x+(w/div), y+(h/div)), sdlwriter, w, h)
p::calc()
calcs.push(p)
y += h/div
end
x += w/div
end
end