Problem:  Some error numbers are not documented
          (Restorer)
Solution: Document missing error numbers (Yegappan Lakshmanan).
fixes: #18114
closes: #18135
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
		
	
		
			
				
	
	
		
			1370 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			1370 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| *vim9class.txt*	For Vim version 9.1.  Last change: 2025 Aug 27
 | |
| 
 | |
| 
 | |
| 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
 | |
| 
 | |
| 
 | |
| Vim9 classes, objects, interfaces, types and enums.		*vim9-class*
 | |
| 
 | |
| 1.  Overview			|Vim9-class-overview|
 | |
| 2.  A simple class		|Vim9-simple-class|
 | |
| 3.  Class variables and methods	|Vim9-class-member|
 | |
| 4.  Using an abstract class	|Vim9-abstract-class|
 | |
| 5.  Using an interface		|Vim9-using-interface|
 | |
| 6.  More class details		|Vim9-class|
 | |
| 7.  Type definition		|Vim9-type|
 | |
| 8.  Enum			|Vim9-enum|
 | |
| 
 | |
| 9.  Rationale
 | |
| 10. To be done later
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 1. Overview					*Vim9-class-overview*
 | |
| 
 | |
| The fancy term is "object-oriented programming".  You can find lots of study
 | |
| material on this subject.  Here we document what |Vim9| script provides,
 | |
| assuming you know the basics already.  Added are helpful hints about how to
 | |
| use this functionality effectively.  Vim9 classes and objects cannot be used
 | |
| in legacy Vim scripts and legacy functions.
 | |
| 
 | |
| The basic item is an object:
 | |
| - An object stores state.  It contains one or more variables that can each
 | |
|   have a value.
 | |
| - An object provides functions that use and manipulate its state.  These
 | |
|   functions are invoked "on the object", which is what sets it apart from the
 | |
|   traditional separation of data and code that manipulates the data.
 | |
| - An object has a well defined interface, with typed member variables and
 | |
|   methods.
 | |
| - Objects are created from a class and all objects have the same interface.
 | |
|   This does not change at runtime, it is not dynamic.
 | |
| 
 | |
| An object can only be created by a class.  A class provides:
 | |
| - A new() method, the constructor, which returns an object for the class.
 | |
|   This method is invoked on the class name: MyClass.new().
 | |
| - State shared by all objects of the class: class variables (class members).
 | |
| - A hierarchy of classes, with super-classes and sub-classes, inheritance.
 | |
| 
 | |
| An interface is used to specify properties of an object:
 | |
| - An object can declare several interfaces that it implements.
 | |
| - Different objects implementing the same interface can be used the same way.
 | |
| 
 | |
| The class hierarchy allows for single inheritance.  Otherwise interfaces are
 | |
| to be used where needed.
 | |
| 
 | |
| Class modeling ~
 | |
| 
 | |
| You can model classes any way you like.  Keep in mind what you are building,
 | |
| don't try to model the real world.  This can be confusing, especially because
 | |
| teachers use real-world objects to explain class relations and you might think
 | |
| your model should therefore reflect the real world.  It doesn't!  The model
 | |
| should match your purpose.
 | |
| 
 | |
| Keep in mind that composition (an object contains other objects) is often
 | |
| better than inheritance (an object extends another object).  Don't waste time
 | |
| trying to find the optimal class model.  Or waste time discussing whether a
 | |
| square is a rectangle or that a rectangle is a square.  It doesn't matter.
 | |
| 
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 2.  A simple class				*Vim9-simple-class*
 | |
| 
 | |
| Let's start with a simple example: a class that stores a text position (see
 | |
| below for how to do this more efficiently): >
 | |
| 
 | |
| 	class TextPosition
 | |
| 	   var lnum: number
 | |
| 	   var col: number
 | |
| 
 | |
| 	   def new(lnum: number, col: number)
 | |
| 	      this.lnum = lnum
 | |
| 	      this.col = col
 | |
| 	   enddef
 | |
| 
 | |
| 	   def SetLnum(lnum: number)
 | |
| 	      this.lnum = lnum
 | |
| 	   enddef
 | |
| 
 | |
| 	   def SetCol(col: number)
 | |
| 	      this.col = col
 | |
| 	   enddef
 | |
| 
 | |
| 	   def SetPosition(lnum: number, col: number)
 | |
| 	      this.lnum = lnum
 | |
| 	      this.col = col
 | |
| 	   enddef
 | |
| 	 endclass
 | |
| <							*object* *Object*
 | |
| You can create an object from this class with the new() method: >
 | |
| 
 | |
| 	var pos = TextPosition.new(1, 1)
 | |
| <
 | |
| The object variables "lnum" and "col" can be accessed directly: >
 | |
| 
 | |
| 	echo $'The text position is ({pos.lnum}, {pos.col})'
 | |
| <						    *E1317* *E1327* *:this*
 | |
| If you have been using other object-oriented languages you will notice that in
 | |
| Vim, within a class definition, the declared object members are consistently
 | |
| referred to with the "this." prefix.  This is different from languages like
 | |
| Java and TypeScript.  The naming convention makes the object members easy to
 | |
| spot.  Also, when a variable does not have the "this." prefix you know it is
 | |
| not an object variable.
 | |
| 								*E1411*
 | |
| From outside the class definition, access an object's methods and variables by
 | |
| using the object name followed by a dot following by the member: >
 | |
| 
 | |
| 	pos.lnum
 | |
| 	pos.SetCol(10)
 | |
| <
 | |
| 							*E1405* *E1406*
 | |
| A class name cannot be used as an expression.  A class name cannot be used in
 | |
| the left-hand-side of an assignment.
 | |
| 
 | |
| Object variable write access ~
 | |
| 						    *read-only-variable*
 | |
| Now try to change an object variable directly: >
 | |
| 
 | |
| 	pos.lnum = 9
 | |
| <							*E1335*
 | |
| This will give you an error!  That is because by default object variables can
 | |
| be read but not set.  That's why the TextPosition class provides a method for
 | |
| it: >
 | |
| 
 | |
| 	pos.SetLnum(9)
 | |
| 
 | |
| Allowing to read but not set an object variable is the most common and safest
 | |
| way.  Most often there is no problem using a value, while setting a value may
 | |
| have side effects that need to be taken care of.  In this case, the SetLnum()
 | |
| method could check if the line number is valid and either give an error or use
 | |
| the closest valid value.
 | |
| 					*:public* *public-variable* *E1331*
 | |
| If you don't care about side effects and want to allow the object variable to
 | |
| be changed at any time, you can make it public: >
 | |
| 
 | |
| 	public var lnum: number
 | |
| 	public var col: number
 | |
| 
 | |
| Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
 | |
| "pos.lnum" directly above will no longer give an error.
 | |
| 							*E1326*
 | |
| If you try to set an object variable that doesn't exist you get an error: >
 | |
| 	pos.other = 9
 | |
| <	E1326: Member not found on object "TextPosition": other ~
 | |
| 
 | |
| 							*E1376*
 | |
| A object variable cannot be accessed using the class name.
 | |
| 
 | |
| Protected variables ~
 | |
| 					*protected-variable* *E1332* *E1333*
 | |
| On the other hand, if you do not want the object variables to be read directly
 | |
| from outside the class or its sub-classes, you can make them protected.  This
 | |
