User Tools

Site Tools


script:walkthrough:records

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
script:walkthrough:records [2017-03-20 22:26]
skyjake
script:walkthrough:records [2019-11-23 21:38] (current)
skyjake
Line 4: Line 4:
  
  
-  *  Creating a record. +Records are the foundation of Doomsday ScriptEach namespace and module is a record, each object is a record, and classes are records, too.
-  *  Creating variables into a record+
-  *  Creating ​subrecord. +
-  *  Checking whether members exist in record+
-  *  Built-in functions: members()subrecords(). +
-  *  Alternative ways to access members ​and subrecords. +
-  *  Having two variables reference the same record. +
-  *  Deleting members from record. +
-  *  Deleting a record pointed to by two variables. +
-  *  Serializing ​records.+
  
 +The ownership model of records is that there is always a single owner for a record, and if that owner gets destroyed, all records owned by it will get destroyed, too. Many records are owned by native code (e.g., built-in modules like ''​Core''​),​ but may still be referenced by variables in scripts.
  
 +A script variable may own a record, or simply reference one owned by someone else. When a record gets destroyed, all variables that reference it will be invalidated. In other words, records are __not__ reference-counted (like Python objects would be).
  
 +In practice, a record is a set of named variables. Some of these variables may point to other records, while others have plain data values. ​
  
 +===== Creation =====
 +
 +There are a few alternative ways to create a record:
 +  * ''​record''​ statement.
 +  * Built-in ''​Record()''​ function.
 +  * ''​record''​ expression.
 +
 +With the ''​record''​ statement, one can create one or more empty records in the local namespace. The following would initialize variables //a// and //b//, giving each a new empty owned record.
 +
 +  record a, b
 +
 +Note that the ''​record''​ statement will replace any existing variables with the same names.
 +
 +The ''​Record()''​ function makes a copy of an another record and returns an owned reference to it. Without arguments, it returns an empty owned record. This can then be assigned to a variable.
 +
 +  a = Record()
 +  b = Record(a)
 +
 +''​Record()''​ also accepts a dictionary as argument. In this case, the keys of the dictionary become variables in the newly created record.
 +
 +  Record({'​a':​ 1, '​b':​ 2})
 +
 +''​record''​ may also be used as an expression, to create a subrecord in the current scope. In this example, //myrec// is created in the local namespace. On the second line //subexp// is created inside //myrec// because it appears after the member operator.
 +
 +  (record myrec).expressionCreated = True
 +  myrec.(record subexp).alsoExpCreated = True
 +
 +The member operator ''​.''​ can be used together with assignment to create new variables inside a record. This works with both owned and non-owned record variables.
 +
 +  $ record myrec
 +  ​
 +  $ myrec.newMember = 100
 +  ​
 +  $ print len(myrec)
 +  1
 +  ​
 +  $ print myrec.newMember
 +  100
 +  ​
 +  $ print myrec
 +  newMember:​100
 +
 +Subrecords can be created with the ''​record''​ expression as seen above, or more simply using the ''​record''​ statement.
 +
 +  $ record myrec
 +  ​
 +  $ record myrec.subrec
 +  ​
 +  $ myrec.subrec.a = 1
 +  ​
 +  $ print myrec  ​
 +  subrec.a:1
 +
 +===== Members =====
 +
 +The ''​in''​ operator is used to check if members exist in a record.
 +
 +  $ print '​subrec'​ in myrec, \
 +          '​newMember'​ in myrec, \
 +          '​not-there'​ in myrec.subrec
 +  True True False
 +
 +The built-in functions ''​members()''​ and ''​subrecords()''​ return dictionaries containing the corresponding members of a record.
 +
 +  record myrec
 +  myrec.var = '​hello'​
 +  myrec.(record subrec).a = 123
 +  print ' ​  ​members:',​ members(myrec)
 +  print '​subrecords:',​ subrecords(myrec)
 +
 +Output:
 +
 +     ​members:​ { subrec: a:123, var: hello }
 +  subrecords: { subrec: a:123 }
 +
 +Members of a record can also be accessed with the ''​[]''​ operator, and via the dictionary returned by ''​members()''​ or ''​subrecords()''​.
 +
 +  print myrec['​newMember'​]
 +  print myrec['​subrec'​]
 +  print myrec['​subrec'​]['​something'​]
 +  print members(myrec)['​newMember'​]
 +
 +Members can also be created or changed using ''​[]''​. However, the key value still has to be a text string that contains no periods. (Otherwise, the member would not be accessible as usual via the member operator.)
 +
 +  myrec['​assignedElement'​] = 3000
 +
 +===== Aliasing =====
 +
 +When assigning a record to another variable, the variable references the same record but doesn'​t share ownership of it. The record can nevertheless be accessed via the reference without restrictions.
 +
 +  record myrec
 +  myrec.firstMember = 123
 +  reference = myrec
 +  reference.otherMember = 5
 +  print "​\nHere'​s myrec:"​
 +  print myrec
 +
 +Output:
 +
 +  Here's myrec:
 +  firstMember:​123
 +  otherMember:​5
 +
 +===== Deletion =====
 +
 +The ''​del''​ keyword can be used to delete individual variables.
 +
 +  del a
 +  del myrec.subrec.something
 +
 +If the variable owns a record, the record will be destroyed as well.
 +
 +  record soonDeleted
 +  reference = soonDeleted
 +  del soonDeleted
 +  try: print reference
 +  catch Error, er: print er
 +
 +Output:
 +
 +  [NullError] (in RecordValue::​verify) Value no longer references a record
 +
 +===== Serialization =====
 +
 +Records can be serialized with ''​serialize()''​.
 +
 +  record myrec
 +  myrec.a = 1
 +  print '​serialize(myrec) =', serialize(myrec)
 +  print '​deserialize(serialize(myrec)) ='
 +  print deserialize(serialize(myrec))
 +
 +Output:
 +
 +  serialize(myrec) = (Block of 29 bytes)
 +  deserialize(serialize(myrec)) =
 +  a:1
 +
 +===== Classes =====
 +
 +A class is a record that acts as the namespace containing the members of the class. Classes can define an initializer method that gets called during instantiation.
 +
 +  record MyClass()
 +      a = 'class member'​
 +      def __init__()
 +          self.b = 'local member'​
 +      end        ​
 +  end
 +  ​
 +  obj = MyClass()
 +  print obj.a
 +  print obj.b
 +
 +Output:
 +
 +  class member
 +  local member
 +
 +Note that there is nothing special about a class — it is simply a record that contains variables, just like any other record. In fact, the only special thing about class instantiation is that behavior of the member operator (''​.''​) is augmented to additionally search for identifiers in the superclasses.
 +
 +This means that, for instance, an imported module could be used as a class. ​
 +
 +Setting up the superclass relationship and calling the initializer method ''<​nowiki>​__init__</​nowiki>''​ happens when one uses the call operator ''​()''​ on a record.
 +
 +The special variable ''​self''​ is set when a function is called via the member operator, referencing the left-hand operand. This is how the initializer and other methods of the class can access the instance.
 +
 +Arguments can be passed to the initializer method.
 +
 +  record MyClass()
 +      def __init__(someVar)
 +          self.v = someVar
 +      end        ​
 +  end
 +  ​
 +  obj = MyClass('​arg'​)
 +  print obj.v
 +
 +Output:
 +
 +  arg
 +
 +==== Inheritance ====
 +
 +A subclass is a class that inherits from an another class.
 +
 +Normally member lookup proceeds to check through each superclass in order, however it is also possible to manually specify the namespace for identifier lookup. This is done with the ''<​nowiki>​-></​nowiki>''​ notation, as seen in the example below. The initializer method of Subclass calls the initializer of the parent class. Without ''<​nowiki>​MyClass-></​nowiki>'',​ the call would recursively be made to Subclass'​s own initializer.
 +
 +  record MyClass()
 +      def __init__(someVar)
 +          self.v = someVar
 +      end        ​
 +  end
 +  ​
 +  record Subclass(MyClass)
 +      def __init__()
 +          self.MyClass->​__init__('​from sub')
 +      end
 +  end
 +  ​
 +  obj = Subclass()
 +  print obj.v
 +
 +Output:
 +
 +  from sub
 +
 +The scope notation can be used sequentially. For example, if the parent class is part of another record:
 +
 +  record t.MyClass()
 +
 +Now in Subclass, the initializer needs to looked up with a sequence of scopes:
 +
 +  self.t->​MyClass->​__init__('​from sub')
 +
 +The behavior of this statement is:
 +  - Look up //self//.
 +  - The member operator ''​.''​ remembers //self// as the current context.
 +  - Look up //t//.
 +  - Within //t//, look up //​MyClass//​.
 +  - Within //​MyClass//,​ look up //<​nowiki>​__init__</​nowiki>//​.
 +  - Make a function call on //<​nowiki>​__init__</​nowiki>//​ using //self// as the context (//self// will be available in the function'​s localspace for the duration of the call).
 +
 +Note that the scope notation does not restrict the lookup to superclasses;​ one could look up any function this way, while retaining //self// as the context.
 +
 +Compare this with:
 +
 +  # This is wrong:
 +  self.t.MyClass.__init__('​from sub'​) ​
 +
 +This doesn'​t work because:
 +  * //t// is in the global namespace, so it's not found within //self//.
 +  * Even if that somehow worked, //<​nowiki>​__init__</​nowiki>//​ would use //MyClass// as the context for the function call.
script/walkthrough/records.1490041565.txt.gz · Last modified: 2017-03-20 22:26 by skyjake