Custom %ListOfDataTypes

Hi community.

I want to create a custom list inheriting %ListOfDatatypes in order to implement some methods like Map, Filter, ForEach, but I have some questions.
Just to mention, I have already read the topic about DeclarativeCOS.

When I define a property like following, caché uses %Collection.ListOfDT or %Collection.ListOfObj, as it's shown in the documentation

Property TestListString As list Of %String;

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_propcoll#GOBJ_propcoll_def.

So if I create a new instance of my class, or create from a JSON, those properties will not have my custom functions

I had this idea to get this done and it seems to work, but I'm not sure if this could cause any problem since it sets ^oddCOM

Class tests.Lists Extends %RegisteredObject
{

Parameter ListOfObjectClass = "tests.collection.ListOfObj";

Parameter ListOfDatatypeClass = "tests.collection.ListOfDT";

Property TestListObject As list Of %ZEN.proxyObject;

Property TestListString As list Of %String;

ClassMethod %ChangeListOfProperty() As %Status [ CodeMode = objectgenerator, ServerOnly = 1 ]
{
    Try{
        Set className = %class.Name
        For i=1:1:%class.Properties.Count(){
            Set prop = %class.Properties.GetAt(i)
            Continue:(prop.Collection '= "list")
            Set propName = prop.Name
            $$$comMemberKeyGetLO(type,origin,className,$$$cCLASSproperty,propName,$$$cPROPtype)
            If $$$classIsDataType(type){
                $$$comMemberKeySet(className,$$$cCLASSproperty,propName,$$$cPROPruntimetype,..#ListOfDatatypeClass)
            }Else{
                $$$comMemberKeySet(className,$$$cCLASSproperty,propName,$$$cPROPruntimetype,..#ListOfObjectClass)
            }            
        }
    } Catch ex {
        Return $SYSTEM.Status.Error(5001, ex.DisplayString())
    }
    Return $$$OK
}

}
 

Do anyone know if there is a better approach to get this working? My Caché version is 2015.2.1
I attached in the topic the whole package of my tests. tests.lists.zip

Thank you all.

  • 0
  • 0
  • 138
  • 0
  • 1

Answers

As you have already discovered, there is no opportunity for a user to change the collection type class. That class is determined by the compiler when processing LIST and ARRAY keywords. This assignment, indicated by the compile-only keyword of RUNTIMETYPE, occurs during inheritance resolution and it cannot be overridden by the user. Your solution of coercing the RUNTIMETYPE using a method generator is not completely correct even though the runtime behavior seems correct. The problem with using the method generator is that is runs late in the compile cycle and the modification you make will not be processed by compiler actions that have already completed by this time. Your changes - simple addition of new methods - will probably work as you expect as long as those methods don't require any additional compiler processing. 

A user has only two ways to add methods to a class member - property in this case. The first and easiest is through the use of the declared type class. Internally this is cPROPtype (you have already discovered this I'm sure ;) ). Methods inherited from the datatype class (actually these are "datatype generator" classes) are combined with the property to produce the datatype implementation. These methods are typically not instance methods of the containing class but one could say they are instance methods of the generated datatype.

The second way for a user to inject methods into a class member runtime is by overriding the member super class. In the old days this was referred to as the property class but that definition is expanded to include other member types such as query, index, and so on. Methods inherited by a member from the member supertype class are typically instance methods of the containing class. For a property these methods include Get and Set. There are two keywords that a user can define - MEMBERSUPER and PROPERTYCLASS.

Both mechanisms for adding methods to a member's runtime produce what we call "composite methods". That doesn't really mean anything other than the name of the method which is composed of the member name and the inherited method name. There is an internal difference but that has little impact on runtime behavior. Composite methods look very much like normal methods but sometimes confuse users because it seems there should be a dot in the middle! For example, direct invocation of the Name property's Set method is

    do oref.NameSet(value)

This could be confusing as it seems logical to use

    do oref.Name.Set(value)

It is possible for a user to define a property super class containing method generators that will produce composite member methods.  This is not a simple process but it can be done. If this interests you then I can provide more details.

What I would recommend is an instantiable helper class. You can construct an instance of the helper class, passing in the collection instance. Methods in the helper class then operate on that instance. This is the model we use for iterators operating on instances of %Library.DynamicAbstractObject.