| is done by prefixing an underscore to the name: >
 | |
| 
 | |
| 	var _lnum: number
 | |
| 	var _col: number
 | |
| 
 | |
| Now you need to provide methods to get the value of the protected variables.
 | |
| These are commonly called getters.  We recommend using a name that starts with
 | |
| "Get": >
 | |
| 
 | |
| 	def GetLnum(): number
 | |
| 	   return this._lnum
 | |
| 	enddef
 | |
| 
 | |
| 	def GetCol(): number
 | |
| 	   return this._col
 | |
| 	enddef
 | |
| 
 | |
| This example isn't very useful, the variables might as well have been public.
 | |
| It does become useful if you check the value.  For example, restrict the line
 | |
| number to the total number of lines: >
 | |
| 
 | |
| 	def GetLnum(): number
 | |
| 	   if this._lnum > this._lineCount
 | |
| 	      return this._lineCount
 | |
| 	   endif
 | |
| 	   return this._lnum
 | |
| 	enddef
 | |
| <
 | |
| Protected methods ~
 | |
| 						*protected-method* *E1366*
 | |
| If you want object methods to be accessible only from other methods of the
 | |
| same class and not used from outside the class, then you can make them
 | |
| protected.  This is done by prefixing the method name with an underscore: >
 | |
| 
 | |
|     class SomeClass
 | |
| 	def _Foo(): number
 | |
| 	  return 10
 | |
| 	enddef
 | |
| 	def Bar(): number
 | |
| 	  return this._Foo()
 | |
| 	enddef
 | |
|     endclass
 | |
| <
 | |
| Accessing a protected method outside the class will result in an error (using
 | |
| the above class): >
 | |
| 
 | |
|     var a = SomeClass.new()
 | |
|     a._Foo()
 | |
| <
 | |
| Simplifying the new() method ~
 | |
| 						*new()* *constructor*
 | |
| See also |default-constructor| and |multiple-constructors|.
 | |
| 
 | |
| Many constructors take values for the object variables.  Thus you very often
 | |
| see this pattern: >
 | |
| 
 | |
| 	 class SomeClass
 | |
| 	   var lnum: number
 | |
| 	   var col: number
 | |
| 
 | |
| 	   def new(lnum: number, col: number)
 | |
| 	      this.lnum = lnum
 | |
| 	      this.col = col
 | |
| 	   enddef
 | |
| 	 endclass
 | |
| <
 | |
| 							*E1390*
 | |
| Not only is this text you need to write, it also has the type of each
 | |
| variable twice.  Since this is so common a shorter way to write new() is
 | |
| provided: >
 | |
| 
 | |
| 	   def new(this.lnum, this.col)
 | |
| 	   enddef
 | |
| 
 | |
| The semantics are easy to understand: Providing the object variable name,
 | |
| including "this.", as the argument to new() means the value provided in the
 | |
| new() call is assigned to that object variable.  This mechanism comes from the
 | |
| Dart language.
 | |
| 
 | |
| Putting together this way of using new() and making the variables public
 | |
| results in a much shorter class definition than what we started with: >
 | |
| 
 | |
| 	class TextPosition
 | |
| 	   public var lnum: number
 | |
| 	   public var col: number
 | |
| 
 | |
| 	   def new(this.lnum, this.col)
 | |
| 	   enddef
 | |
| 
 | |
| 	   def SetPosition(lnum: number, col: number)
 | |
| 	      this.lnum = lnum
 | |
| 	      this.col = col
 | |
| 	   enddef
 | |
| 	 endclass
 | |
| 
 | |
| The sequence of constructing a new object is:
 | |
| 1. Memory is allocated and cleared.  All values are zero/false/empty.
 | |
| 2. For each declared object variable that has an initializer, the expression
 | |
|    is evaluated and assigned to the variable.  This happens in the sequence
 | |
|    the variables are declared in the class.
 | |
| 3. Arguments in the new() method in the "this.name" form are assigned.
 | |
| 4. The body of the new() method is executed.
 | |
| 
 | |
| If the class extends a parent class, the same thing happens.  In the second
 | |
| step the object variables of the parent class are initialized first.  There is
 | |
| no need to call "super()" or "new()" on the parent.
 | |
| 
 | |
| 							*E1365*
 | |
| When defining the new() method the return type should not be specified.  It
 | |
| always returns an object of the class.
 | |
| 
 | |
| The new() method can be made a protected method by using "_new()".  This can
 | |
| be used to support the singleton design pattern.
 | |
| 
 | |
| 							*E1386*
 | |
| When invoking an object method, the method name should be preceded by the
 | |
| object variable name.  An object method cannot be invoked using the class
 | |
| name.
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 3.  Class Variables and Methods			*Vim9-class-member*
 | |
| 
 | |
| 				    *:static* *E1329* *E1337* *E1338* *E1368*
 | |
| Class members are declared with "static".  They are used by the name without a
 | |
| prefix in the class where they are defined: >
 | |
| 
 | |
| 	class OtherThing
 | |
| 	   var size: number
 | |
| 	   static var totalSize: number
 | |
| 
 | |
| 	   def new(this.size)
 | |
| 	      totalSize += this.size
 | |
| 	   enddef
 | |
| 	endclass
 | |
| <							*E1340* *E1341*
 | |
| Since the name is used as-is, shadowing the name by a method argument name
 | |
| or local variable name is not allowed.
 | |
| 
 | |
| 					    *E1374* *E1375* *E1384* *E1385*
 | |
| To access a class member outside of the class where it is defined, the class
 | |
| name prefix must be used.  A class member cannot be accessed using an object.
 | |
| 
 | |
| Just like object members the access can be made protected by using an
 | |
| underscore as the first character in the name, and it can be made public by
 | |
| prefixing "public": >
 | |
| 
 | |
|     class OtherThing
 | |
| 	static var total: number	  # anybody can read, only class can write
 | |
| 	static var _sum: number	          # only class can read and write
 | |
| 	public static var result: number  # anybody can read and write
 | |
|     endclass
 | |
| <
 | |
| 							*class-method*
 | |
| Class methods are also declared with "static".  They can use the class
 | |
| variables but they have no access to the object variables, they cannot use the
 | |
| "this" keyword:
 | |
| >
 | |
| 	class OtherThing
 | |
| 	   var size: number
 | |
| 	   static var totalSize: number
 | |
| 
 | |
| 	   # Clear the total size and return the value it had before.
 | |
| 	   static def ClearTotalSize(): number
 | |
| 	      var prev = totalSize
 | |
| 	      totalSize = 0
 | |
| 	      return prev
 | |
| 	   enddef
 | |
| 	endclass
 | |
| 
 | |
| Inside the class, the class method can be called by name directly, outside the
 | |
| class, the class name must be prefixed: `OtherThing.ClearTotalSize()`.  Also,
 | |
| the name prefix must be used for public class methods in the special contexts
 | |
| of class variable initializers and of lambda expressions and nested functions:
 | |
| >
 | |
|     class OtherThing
 | |
| 	static var name: string = OtherThing.GiveName()
 | |
| 
 | |
| 	static def GiveName(): string
 | |
| 	    def DoGiveName(): string
 | |
| 		return OtherThing.NameAny()
 | |
| 	    enddef
 | |
| 
 | |
| 	    return DoGiveName()
 | |
| 	enddef
 | |
| 
 | |
