| *vim9class.txt* For Vim version 9.0. Last change: 2023 Jan 09 |
| |
| |
| VIM REFERENCE MANUAL by Bram Moolenaar |
| |
| |
| NOTE - This is under development, anything can still change! - NOTE |
| |
| |
| Vim9 classes, objects, interfaces, types and enums. |
| |
| 1. Overview |Vim9-class-overview| |
| 2. A simple class |Vim9-simple-class| |
| 3. Using an abstract class |Vim9-abstract-class| |
| 4. Using an interface |Vim9-using-interface| |
| 5. More class details |Vim9-class| |
| 6. Type definition |Vim9-type| |
| 7. 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 about 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. |
| |
| The basic item is an object: |
| - An object stores state. It contains one or more variables that can each |
| have a value. |
| - An object usually provides functions that 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 |
| member functions. |
| - Objects are created by a class and all objects have the same interface. |
| This never changes, 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 and constants. |
| - 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. |
| |
| You will soon find that composition is often better than inheritance. 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: > |
| |
| class TextPosition |
| this.lnum: number |
| this.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 members "lnum" and "col" can be accessed directly: > |
| |
| echo $'The text position is ({pos.lnum}, {pos.col})' |
| < *E1317* *E1327* |
| If you have been using other object-oriented languages you will notice that |
| in Vim the object members are consistently referred to with the "this." |
| prefix. This is different from languages like Java and TypeScript. This |
| 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 member. |
| |
| |
| Member write access ~ |
| |
| Now try to change an object member directly: > |
| |
| pos.lnum = 9 |
| < *E1335* |
| This will give you an error! That is because by default object members can be |
| read but not set. That's why the class provides a method for it: > |
| |
| pos.SetLnum(9) |
| |
| Allowing to read but not set an object member 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* *E1331* |
| If you don't care about side effects and want to allow the object member to be |
| changed at any time, you can make it public: > |
| |
| public this.lnum: number |
| public this.col number |
| |
| Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting |
| "pos.lnum" directly above will no longer give an error. |
| *E1334* |
| If you try to set an object member that doesn't exist you get an error: > |
| pos.other = 9 |
| < E1334: Object member not found: other ~ |
| |
| |
| Private members ~ |
| *E1332* *E1333* |
| On the other hand, if you do not want the object members to be read directly, |
| you can make them private. This is done by prefixing an underscore to the |
| name: > |
| |
| this._lnum: number |
| this._col number |
| |
| Now you need to provide methods to get the value of the private members. |
| These are commonly call 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 members 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 |
| |
| |
| Simplifying the new() method ~ |
| |
| Many constructors take values for the object members. Thus you very often see |
| this pattern: > |
| |
| this.lnum: number |
| this.col: number |
| |
| def new(lnum: number, col: number) |
| this.lnum = lnum |
| this.col = col |
| enddef |
| |
| Not only is this text you need to write, it also has the type of each member |
| 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 member name, |
| including "this.", as the argument to new() means the value provided in the |
| new() call is assigned to that object member. This mechanism is coming from |
| the Dart language. |
| |
| The sequence of constructing a new object is: |
| 1. Memory is allocated and cleared. All values are zero/false/empty. |
| 2. For each declared member that has an initializer, the expression is |
| evaluated and assigned to the member. This happens in the sequence the |
| members 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. |
| |
| TODO: for a sub-class the constructor of the parent class will be invoked |
| somewhere. |
| |
| |
| ============================================================================== |
| |
| 3. 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 those 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. |
| |
| An abstract class does not have a new() method. |
| |
| 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 |
| this.color = Color.Black |
| this.thickness = 10 |
| endclass |
| |
| class Square extends Shape |
| this.size: number |
| |
| def new(this.size) |
| enddef |
| endclass |
| |
| class Triangle extends Shape |
| this.base: number |
| this.height: number |
| |
| def new(this.base, this.height) |
| enddef |
| endclass |
| < |
| *class-member* *:static* *E1337* *E1338* |
| Class members are declared with "static". They are used by the name without a |
| prefix: > |
| |
| class OtherThing |
| this.size: number |
| static 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 function argument name |
| or variable name is not allowed. |
| |
| Just like object members the access can be made private by using an underscore |
| as the first character in the name, and it can be made public by prefixing |
| "public": > |
| class OtherThing |
| static total: number # anybody can read, only class can write |
| static _sum: number # only class can read and write |
| public static result: number # anybody can read and write |
| endclass |
| < |
| *class-function* |
| Class functions are also declared with "static". They have no access to |
| object members, they cannot use the "this" keyword. > |
| |
| class OtherThing |
| this.size: number |
| static 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 |
| |
| |
| ============================================================================== |
| |
| 4. 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 |
| this.color = Color.Black |
| this.thickness = 10 |
| endclass |
| |
| interface HasSurface |
| def Surface(): number |
| endinterface |
| |
| class Square extends Shape implements HasSurface |
| this.size: number |
| |
| def new(this.size) |
| enddef |
| |
| def Surface(): number |
| return this.size * this.size |
| enddef |
| endclass |
| |
| class Triangle extends Shape implements HasSurface |
| this.base: number |
| this.height: number |
| |
| def new(this.base, this.height) |
| enddef |
| |
| def Surface(): number |
| return this.base * this.height / 2 |
| enddef |
| endclass |
| |
| If a class declares to implement an interface, all the items specified in the |
| interface must appear in the class, with the same types. *E1348* *E1349* |
| |
| 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 |
| |
| |
| ============================================================================== |
| |
| 5. 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. |
| |
| 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 |
| < *extends* |
| A class can extend one other class. *E1352* *E1353* *E1354* |
| *implements* *E1346* *E1347* |
| 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* |
| *specifies* |
| A class can declare its interface, the object members and methods, with a |
| named interface. This avoids the need for separately specifying the |
| interface, which is often done in many languages, especially Java. |
| |
| |
| Items in a class ~ |
| *E1318* *E1325* *E1326* |
| Inside a class, in betweeen `:class` and `:endclass`, these items can appear: |
| - An object member declaration: > |
| this._memberName: memberType |
| this.memberName: memberType |
| public this.memberName: memberType |
| - A constructor method: > |
| def new(arguments) |
| def newName(arguments) |
| - An object method: > |
| def SomeMethod(arguments) |
| < *E1329* |
| For the object member 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: > |
| this.nameList = [] |
| This specifies a list, but the item type is unknown. Better use: > |
| this.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". |
| |
| |
| Defining an 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 members, 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 |
| this.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* |
| |
| |
| 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 |
| members, in the order they were specified. Thus if your class looks like: > |
| |
| class AutoNew |
| this.name: string |
| this.age: number |
| this.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 members will be used. This is a more useful example, |
| with default values: > |
| |
| class TextPosition |
| this.lnum: number = 1 |
| this.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 |
| < *E1328* |
| Note that you cannot use another default value than "v:none" here. If you |
| want to initialize the object members, do it where they are declared. This |
| way you only need to look in one place for the default values. |
| |
| All object members will be used in the default constructor, also private |
| access ones. |
| |
| If the class extends another one, the object members of that class will come |
| first. |
| |
| |
| 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. |
| |
| |
| ============================================================================== |
| |
| 6. Type definition *Vim9-type* *:type* |
| |
| A type definition is giving a name to a type specification. For Example: > |
| |
| :type ListOfStrings list<string> |
| |
| TODO: more explanation |
| |
| |
| ============================================================================== |
| |
| 7. Enum *Vim9-enum* *:enum* *:endenum* |
| |
| An enum is a type that can have one of a list of values. Example: > |
| |
| :enum Color |
| White |
| Red |
| Green |
| Blue |
| Black |
| :endenum |
| |
| TODO: more explanation |
| |
| |
| ============================================================================== |
| |
| 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 |
| functions. That means it is not possible to use the same function 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.member" everywhere ~ |
| |
| The object members 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 member. Then for these members "this." needs to be prefixed in |
| the body, while for other members this is not needed and often omitted. This |
| leads to a mix of members with and without "this.", which is inconsistent. |
| |
| For |Vim9| classes the "this." prefix is always used. Also for declaring the |
| members. Simple and consistent. When looking at the code inside a class it's |
| also directly clear which variable references are object members and which |
| aren't. |
| |
| |
| Using class members ~ |
| |
| Using "static member" to declare a class member 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 function. Since object |
| members are always accessed with "this." prepended, it's also quickly clear |
| what kind of member it is. |
| |
| TypeScript prepends the class name before the class member, 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 members ~ |
| |
| 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 members 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" changes that: > |
| class Point |
| var this.x: number |
| var this.y = 0 |
| endclass |
| |
| We also need to be able to declare class members using the "static" keyword. |
| There we can also choose to leave out "var": > |
| class Point |
| var this.x: number |
| static count = 0 |
| endclass |
| |
| Or do use it, before "static": > |
| class Point |
| var this.x: number |
| var static count = 0 |
| endclass |
| |
| Or after "static": > |
| class Point |
| var this.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, Java and other popular languages do not use it. |
| 2. Less clutter. |
| |
| |
| 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. |
| |
| |
| Default read access to object members ~ |
| |
| Some users will remark that the access rules for object members 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 members 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 members private with an underscore ~ |
| |
| When an object member is private, 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 member 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 member inside the class to make it private, 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 "private" keyword, just like "public" |
| changes the access in the other direction. Well, that's just to reduce the |
| number of keywords. |
| |
| |
| No protected object members ~ |
| |
| Some languages provide several ways to control access to object members. The |
| most known is "protected", and the meaning varies from language to language. |
| Others are "shared", "private" 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 provide 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: |