digitalmars.D - An Idea - New data stucture for D
- kov_serg (197/197) Nov 04 2010 I want to offer to introduce new data structures into D language.
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