| 	static def NameAny(): string
 | |
| 	    return "any"
 | |
| 	enddef
 | |
|     endclass
 | |
| <
 | |
| 
 | |
| Just like object methods the access can be made protected by using an
 | |
| underscore as the first character in the method name: >
 | |
| 
 | |
|     class OtherThing
 | |
| 	static def _Foo()
 | |
| 	    echo "Foo"
 | |
| 	enddef
 | |
| 	def Bar()
 | |
| 	    _Foo()
 | |
| 	enddef
 | |
|     endclass
 | |
| <
 | |
| 							*E1370*
 | |
| Note that constructors cannot be declared as "static". They are called like a
 | |
| static but execute as an object method; they have access to "this".
 | |
| 
 | |
| To access the class methods and class variables of a super class in an
 | |
| extended class, the class name prefix should be used just as from anywhere
 | |
| outside of the defining class: >
 | |
| 
 | |
|     vim9script
 | |
|     class Vehicle
 | |
| 	static var nextID: number = 1000
 | |
| 	static def GetID(): number
 | |
| 	    nextID += 1
 | |
| 	    return nextID
 | |
| 	enddef
 | |
|     endclass
 | |
|     class Car extends Vehicle
 | |
| 	var myID: number
 | |
| 	def new()
 | |
| 	    this.myID = Vehicle.GetID()
 | |
| 	enddef
 | |
|     endclass
 | |
| <
 | |
| Class variables and methods are not inherited by a child class.  A child class
 | |
| can declare a static variable or a method with the same name as the one in the
 | |
| super class.  Depending on the class where the member is used the
 | |
| corresponding class member will be used.  The type of the class member in a
 | |
| child class can be different from that in the super class.
 | |
| 
 | |
| The double underscore (__) prefix for a class or object method name is
 | |
| reserved for future use.
 | |
| 
 | |
| 					*object-final-variable* *E1409*
 | |
| The |:final| keyword can be used to make a class or object variable a
 | |
| constant.  Examples: >
 | |
| 
 | |
|     class A
 | |
| 	final v1 = [1, 2]		# final object variable
 | |
| 	public final v2 = {x: 1}	# final object variable
 | |
| 	static final v3 = 'abc'		# final class variable
 | |
| 	public static final v4 = 0z10	# final class variable
 | |
|     endclass
 | |
| <
 | |
| A final variable can be changed only from a constructor function.  Example: >
 | |
| 
 | |
|     class A
 | |
| 	final v1: list<number>
 | |
| 	def new()
 | |
| 	    this.v1 = [1, 2]
 | |
| 	enddef
 | |
|     endclass
 | |
|     var a = A.new()
 | |
|     echo a.v1
 | |
| <
 | |
| Note that the value of a final variable can be changed.  Example: >
 | |
| 
 | |
|     class A
 | |
| 	public final v1 = [1, 2]
 | |
|     endclass
 | |
|     var a = A.new()
 | |
|     a.v1[0] = 6			# OK
 | |
|     a.v1->add(3)		# OK
 | |
|     a.v1 = [3, 4]		# Error
 | |
| <
 | |
| 							*E1408*
 | |
| Final variables are not supported in an interface.  A class or object method
 | |
| cannot be final.
 | |
| 
 | |
| 					*object-const-variable*
 | |
| The |:const| keyword can be used to make a class or object variable and the
 | |
| value a constant.  Examples: >
 | |
| 
 | |
|     class A
 | |
| 	const v1 = [1, 2]		# const object variable
 | |
| 	public const v2 = {x: 1}	# const object variable
 | |
| 	static const v3 = 'abc'		# const class variable
 | |
| 	public static const v4 = 0z10	# const class variable
 | |
|     endclass
 | |
| <
 | |
| A const variable can be changed only from a constructor function. Example: >
 | |
| 
 | |
|     class A
 | |
| 	const v1: list<number>
 | |
| 	def new()
 | |
| 	    this.v1 = [1, 2]
 | |
| 	enddef
 | |
|     endclass
 | |
|     var a = A.new()
 | |
|     echo a.v1
 | |
| <
 | |
| A const variable and its value cannot be changed.  Example: >
 | |
| 
 | |
|     class A
 | |
| 	public const v1 = [1, 2]
 | |
|     endclass
 | |
|     var a = A.new()
 | |
|     a.v1[0] = 6			# Error
 | |
|     a.v1->add(3)		# Error
 | |
|     a.v1 = [3, 4]		# Error
 | |
| <
 | |
| 							 *E1410*
 | |
| Const variables are not supported in an interface.  A class or object method
 | |
| cannot be a const.
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 4.  Using an abstract class			*Vim9-abstract-class*
 | |
| 
 | |
| An abstract class forms the base for at least one sub-class.  In the class
 | |
| model one often finds that a few classes have the same properties that can be
 | |
| shared, but a class with these properties does not have enough state to create
 | |
| an object from.  A sub-class must extend the abstract class and add the
 | |
| missing state and/or methods before it can be used to create objects for.
 | |
| 
 | |
| For example, a Shape class could store a color and thickness.  You cannot
 | |
| create a Shape object, it is missing the information about what kind of shape
 | |
| it is.  The Shape class functions as the base for a Square and a Triangle
 | |
| class, for which objects can be created.  Example: >
 | |
| 
 | |
| 	abstract class Shape
 | |
| 	   var color = Color.Black
 | |
| 	   var thickness = 10
 | |
| 	endclass
 | |
| 
 | |
| 	class Square extends Shape
 | |
| 	   var size: number
 | |
| 
 | |
| 	   def new(this.size)
 | |
| 	   enddef
 | |
| 	endclass
 | |
| 
 | |
| 	class Triangle extends Shape
 | |
| 	   var base: number
 | |
| 	   var height: number
 | |
| 
 | |
| 	   def new(this.base, this.height)
 | |
| 	   enddef
 | |
| 	endclass
 | |
| <
 | |
| An abstract class is defined the same way as a normal class, except that it
 | |
| does not have any new() method. *E1359*
 | |
| 
 | |
| 					    *abstract-method* *E1371* *E1372*
 | |
| An abstract method can be defined in an abstract class by using the "abstract"
 | |
| prefix when defining the method: >
 | |
| 
 | |
| 	abstract class Shape
 | |
| 	   abstract def Draw()
 | |
| 	endclass
 | |
| <
 | |
| A static method in an abstract class cannot be an abstract method.
 | |
| 
 | |
| 						*E1404*
 | |
| An interface method cannot be an abstract method.
 | |
| 
 | |
| 						*E1373*
 | |
| A non-abstract class extending the abstract class must implement all the
 | |
| abstract methods.  The signature (arguments, argument types and return type)
 | |
| must be exactly the same.  If the return type of a method is a class, then
 | |
| that class or one of its subclasses can be used in the extended method.
 | |
| 
 | |
| 						*E1431*
 | |
| An abstract method in an abstract super class cannot be invoked.
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 5.  Using an interface				*Vim9-using-interface*
 | |
| 
 | |
| The example above with Shape, Square and Triangle can be made more useful if
 | |
| we add a method to compute the surface of the object.  For that we create the
 | |
| interface called HasSurface, which specifies one method Surface() that returns
 | |
| a number.  This example extends the one above: >
 | |
| 
 | |
| 	abstract class Shape
 | |
| 	   var color = Color.Black
 | |
