Theme-D

Examples

Here is the implementation of "Hello World" in Theme-D:
	    (define-proper-program hello-world

                (import (standard-library core)
                        (standard-library console-io))

                (define-main-proc (() <none> nonpure)
                    (console-display-line "Hello, world!")))
	  

The example and the test programs can be found in the Theme-D testing environment. See the Theme-D User Guide for instructions how to install it and build the programs. The programs can also be found in the Theme-D source code package . Next we define a class for rational numbers:

	    (define-class <rational>
	      (fields
	        (numerator <integer> public module)
		(denumerator <integer> public module)))
	  
The field name is followed by field type, read access, and write access. Access "module" means that the field can be accessed (read or write) in the same module where it is defined. Now we can create rational number objects with commands like
	      (make <rational> 2 3)
	  
Now we define a module implementing rational numbers. A Theme-D module consists of an interface and a body. The interface declares the variables exported by the module and the body contains the definitions of the variables. However, exported classes and types are defined in the interface. Here is the example interface:
	    (define-interface rational

	      (import (standard-library core))

	      (define-class <rational>
	        (fields
	          (numerator <integer> public module)
		  (denumerator <integer> public module)))

	      (declare rational+ (:simple-proc
	        (<rational> <rational>) <rational> pure))
		  
	      (declare rational- (:simple-proc
	        (<rational> <rational>) <rational> pure)))
	  
and here the body:
	    (define-body rational

	      (define-simple-proc rational+ (((rat1 <rational>)
	          (rat2 <rational>)) <rational> pure)
		(make <rational>
		  (+ (* (field-ref rat1 'numerator) (field-ref rat2 'denominator))
		     (* (field-ref rat1 'denominator) (field-ref rat2 'numerator)))
		  (* (field-ref rat1 'denominator) (field-ref rat2 'denominator))))
		  
	      (define-simple-proc rational- (((rat1 <rational>)
	          (rat2 <rational>)) <rational> pure)
		(make <rational>
		  (- (* (field-ref rat1 'numerator) (field-ref rat2 'denominator))
		     (* (field-ref rat1 'denominator) (field-ref rat2 'numerator)))
		  (* (field-ref rat1 'denominator) (field-ref rat2 'denominator)))))
	  
Classes may also inherit from other classes. If class <a> inherits class <b> objects of class <a> contain all the fields of class <b>, too. Here is an example:
	    (define-class <object-with-id>
	      (fields
	        (str-id <string> public module)))

	    (define-class <widget>
	      (superclass <object-with-id>)
	      (fields
	        (i-x <integer> public module)
	        (i-y <integer> public module)))
	  
Object construction may involve more complex operations than just setting the field values:
	    (define-class <rectangle>
	      (construct ((_x1 <integer>) (_y1 <integer>)
	                  (_x2 <integer>) (_y2 <integer>)))
	      (fields
	        (x1 <integer> public module _x1)
	        (y1 <integer> public module _y1)
	        (x2 <integer> public module _x2)
	        (y2 <integer> public module _y2)
	        (width <integer> public module
	          (+ (- _x2 _x1) 1))
	        (height <integer> public module
	          (+ (- _y2 _y1) 1))))
	  
Now the arguments defined by the construct statement are passed to the make expression when a <rectangle> object is created. Parametrized classes are classes taking type parameters. Here we define a parametrized class for trees:
	    (define-param-class :tree
	      (parameters %element)
	      (fields
	        (node-value %element public module)
	        (subtrees (:uniform-list (:tree %element)))))
	  
Now we can create trees by commands like
	    (make (:tree <string>) "abc"
	      (list (make (:tree <string>) "def" null)))
	  
Generic procedures are collections of simple and parametrized procedures called methods. When a generic procedure is called the call is routed to the method that matches the argument types best. Considering rational numbers we may implement methods + and - for them:
	    (add-method + rational+)
	    (add-method - rational-)
	  
Theme-D has a keywords match-type and match-type-strong, which are used to branch on the basis of expression type. Here is the implementation of procedure map1, which uses this feature:
	    (define-param-proc map1 (%argtype %result-type)
                (((proc (:procedure (%argtype) %result-type pure))
  	          (lst (:uniform-list %argtype)))
                 (:uniform-list %result-type)
                 pure)
              (match-type-strong lst
                ((<null>) null)
                ((lst1 (:nonempty-uniform-list %argtype))
                 (cons (proc (car lst1))
	               (map1 proc (cdr lst1))))))
	  
Iterating a list can be performed easily with form iterate-list:
	    (let ((l '(apples oranges cherries pineapples)))
	      (iterate-list (x <symbol> l)
	        (console-display " * ")
	        (console-display-line x)))
	  

Back to the main page

Last updated: September 12, 2024

Copyright (C) 2020-2021 Tommi Höynälänmaa

tommi.hoynalanmaa@REMOVETHISiki.fi