Introduction to Programming in ATS: | ||
---|---|---|
<<< Previous | Abstract Views and Viewtypes | Next >>> |
Objects in the physical world are conspicuously linear: They cannot be created from nothing or simply go vanished by turning into nothing. Thus, it is only natural to assign linear types to values that represent physical objects. I choose the name simple linear object here to refer to a linear value representing an object of some sort that does not contain built-in mechanism for supporting inheritance.
Let us first introduce a boxed abstract viewtype as follows for simple linear objects:
Given a viewtype VT, sobjptr(VT) is essentially meant for a pointer to some memory location L where a value of the viewtype VT is stored. The following function template sobjptr_new and function sobjptr_free are for creating and destroying (i.e., freeing) simple linear objects, respectively:
The abstract viewtype sobjptr can be given the following definition:
assume sobjptr (a:viewt@ype) = [l:addr] @{ atview= a @ l, gcview= free_gc_v (a?, l), ptr= ptr l } // end of [sobjptr] |
Subsequently, sobjptr_new and sobjptr_free can be implemented as follows:
implement{a} sobjptr_new () = let val (pfgc, pfat | p) = ptr_alloc<a> () in @{ atview= pfat, gcview= pfgc, ptr= p } end // end of [sobjptr_new] implement sobjptr_free {a} (pobj) = ptr_free (pobj.gcview, pobj.atview | pobj.ptr) // end of [sobjptr_free] |
Clearly, a simple object needs to be initialized before it is of any use. This can be done by calling the following function sobjptr_init:
extern fun sobjptr_init {a:viewt@ype} (pobj: !sobjptr (a?) >> sobjptr (a), f: (&a? >> a) -> void): void // end of [sobjptr_init] implement sobjptr_init (pobj, f) = let prval pfat = pobj.atview val () = f !(pobj.ptr) prval () = pobj.atview := pfat in // nothing end // end of [sobjptr_init] |
As a simple object may contain resources, it needs to be cleared out before it is allowed to be freed. This can be done by calling the following function sobjptr_clear:
extern fun sobjptr_clear {a:viewt@ype} ( x: !sobjptr (a) >> sobjptr (a?), f: (&a >> a?) -> void ) : void // end of [sobjptr_clear] implement sobjptr_clear (pobj, f) = let prval pfat = pobj.atview val () = f !(pobj.ptr) prval () = pobj.atview := pfat in // nothing end // end of [sobjptr_clear] |
Note that each type T (of the sort t@ype) is a subtype of T?, implying that sobjptr(T) is a subtype of sobjptr(T?) (as sobjptr is co-variant). Therefore, sobjptr_free can be called directly on a value of the type sobjptr(T) without need to call sobjptr_clear on the value first.
Let us now see a concrete example of simple linear object. Suppose that a timer (that is, stopwatch) is wanted to measure time (of some sort). Following is a natural interface for functions creating, destroying and manipulating timer objects:
absviewtype timerObj fun timerObj_new (): timerObj fun timerObj_free (x: timerObj): void fun timerObj_start (x: !timerObj): void fun timerObj_finish (x: !timerObj): void fun timerObj_pause (x: !timerObj): void fun timerObj_resume (x: !timerObj): void fun timerObj_get_ntick (x: !timerObj): uint fun timerObj_reset (x: !timerObj): void |
The (flat) record type timer_struct is defined as follows to represent the state of a timer object:
typedef timer_struct = @{ started= bool // the timer has started , running= bool // the timer is running // the tick number recorded , ntick_beg= uint // when the timer was turned on the last time , ntick_acc= uint // the number of accumulated ticks } // end of [timer_struct] |
The abstract viewtype timerObj can then be mapped to sobjptr(timer_struct):
The functions timerObj_new and timerObj_free can now be given the following implementation:
implement timerObj_new () = let typedef T = timer_struct fn f ( x: &T? >> T ) : void = { val () = x.started := false val () = x.running := false val () = x.ntick_beg := 0u // unsigned val () = x.ntick_acc := 0u // unsigned } // end of [f] val pobj = sobjptr_new<T> () in sobjptr_init {T} (pobj, f); pobj end // end of [timerObj_new] implement timerObj_free (pobj) = sobjptr_free (pobj) |
For brevity, I omit the code implementing the other functions on timer objects, which the interested reader can find on-line together with some additional testing code.
<<< Previous | Home | Next >>> |
Abstract Views and Viewtypes | Up | Example: Implementing an Array-Based Circular Buffer |