| 	   var thickness = 10
 | |
| 	endclass
 | |
| 
 | |
| 	interface HasSurface
 | |
| 	   def Surface(): number
 | |
| 	endinterface
 | |
| 
 | |
| 	class Square extends Shape implements HasSurface
 | |
| 	   var size: number
 | |
| 
 | |
| 	   def new(this.size)
 | |
| 	   enddef
 | |
| 
 | |
| 	   def Surface(): number
 | |
| 	      return this.size * this.size
 | |
| 	   enddef
 | |
| 	endclass
 | |
| 
 | |
| 	class Triangle extends Shape implements HasSurface
 | |
| 	   var base: number
 | |
| 	   var height: number
 | |
| 
 | |
| 	   def new(this.base, this.height)
 | |
| 	   enddef
 | |
| 
 | |
| 	   def Surface(): number
 | |
| 	      return this.base * this.height / 2
 | |
| 	   enddef
 | |
| 	endclass
 | |
| <
 | |
| 					*E1348* *E1349* *E1367* *E1382* *E1383*
 | |
| If a class declares to implement an interface, all the items specified in the
 | |
| interface must appear in the class, with the same types.
 | |
| 
 | |
| The interface name can be used as a type: >
 | |
| 
 | |
| 	var shapes: list<HasSurface> = [
 | |
| 				Square.new(12),
 | |
| 				Triangle.new(8, 15),
 | |
| 				]
 | |
| 	for shape in shapes
 | |
| 	   echo $'the surface is {shape.Surface()}'
 | |
| 	endfor
 | |
| <
 | |
| 					*E1378* *E1379* *E1380* *E1387*
 | |
| An interface can contain only object methods and read-only object variables.
 | |
| An interface cannot contain read-write or protected object variables,
 | |
| protected object methods, class variables and class methods.
 | |
| 
 | |
| An interface can extend another interface using "extends".  The sub-interface
 | |
| inherits all the instance variables and methods from the super interface.
 | |
| 
 | |
| An interface cannot be defined inside a function.	*E1436*
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 6.  More class details				*Vim9-class* *Class* *class*
 | |
| 
 | |
| Defining a class ~
 | |
| 					*:class* *:endclass* *:abstract*
 | |
| A class is defined between `:class` and `:endclass`.  The whole class is
 | |
| defined in one script file.  It is not possible to add to a class later.
 | |
| 
 | |
| A class can only be defined in a |Vim9| script file.  *E1316*
 | |
| A class cannot be defined inside a function.  *E1429*
 | |
| 
 | |
| It is possible to define more than one class in a script file.  Although it
 | |
| usually is better to export only one main class.  It can be useful to define
 | |
| types, enums and helper classes though.
 | |
| 
 | |
| The `:abstract` keyword may be prefixed and `:export` may be used.  That gives
 | |
| these variants: >
 | |
| 
 | |
| 	class ClassName
 | |
| 	endclass
 | |
| 
 | |
| 	export class ClassName
 | |
| 	endclass
 | |
| 
 | |
| 	abstract class ClassName
 | |
| 	endclass
 | |
| 
 | |
| 	export abstract class ClassName
 | |
| 	endclass
 | |
| <
 | |
| 							*E1314*
 | |
| The class name should be CamelCased.  It must start with an uppercase letter.
 | |
| That avoids clashing with builtin types.
 | |
| 							*E1315*
 | |
| After the class name these optional items can be used.  Each can appear only
 | |
| once.  They can appear in any order, although this order is recommended: >
 | |
| 	extends ClassName
 | |
| 	implements InterfaceName, OtherInterface
 | |
| 	specifies SomeInterface
 | |
| <
 | |
| The "specifies" feature is currently not implemented.
 | |
| 
 | |
| 							*E1355* *E1369*
 | |
| Each variable and method name can be used only once.  It is not possible to
 | |
| define a method with the same name and different type of arguments.  It is not
 | |
| possible to use a public and protected member variable with the same name.  An
 | |
| object variable name used in a super class cannot be reused in a child class.
 | |
| 
 | |
| Object Variable Initialization ~
 | |
| 
 | |
| 							*E1430*
 | |
| If the type of a variable is not explicitly specified in a class, then it is
 | |
| set to "any" during class definition.  When an object is instantiated from the
 | |
| class, then the type of the variable is set.
 | |
| 
 | |
| The following reserved keyword names cannot be used as an object or class
 | |
| variable name: "super", "this", "true", "false", "null", "null_blob",
 | |
| "null_channel", "null_class", "null_dict", "null_function", "null_job",
 | |
| "null_list", "null_object", "null_partial" and "null_string".
 | |
| 
 | |
| Extending a class ~
 | |
| 							*extends*
 | |
| A class can extend one other class. *E1352* *E1353* *E1354*
 | |
| The basic idea is to build on top of an existing class, add properties to it.
 | |
| 
 | |
| The extended class is called the "base class" or "super class".  The new class
 | |
| is called the "child class".
 | |
| 
 | |
| Object variables from the base class are all taken over by the child class.  It
 | |
| is not possible to override them (unlike some other languages).
 | |
| 
 | |
| 						*E1356* *E1357* *E1358*
 | |
| Object methods of the base class can be overruled.  The signature (arguments,
 | |
| argument types and return type) must be exactly the same.  If the return type
 | |
| of a method is a class, then that class or one of its subclasses can be used
 | |
| in the extended method.  The method of the base class can be called by
 | |
| prefixing "super.".
 | |
| 
 | |
| 						*E1377*
 | |
| The access level of a method (public or protected) in a child class should be
 | |
| the same as the super class.
 | |
| 
 | |
| Other object methods of the base class are taken over by the child class.
 | |
| 
 | |
| Class methods, including methods starting with "new", can be overruled, like
 | |
| with object methods.  The method on the base class can be called by prefixing
 | |
| the name of the class (for class methods) or "super.".
 | |
| 
 | |
| Unlike other languages, the constructor of the base class does not need to be
 | |
| invoked.  In fact, it cannot be invoked.  If some initialization from the base
 | |
| class also needs to be done in a child class, put it in an object method and
 | |
| call that method from every constructor().
 | |
| 
 | |
| If the base class did not specify a new() method then one was automatically
 | |
| created.  This method will not be taken over by the child class.  The child
 | |
| class can define its own new() method, or, if there isn't one, a new() method
 | |
| will be added automatically.
 | |
| 
 | |
| 
 | |
| A class implementing an interface ~
 | |
| 					*implements* *E1346* *E1347* *E1389*
 | |
| A class can implement one or more interfaces.  The "implements" keyword can
 | |
| only appear once *E1350* .  Multiple interfaces can be specified, separated by
 | |
| commas.  Each interface name can appear only once. *E1351*
 | |
| 
 | |
| A class defining an interface ~
 | |
| 							*specifies*
 | |
| A class can declare its interface, the object variables and methods, with a
 | |
| named interface.  This avoids the need for separately specifying the
 | |
| interface, which is often done in many languages, especially Java.
 | |
| TODO: This is currently not implemented.
 | |
| 
 | |
| Items in a class ~
 | |
| 						*E1318* *E1325* *E1388*
 | |
| Inside a class, in between `:class` and `:endclass`, these items can appear:
 | |
| - An object variable declaration: >
 | |
| 	var _protectedVariableName: memberType
 | |
