www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - An Idea - New data stucture for D

I want to offer to introduce new data structures into D language.
It should help to write user interfaces more easly and faster.
Acording design goals 5 "Make doing things the right way easier than the wrong
way."
These stuctures very simple, but I belive them could make a small revolution.

The idea is simple and based on MVC conception. Now I have to use it meta
language to compile it to language used.

The first type of structures let call it "model". It looks like simple stucture.
But the main difference from stucture in it's additionl behaviour it should
have.

Let's look on implementation in pseudo language

NotifyDelegate - is a pointer to function without parameters

struct Notifier // can notify list of subsctibers
	Modify()    // mark modified
	Lock()      // lock notification (recursive)
	Unlock()    // unlock notification
	Subscribe(NotifyDelegate)   // subscribe listner and call it first time
	Unsubscribe(NotifyDelegate) // unsubscribe listner
	AddChild(Notifier)          // subscribe child(group) listner
	RemoveChild(Notifier)       // unsubscribe child

This class contains list of subscribers and subnotifiers. Lock/Unlock can delay
notification until lock_level=0 for all subscribers and subnotifiers.
This class is very flexible and allows to have very complex notification
topology.
More over then it call notify subscribers it goes to lock_level=-1. It means
no Lock/Unlock allowed. It means read only mode. This mode is very usefull
to avoiding bugs. Because views that use model in notify must have read only
access.

struct Model
	Notifier rootNotifier;  -- notify any change of model and submodels if any
	Notifier scopeNotifier; -- notify any change of scope fields

Base interface it has only to notification source scopeNotifier to notify
change in model fields. And rootNotifier to notify any change of model.
It means if field chage or if submodel change it should notify all subscriber
about it (when lock_level drops to zero).

Typical declaration in speudocode:

struct Model1 : Model
	private type1 _FieldName1=default_value1;
	property type1 FieldName1 {
		get { return _FieldName1; }
		set { if (_FieldName1!=value) { _Name1=value; scopeNotifier.Modify(); }
	}
	...
	private typeN _NameN=default_valueN;
	property typeN NameN {
		get { return _NameN; }
		set { if (_NameN!=value) { _Name1=value; scopeNotifier.Modify(); }
	}
	SubModel1 submodel1;
	...
	SubModelN submodelN;
	ctor() { // constructor
		rootNotifier.AddChild(scopeNotifier);
		rootNotifier.AddChild(submodel1);
		...
		rootNotifier.AddChild(submodelN);
	}
	dtor() { // destructor
		rootNotifier.RemoveChild(scopeNotifier);
		rootNotifier.RemoveChild(submodel1);
		...
		rootNotifier.RemoveChild(submodelN);
	}
	assign_scope(Model1 src) {
		rootNotifier.Lock();
		FieldName1=src.FieldName1;
		...
		FieldNameN=src.FieldName1;
		rootNotifier.Unlock();
	}
	assign(Model1 src) { // this function allows to make full copy
		rootNotifier.Lock();
		assign_scope(src);
		SubModel1.Assign(src.SubModel1);
		...
		SubModelN.Assign(src.SubModelN);
		rootNotifier.Unlock();
	}

Here a lot of dead-level code. I offer to introduce new struct type to have same
result with smaller line of code.

struct_model Model1
	type1 FieldName1=def_value1;
	...
	typeN FieldNameN=def_valueN;
	SumModel1 submodel1
	...
	SumModelN submodelN

All other method should be generated by compiler to have save functional.
So we can simple declare field and submodels and use it. Easy read, easy modify.

Typical use:

void change_model_fn1(Model m) { // simple change
	m.field1=2;
}
void change_model_fn2(Model m) { // group change
	ModelLocker locker(m);
	m.field2="text";
	change_model_fn1(m);
	m.field3=3.14;
}
void change_model_fn3(Model m) {
	ModelLocker locker(m);
	m.field4=m.field4+1;
	change_model_fn2(m); // reuse
	m.field1=2;
}

One one possible views for model:

struct SomeView : SomeVisualControl {
	Model m;
	void attach(Model model) {
		this.model=model;
		model.rootNotifier.Subscribe(onModelChange); // add and call it first time
		//or may be like this: model.OnChange+=onModelChange;
		//or model.OnLocalChange+=onSimpleChange;
	}
	void dettach() {
		model.rootNotifier.Unsubscribe(onModelChage); // remove
		// model.OnChange-=onModelChange;
	}
	void onModelChange() { // update view
		bool en=m.field1>0;
		control2.enable=en;
		control1.enable=en;
		control1.text=m.field2;
		...
	}
	...
};

I beleave this greately improve ability of language for writing user interfaces.

Design notes:
1. This structure must not be thread safe. It must be simple.
2. Multithread use could be implemented by "assign" operation and model
controller.

If we have working thread that work with model. We can subsribe on model in this
thread and synchronously make a full copy of model to passit for other threads
later.

wrok_thread
	Model1 model;
	void main() {
		here work with model
	}
	// addeditional
	Model1 model_copy;
	Mutex  mutex;
	void onModelChange() {
		ModelLocker locker(model_copy);
		lock(mutex) {
			model_copy.assign(model);
		}
	}
	void init() {
		model.rootNotifier.Subscribe(onModelChange);
	}
	void done() {
		model.rootNotifier.Unsubscribe(onModelChange);
	}
	public void getModel(Model m) {
		ModelLocker locker(m);
		lock(mutex) {
			m.assign(model_copy);
		}
	}

So we can time to time call getModel() from gui thread. This model would work as
dummy. And views can subscribe on it and show it state. Here no write access
required. But if we want to ineract with it. We need to use model controller.
Special class that can pass action we neeed into thread that work with this
model.
So we need another construction "model_controller".

This construction is greately depends on serialize of function call.
If we have controller with function of special type that did not return result,
but only do action or initiate them. Indeed only this type of functions we need.

interface Controller
	void fn1(params);
	...
	void fnN(params);

Additional structure to serialize call may look like this:

struct ControllerFnSerializer : Controller
	ThreadSafeQueue<Call> queue;
	void Execute(Controller ctrl,Call call);
	void Serialize(Call call);
	void ExecuteAll(Controller ctrl);

	void fn1(params) { Serialize(Call(fn1,params)); }
	...
	void fnN(params) { Serialize(Call(fnN,params)); }
}

So there is a need in ability to declare such function call
serializer/deserializer.
It allows to pass parameters for inter-thread inter-process and delayed calls
easy.

struct ControllerFnSerializer : dispatcher<Controller> {};

or simply

disp_interface Controller
	void fn1(params);
	...
	void fnN(params);

automatically generate interface and interface call dispatcher class.

So that is the idea.
I offer build in D language "sturct_model" and "disp_interface" or something
similair.
Syntax may vary but it should be brief, simple, clear and pictorial.

Thanks you for reading.
Nov 04 2010