| 	var readonlyVariableName: memberType
 | |
| 	public var readwriteVariableName: memberType
 | |
| - A class variable declaration: >
 | |
| 	static var _protectedClassVariableName: memberType
 | |
| 	static var readonlyClassVariableName: memberType
 | |
| 	public static var readwriteClassVariableName: memberType
 | |
| - A constructor method: >
 | |
| 	def new(arguments)
 | |
| 	def newName(arguments)
 | |
| - A class method: >
 | |
| 	static def SomeMethod(arguments)
 | |
| 	static def _ProtectedMethod(arguments)
 | |
| - An object method: >
 | |
| 	def SomeMethod(arguments)
 | |
| 	def _ProtectedMethod(arguments)
 | |
| 
 | |
| For the object variable the type must be specified.  The best way is to do
 | |
| this explicitly with ": {type}".  For simple types you can also use an
 | |
| initializer, such as "= 123", and Vim will see that the type is a number.
 | |
| Avoid doing this for more complex types and when the type will be incomplete.
 | |
| For example: >
 | |
| 	var nameList = []
 | |
| This specifies a list, but the item type is unknown.  Better use: >
 | |
| 	var nameList: list<string>
 | |
| The initialization isn't needed, the list is empty by default.
 | |
| 							*E1330*
 | |
| Some types cannot be used, such as "void", "null" and "v:none".
 | |
| 
 | |
| Builtin Object Methods ~
 | |
|                                                        *builtin-object-methods*
 | |
| Some of the builtin functions like |empty()|, |len()| and |string()| can be
 | |
| used with an object.  An object can implement a method with the same name as
 | |
| these builtin functions to return an object-specific value.
 | |
| 
 | |
| 							*E1412*
 | |
| The following builtin methods are supported:
 | |
| 							*object-empty()*
 | |
|     empty()  Invoked by the |empty()| function to check whether an object is
 | |
| 	     empty.  If this method is missing, then true is returned.  This
 | |
| 	     method should not accept any arguments and must return a boolean.
 | |
| 							*object-len()*
 | |
|     len()    Invoked by the |len()| function to return the length of an
 | |
| 	     object.  If this method is missing in the class, then an error is
 | |
| 	     given and zero is returned.  This method should not accept any
 | |
| 	     arguments and must return a number.
 | |
| 							*object-string()*
 | |
|     string() Invoked by the |string()| function to get a textual
 | |
| 	     representation of an object.  Also used by the |:echo| command
 | |
| 	     for an object.  If this method is missing in the class, then a
 | |
| 	     built-in default textual representation is used.  This method
 | |
| 	     should not accept any arguments and must return a string.
 | |
| 
 | |
| 							*E1413*
 | |
| A class method cannot be used as a builtin method.
 | |
| 
 | |
| Defining an interface ~
 | |
| 					*Interface* *:interface* *:endinterface*
 | |
| An interface is defined between `:interface` and `:endinterface`.  It may be
 | |
| prefixed with `:export`: >
 | |
| 
 | |
| 	interface InterfaceName
 | |
| 	endinterface
 | |
| 
 | |
| 	export interface InterfaceName
 | |
| 	endinterface
 | |
| <							*E1344*
 | |
| An interface can declare object variables, just like in a class but without
 | |
| any initializer.
 | |
| 							*E1345*
 | |
| An interface can declare methods with `:def`, including the arguments and
 | |
| return type, but without the body and without `:enddef`.  Example: >
 | |
| 
 | |
| 	interface HasSurface
 | |
| 	   var size: number
 | |
| 	   def Surface(): number
 | |
| 	endinterface
 | |
| 
 | |
| An interface name must start with an uppercase letter. *E1343*
 | |
| The "Has" prefix can be used to make it easier to guess this is an interface
 | |
| name, with a hint about what it provides.
 | |
| An interface can only be defined in a |Vim9| script file.  *E1342*
 | |
| An interface cannot "implement" another interface but it can "extend" another
 | |
| interface. *E1381*
 | |
| 
 | |
| null object ~
 | |
| 
 | |
| When a variable is declared to have the type of an object, but it is not
 | |
| initialized, the value is null.  When trying to use this null object Vim often
 | |
| does not know what class was supposed to be used.  Vim then cannot check if
 | |
| a variable name is correct and you will get a "Using a null object" error,
 | |
| even when the variable name is invalid. *E1360* *E1362*
 | |
| 
 | |
| Default constructor ~
 | |
| 							*default-constructor*
 | |
| In case you define a class without a new() method, one will be automatically
 | |
| defined.  This default constructor will have arguments for all the object
 | |
| variables, in the order they were specified.  Thus if your class looks like: >
 | |
| 
 | |
| 	class AutoNew
 | |
| 	   var name: string
 | |
| 	   var age: number
 | |
| 	   var gender: Gender
 | |
| 	endclass
 | |
| 
 | |
| Then the default constructor will be: >
 | |
| 
 | |
| 	def new(this.name = v:none, this.age = v:none, this.gender = v:none)
 | |
| 	enddef
 | |
| 
 | |
| The "= v:none" default values make the arguments optional.  Thus you can also
 | |
| call `new()` without any arguments.  No assignment will happen and the default
 | |
| value for the object variables will be used.  This is a more useful example,
 | |
| with default values: >
 | |
| 
 | |
| 	class TextPosition
 | |
| 	   var lnum: number = 1
 | |
| 	   var col: number = 1
 | |
| 	endclass
 | |
| 
 | |
| If you want the constructor to have mandatory arguments, you need to write it
 | |
| yourself.  For example, if for the AutoNew class above you insist on getting
 | |
| the name, you can define the constructor like this: >
 | |
| 
 | |
| 	def new(this.name, this.age = v:none, this.gender = v:none)
 | |
| 	enddef
 | |
| <
 | |
| When using the default new() method, if the order of the object variables in
 | |
| the class is changed later, then all the callers of the default new() method
 | |
| need to change.  To avoid this, the new() method can be explicitly defined
 | |
| without any arguments.
 | |
| 
 | |
| 							*E1328*
 | |
| Note that you cannot use another default value than "v:none" here.  If you
 | |
| want to initialize the object variables, do it where they are declared.  This
 | |
| way you only need to look in one place for the default values.
 | |
| 
 | |
| All object variables will be used in the default constructor, including
 | |
| protected access ones.
 | |
| 
 | |
| If the class extends another one, the object variables of that class will come
 | |
| first.
 | |
| 
 | |
| 
 | |
| Multiple constructors ~
 | |
| 						*multiple-constructors*
 | |
| Normally a class has just one new() constructor.  In case you find that the
 | |
| constructor is often called with the same arguments you may want to simplify
 | |
| your code by putting those arguments into a second constructor method.  For
 | |
| example, if you tend to use the color black a lot: >
 | |
| 
 | |
| 	def new(this.garment, this.color, this.size)
 | |
| 	enddef
 | |
| 	...
 | |
| 	var pants = new(Garment.pants, Color.black, "XL")
 | |
| 	var shirt = new(Garment.shirt, Color.black, "XL")
 | |
| 	var shoes = new(Garment.shoes, Color.black, "45")
 | |
| 
 | |
| Instead of repeating the color every time you can add a constructor that
 | |
| includes it: >
 | |
| 
 | |
| 	def newBlack(this.garment, this.size)
 | |
| 	   this.color = Color.black
 | |
| 	enddef
 | |
| 	...
 | |
| 	var pants = newBlack(Garment.pants, "XL")
 | |
| 	var shirt = newBlack(Garment.shirt, "XL")
 | |
| 	var shoes = newBlack(Garment.shoes, "9.5")
 | |
| 
 | |
| Note that the method name must start with "new".  If there is no method called
 | |
| "new()" then the default constructor is added, even though there are other
 | |
| constructor methods.
 | |
| 
 | |
| Using variable type "any" for an Object~
 | |
| 							*obj-var-type-any*
 | |
| You can use a variable declared with type "any" to hold an object.  e.g.
 | |
| >
 | |
|     vim9script
 | |
|     class A
 | |
| 	var n = 10
 | |
| 	def Get(): number
 | |
| 	    return this.n
 | |
| 	enddef
 | |
|     endclass
 | |
| 
 | |
|     def Fn(o: any)
 | |
| 	echo o.n
 | |
| 	echo o.Get()
 | |
|     enddef
 | |
| 
 | |
|     var a = A.new()
 | |
|     Fn(a)
 | |
| <
 | |
| In this example, Vim cannot determine the type of the parameter "o" for
 | |
| function Fn() at compile time.  It can be either a |Dict| or an |Object|
 | |
| value.  Therefore, at runtime, when the type is known, the object member
 | |
| variable and method are looked up.  This process is not efficient, so it is
 | |
| recommended to use a more specific type whenever possible for better
 | |
| efficiency.
 | |
| 
 | |
| Compiling methods in a Class ~
 | |
| 							*class-compile*
 | |
| The |:defcompile| command can be used to compile all the class and object
 | |
| methods defined in a class: >
 | |
| 
 | |
| 	defcompile MyClass	# Compile class "MyClass"
 | |
| 	defcompile		# Compile the classes in the current script
 | |
| <
 | |
| ==============================================================================
 | |
| 
 | |
| 7.  Type definition				*typealias* *Vim9-type* *:type*
 | |
| 
 | |
| 					*E1393* *E1395* *E1396* *E1397* *E1398*
 | |
| A type definition is giving a name to a type specification.  This is also
 | |
| known as a "type alias".  The type alias can be used wherever a built-in type
 | |
| can be used.  Example: >
 | |
| 
 | |
|     type ListOfStrings = list<string>
 | |
|     var s: ListOfStrings = ['a', 'b']
 | |
| 
 | |
|     def ProcessStr(str: ListOfStrings): ListOfStrings
 | |
| 	return str
 | |
|     enddef
 | |
|     echo ProcessStr(s)
 | |
| <
 | |
| 							*E1394*
 | |
| A type alias name must start with an upper case character.  Only existing
 | |
| types can be aliased.
 | |
| 
 | |
| 							*E1399*
 | |
| A type alias can be created only at the script level and not inside a
 | |
| function.  A type alias can be exported and used across scripts.
 | |
| 
 | |
| 					*E1400* *E1401* *E1402* *E1403* *E1407*
 | |
| A type alias cannot be used as an expression.  A type alias cannot be used in
 | |
| the left-hand-side of an assignment.
 | |
| 
 | |
| For a type alias name, the |typename()| function returns the type that is
 | |
| aliased: >
 | |
| 
 | |
|     type ListOfStudents = list<dict<any>>
 | |
|     echo typename(ListOfStudents)
 | |
|     typealias<list<dict<any>>>
 | |
| <
 | |
| ==============================================================================
 | |
| 
 | |
| 8.  Enum					*Vim9-enum* *:enum* *:endenum*
 | |
| 
 | |
| 						*enum* *E1418* *E1419* *E1420*
 | |
| An enum is a type that can have one of a list of values.  Example: >
 | |
| 
 | |
|     enum Color
 | |
| 	White,
 | |
| 	Red,
 | |
| 	Green, Blue, Black
 | |
|     endenum
 | |
| <
 | |
| 						*enumvalue* *E1422* *E1428*
 | |
| The enum values are separated by commas.  More than one enum value can be
 | |
| listed in a single line.  The final enum value should not be followed by a
 | |
| comma.  Duplicate enum values are not allowed.
 | |
| 
 | |
| An enum value is accessed using the enum name followed by the value name: >
 | |
| 
 | |
|     var a: Color = Color.Blue
 | |
| <
 | |
| Enums are treated as classes, where each enum value is essentially an instance
 | |
| of that class.  Unlike typical object instantiation with the |new()| method,
 | |
| enum instances cannot be created this way.
 | |
| 
 | |
| An enum can only be defined in a |Vim9| script file.	*E1414*
 | |
| An enum cannot be defined inside a function.		*E1435*
 | |
| 
 | |
| 							*E1415*
 | |
| An enum name must start with an uppercase letter.  The name of an enum value
 | |
| in an enum can start with an upper or lowercase letter.
 | |
| 
 | |
| 							*E1416*
 | |
| An enum can implement an interface but cannot extend a class: >
 | |
| 
 | |
|     enum MyEnum implements MyIntf
 | |
| 	Value1,
 | |
| 	Value2
 | |
| 
 | |
| 	def SomeMethod()
 | |
| 	enddef
 | |
|     endenum
 | |
| <
 | |
| 							*enum-constructor*
 | |
| The enum value objects in an enum are constructed like any other objects using
 | |
| the |new()| method.  Arguments can be passed to the enum constructor by
 | |
| specifying them after the enum value name, just like calling a function.  The
 | |
| default constructor doesn't have any arguments.
 | |
| 
 | |
| 							*E1417*
 | |
| An enum can contain class variables, class methods, object variables and
 | |
| object methods.  The methods in an enum cannot be |:abstract| methods.
 | |
| 
 | |
| The following example shows an enum with object variables and methods: >
 | |
| 
 | |
|     vim9script
 | |
|     enum Planet
 | |
| 	Earth(1, false),
 | |
| 	Jupiter(95, true),
 | |
| 	Saturn(146, true)
 | |
| 
 | |
| 	var moons: number
 | |
| 	var has_rings: bool
 | |
| 	def GetMoons(): number
 | |
| 	    return this.moons
 | |
| 	enddef
 | |
|     endenum
 | |
|     echo Planet.Jupiter.GetMoons()
 | |
|     echo Planet.Earth.has_rings
 | |
| <
 | |
| 						*E1421* *E1423* *E1424* *E1425*
 | |
| Enums and their values are immutable.  They cannot be utilized as numerical or
 | |
| string types.  Enum values can declare mutable instance variables.
 | |
| 
 | |
| 						*enum-name* *E1427*
 | |
| Each enum value object has a "name" instance variable which contains the name
 | |
| of the enum value.  This is a readonly variable.
 | |
| 
 | |
| 						*enum-ordinal* *E1426*
 | |
| Each enum value has an associated ordinal number starting with 0.  The ordinal
 | |
| number of an enum value can be accessed using the "ordinal" instance variable.
 | |
| This is a readonly variable.  Note that if the ordering of the enum values in
 | |
| an enum is changed, then their ordinal values will also change.
 | |
| 
 | |
| 						*enum-values*
 | |
| All the values in an enum can be accessed using the "values" class variable
 | |
| which is a List of the enum objects.  This is a readonly variable.
 | |
| 
 | |
| Example: >
 | |
|     enum Planet
 | |
| 	Mercury,
 | |
| 	Venus,
 | |
| 	Earth
 | |
|     endenum
 | |
| 
 | |
|     echo Planet.Mercury
 | |
|     echo Planet.Venus.name
 | |
|     echo Planet.Venus.ordinal
 | |
|     for p in Planet.values
 | |
| 	# ...
 | |
|     endfor
 | |
| <
 | |
| An enum is a class with class variables for the enum value objects and object
 | |
| variables for the enum value name and the enum value ordinal: >
 | |
| 
 | |
|     enum Planet
 | |
| 	Mercury,
 | |
| 	Venus
 | |
|     endenum
 | |
| <
 | |
| The above enum definition is equivalent to the following class definition: >
 | |
| 
 | |
|     class Planet
 | |
|       public static final Mercury: Planet = Planet.new('Mercury', 0)
 | |
|       public static final Venus: Planet = Planet.new('Venus', 1)
 | |
| 
 | |
|       public static const values: list<Planet> = [Planet.Mercury, Planet.Venus]
 | |
| 
 | |
|       public const name: string
 | |
|       public const ordinal: number
 | |
|     endclass
 | |
| <
 | |
| A enum can contain object variables and methods just like a regular class: >
 | |
| 
 | |
|     enum Color
 | |
| 	Cyan([0, 255, 255]),
 | |
| 	Magenta([255, 0, 255]),
 | |
| 	Gray([128, 128, 128])
 | |
| 
 | |
| 	var rgb_values: list<number>
 | |
| 
 | |
| 	def Get_RGB(): list<number>
 | |
| 	    return this.rgb_values
 | |
| 	enddef
 | |
|     endenum
 | |
|     echo Color.Magenta.Get_RGB()
 | |
| <
 | |
| ==============================================================================
 | |
| 
 | |
| 9.  Rationale
 | |
| 
 | |
| Most of the choices for |Vim9| classes come from popular and recently
 | |
| developed languages, such as Java, TypeScript and Dart.  The syntax has been
 | |
| made to fit with the way Vim script works, such as using `endclass` instead of
 | |
| using curly braces around the whole class.
 | |
| 
 | |
| Some common constructs of object-oriented languages were chosen very long ago
 | |
| when this kind of programming was still new, and later found to be
 | |
| sub-optimal.  By this time those constructs were widely used and changing them
 | |
| was not an option.  In Vim we do have the freedom to make different choices,
 | |
| since classes are completely new.  We can make the syntax simpler and more
 | |
| consistent than what "old" languages use.  Without diverting too much, it
 | |
| should still mostly look like what you know from existing languages.
 | |
| 
 | |
| Some recently developed languages add all kinds of fancy features that we
 | |
| don't need for Vim.  But some have nice ideas that we do want to use.
 | |
| Thus we end up with a base of what is common in popular languages, dropping
 | |
| what looks like a bad idea, and adding some nice features that are easy to
 | |
| understand.
 | |
| 
 | |
| The main rules we use to make decisions:
 | |
| - Keep it simple.
 | |
| - No surprises, mostly do what other languages are doing.
 | |
| - Avoid mistakes from the past.
 | |
| - Avoid the need for the script writer to consult the help to understand how
 | |
|   things work, most things should be obvious.
 | |
| - Keep it consistent.
 | |
| - Aim at an average size plugin, not at a huge project.
 | |
| 
 | |
| 
 | |
| Using new() for the constructor ~
 | |
| 
 | |
| Many languages use the class name for the constructor method.  A disadvantage
 | |
| is that quite often this is a long name.  And when changing the class name all
 | |
| constructor methods need to be renamed.  Not a big deal, but still a
 | |
| disadvantage.
 | |
| 
 | |
| Other languages, such as TypeScript, use a specific name, such as
 | |
| "constructor()".  That seems better.  However, using "new" or "new()" to
 | |
| create a new object has no obvious relation with "constructor()".
 | |
| 
 | |
| For |Vim9| script using the same method name for all constructors seemed like
 | |
| the right choice, and by calling it new() the relation between the caller and
 | |
| the method being called is obvious.
 | |
| 
 | |
| No overloading of the constructor ~
 | |
| 
 | |
| In Vim script, both legacy and |Vim9| script, there is no overloading of
 | |
| methods.  That means it is not possible to use the same method name with
 | |
| different types of arguments.  Therefore there also is only one new()
 | |
| constructor.
 | |
| 
 | |
| With |Vim9| script it would be possible to support overloading, since
 | |
| arguments are typed.  However, this gets complicated very quickly.  Looking at
 | |
| a new() call one has to inspect the types of the arguments to know which of
 | |
| several new() methods is actually being called.  And that can require
 | |
| inspecting quite a bit of code.  For example, if one of the arguments is the
 | |
| return value of a method, you need to find that method to see what type it is
 | |
| returning.
 | |
| 
 | |
| Instead, every constructor has to have a different name, starting with "new".
 | |
| That way multiple constructors with different arguments are possible, while it
 | |
| is very easy to see which constructor is being used.  And the type of
 | |
| arguments can be properly checked.
 | |
| 
 | |
| No overloading of methods ~
 | |
| 
 | |
| Same reasoning as for the constructor: It is often not obvious what type
 | |
| arguments have, which would make it difficult to figure out what method is
 | |
| actually being called.  Better just give the methods a different name, then
 | |
| type checking will make sure it works as you intended.  This rules out
 | |
| polymorphism, which we don't really need anyway.
 | |
| 
 | |
| Single inheritance and interfaces ~
 | |
| 
 | |
| Some languages support multiple inheritance.  Although that can be useful in
 | |
| some cases, it makes the rules of how a class works quite complicated.
 | |
| Instead, using interfaces to declare what is supported is much simpler.  The
 | |
| very popular Java language does it this way, and it should be good enough for
 | |
| Vim.  The "keep it simple" rule applies here.
 | |
| 
 | |
| Explicitly declaring that a class supports an interface makes it easy to see
 | |
| what a class is intended for.  It also makes it possible to do proper type
 | |
| checking.  When an interface is changed any class that declares to implement
 | |
| it will be checked if that change was also changed.  The mechanism to assume a
 | |
| class implements an interface just because the methods happen to match is
 | |
| brittle and leads to obscure problems, let's not do that.
 | |
| 
 | |
| Using "this.variable" everywhere ~
 | |
| 
 | |
| The object variables in various programming languages can often be accessed in
 | |
| different ways, depending on the location.  Sometimes "this." has to be
 | |
| prepended to avoid ambiguity.  They are usually declared without "this.".
 | |
| That is quite inconsistent and sometimes confusing.
 | |
| 
 | |
| A very common issue is that in the constructor the arguments use the same name
 | |
| as the object variable.  Then for these variables "this." needs to be prefixed
 | |
| in the body, while for other variables this is not needed and often omitted.
 | |
| This leads to a mix of variables with and without "this.", which is
 | |
| inconsistent.
 | |
| 
 | |
| For |Vim9| classes the "this." prefix is always used for declared methods and
 | |
| variables.  Simple and consistent.  When looking at the code inside a class
 | |
| it's also directly clear which variable references are object variables and
 | |
| which aren't.
 | |
| 
 | |
| Using class variables ~
 | |
| 
 | |
| Using "static variable" to declare a class variable is very common, nothing
 | |
| new here.  In |Vim9| script these can be accessed directly by their name.
 | |
| Very much like how a script-local variable can be used in a method.  Since
 | |
| object variables are always accessed with "this." prepended, it's also quickly
 | |
| clear what kind of variable it is.
 | |
| 
 | |
| TypeScript prepends the class name before the class variable name, also inside
 | |
| the class.  This has two problems: The class name can be rather long, taking
 | |
| up quite a bit of space, and when the class is renamed all these places need
 | |
| to be changed too.
 | |
| 
 | |
| Declaring object and class variables ~
 | |
| 
 | |
| The main choice is whether to use "var" as with variable declarations.
 | |
| TypeScript does not use it: >
 | |
| 	class Point {
 | |
| 	  x: number;
 | |
| 	  y = 0;
 | |
| 	}
 | |
| 
 | |
| Following that Vim object variables could be declared like this: >
 | |
| 	class Point
 | |
| 	  this.x: number
 | |
| 	  this.y = 0
 | |
| 	endclass
 | |
| 
 | |
| Some users pointed out that this looks more like an assignment than a
 | |
| declaration.  Adding "var" and omitting "this." changes that: >
 | |
| 	class Point
 | |
| 	  var x: number
 | |
| 	  var y = 0
 | |
| 	endclass
 | |
| 
 | |
| We also need to be able to declare class variables using the "static" keyword.
 | |
| There we can also choose to leave out "var": >
 | |
| 	class Point
 | |
| 	  var x: number
 | |
| 	  static count = 0
 | |
| 	endclass
 | |
| 
 | |
| Or do use it, before "static": >
 | |
| 	class Point
 | |
| 	  var x: number
 | |
| 	  var static count = 0
 | |
| 	endclass
 | |
| 
 | |
| Or after "static": >
 | |
| 	class Point
 | |
| 	  var x: number
 | |
| 	  static var count = 0
 | |
| 	endclass
 | |
| 
 | |
| This is more in line with "static def Func()".
 | |
| 
 | |
| There is no clear preference whether to use "var" or not.  The two main
 | |
| reasons to leave it out are:
 | |
| 1. TypeScript and other popular languages do not use it.
 | |
| 2. Less clutter.
 | |
| 
 | |
| However, it is more common for languages to reuse their general variable and
 | |
| function declaration syntax for class/object variables and methods.  Vim9 also
 | |
| reuses the general function declaration syntax for methods.  So, for the sake
 | |
| of consistency, we require "var" in these declarations.
 | |
| 
 | |
| Using "ClassName.new()" to construct an object ~
 | |
| 
 | |
| Many languages use the "new" operator to create an object, which is actually
 | |
| kind of strange, since the constructor is defined as a method with arguments,
 | |
| not a command.  TypeScript also has the "new" keyword, but the method is
 | |
| called "constructor()", it is hard to see the relation between the two.
 | |
| 
 | |
| In |Vim9| script the constructor method is called new(), and it is invoked as
 | |
| new(), simple and straightforward.  Other languages use "new ClassName()",
 | |
| while there is no ClassName() method, it's a method by another name in the
 | |
| class called ClassName.  Quite confusing.
 | |
| 
 | |
| 
 | |
| Vim9class access modes ~
 | |
| 						    *vim9-access-modes*
 | |
| The variable access modes, and their meaning, supported by Vim9class are
 | |
| 	|public-variable|	read and write from anywhere
 | |
| 	|read-only-variable|	read from anywhere, write from inside the
 | |
| 				class and sub-classes
 | |
| 	|protected-variable|	read and write from inside the class and
 | |
| 				sub-classes
 | |
| 
 | |
| The method access modes are similar, but without the read-only mode.
 | |
| 
 | |
| 
 | |
| Default read access to object variables ~
 | |
| 
 | |
| Some users will remark that the access rules for object variables are
 | |
| asymmetric.  Well, that is intentional.  Changing a value is a very different
 | |
| action than reading a value.  The read operation has no side effects, it can
 | |
| be done any number of times without affecting the object.  Changing the value
 | |
| can have many side effects, and even have a ripple effect, affecting other
 | |
| objects.
 | |
| 
 | |
| When adding object variables one usually doesn't think much about this, just
 | |
| get the type right.  And normally the values are set in the new() method.
 | |
| Therefore defaulting to read access only "just works" in most cases.  And when
 | |
| directly writing you get an error, which makes you wonder if you actually want
 | |
| to allow that.  This helps writing code with fewer mistakes.
 | |
| 
 | |
| 
 | |
| Making object variables protected with an underscore ~
 | |
| 
 | |
| When an object variable is protected, it can only be read and changed inside
 | |
| the class (and in sub-classes), then it cannot be used outside of the class.
 | |
| Prepending an underscore is a simple way to make that visible.  Various
 | |
| programming languages have this as a recommendation.
 | |
| 
 | |
| In case you change your mind and want to make the object variable accessible
 | |
| outside of the class, you will have to remove the underscore everywhere.
 | |
| Since the name only appears in the class (and sub-classes) they will be easy
 | |
| to find and change.
 | |
| 
 | |
| The other way around is much harder: you can easily prepend an underscore to
 | |
| the object variable inside the class to make it protected, but any usage
 | |
| elsewhere you will have to track down and change.  You may have to make it a
 | |
| "set" method call.  This reflects the real world problem that taking away
 | |
| access requires work to be done for all places where that access exists.
 | |
| 
 | |
| An alternative would have been using the "protected" keyword, just like
 | |
| "public" changes the access in the other direction.  Well, that's just to
 | |
| reduce the number of keywords.
 | |
| 
 | |
| No private object variables ~
 | |
| 
 | |
| Some languages provide several ways to control access to object variables.
 | |
| The most known is "protected", and the meaning varies from language to
 | |
| language.  Others are "shared", "private", "package" and even "friend".
 | |
| 
 | |
| These rules make life more difficult.  That can be justified in projects where
 | |
| many people work on the same, complex code where it is easy to make mistakes.
 | |
| Especially when refactoring or other changes to the class model.
 | |
| 
 | |
| The Vim scripts are expected to be used in a plugin, with just one person or a
 | |
| small team working on it.  Complex rules then only make it more complicated,
 | |
| the extra safety provided by the rules isn't really needed.  Let's just keep
 | |
| it simple and not specify access details.
 | |
| 
 | |
| 
 | |
| ==============================================================================
 | |
| 
 | |
| 10. To be done later
 | |
| 
 | |
| Can a newSomething() constructor invoke another constructor?  If yes, what are
 | |
| the restrictions?
 | |
| 
 | |
| Thoughts:
 | |
| - Generics for a class: `class <Tkey, Tentry>`
 | |
| - Generics for a function: `def <Tkey> GetLast(key: Tkey)`
 | |
| - Mixins: not sure if that is useful, leave out for simplicity.
 | |
| 
 | |
| Some things that look like good additions:
 | |
| - For testing: Mock mechanism
 | |
| 
 | |
| An important class to be provided is "Promise".  Since Vim is single
 | |
| threaded, connecting asynchronous operations is a natural way of allowing
 | |
| plugins to do their work without blocking the user.  It's a uniform way to
 | |
| invoke callbacks and handle timeouts and errors.
 | |
| 
 | |
| 
 | |
|  vim:tw=78:ts=8:noet:ft=help:norl:
 |