blob: cb86b4d1364ecc907c2925f885601792b8ddd002 [file] [log] [blame]
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00001*vim9class.txt* For Vim version 9.0. Last change: 2022 Dec 04
2
3
4 VIM REFERENCE MANUAL by Bram Moolenaar
5
6
7NOTE - This is under development, anything can still change! - NOTE
8
9
10Vim9 classes, objects, interfaces, types and enums.
11
121. Overview |Vim9-class-overview|
132. A simple class |Vim9-simple-class|
143. Using an abstract class |Vim9-abstract-class|
154. Using an interface |Vim9-using-interface|
165. More class details |Vim9-class|
176. Type definition |Vim9-type|
187. Enum |Vim9-enum|
19
209. Rationale
2110. To be done later
22
23==============================================================================
24
251. Overview *Vim9-class-overview*
26
27The fancy term is "object-oriented programming". You can find lots of study
28material about this subject. Here we document what |Vim9| script provides,
29assuming you know the basics already. Added are helpful hints about how
30to use this functionality effectively.
31
32The basic item is an object:
33- An object stores state. It contains one or more variables that can each
34 have a value.
35- An object usually provides functions that manipulate its state. These
36 functions are invoked "on the object", which is what sets it apart from the
37 traditional separation of data and code that manipulates the data.
38- An object has a well defined interface, with typed member variables and
39 member functions.
40- Objects are created by a class and all objects have the same interface.
41 This never changes, it is not dynamic.
42
43An object can only be created by a class. A class provides:
44- A new() method, the constructor, which returns an object for the class.
45 This method is invoked on the class name: MyClass.new().
46- State shared by all objects of the class: class variables and constants.
47- A hierarchy of classes, with super-classes and sub-classes, inheritance.
48
49An interface is used to specify properties of an object:
50- An object can declare several interfaces that it implements.
51- Different objects implementing the same interface can be used the same way.
52
53The class hierarchy allows for single inheritance. Otherwise interfaces are
54to be used where needed.
55
56
57Class modeling ~
58
59You can model classes any way you like. Keep in mind what you are building,
60don't try to model the real world. This can be confusing, especially because
61teachers use real-world objects to explain class relations and you might think
62your model should therefore reflect the real world. It doesn't! The model
63should match your purpose.
64
65You will soon find that composition is often better than inheritance. Don't
66waste time trying to find the optimal class model. Or waste time discussing
67whether a square is a rectangle or that a rectangle is a square. It doesn't
68matter.
69
70
71==============================================================================
72
732. A simple class *Vim9-simple-class*
74
75Let's start with a simple example: a class that stores a text position: >
76
77 class TextPosition
78 this.lnum: number
79 this.col: number
80
81 def new(lnum: number, col: number)
82 this.lnum = lnum
83 this.col = col
84 enddef
85
86 def SetLnum(lnum: number)
87 this.lnum = lnum
88 enddef
89
90 def SetCol(col: number)
91 this.col = col
92 enddef
93
94 def SetPosition(lnum: number, col: number)
95 this.lnum = lnum
96 this.col = col
97 enddef
98 endclass
99
100You can create an object from this class with the new() method: >
101
102 var pos = TextPosition.new(1, 1)
103
104The object members "lnum" and "col" can be accessed directly: >
105
106 echo $'The text position is ({pos.lnum}, {pos.col})'
107
108If you have been using other object-oriented languages you will notice that
109in Vim the object members are consistently referred to with the "this."
110prefix. This is different from languages like Java and TypeScript. This
111naming convention makes the object members easy to spot. Also, when a
112variable does not have the "this." prefix you know it is not an object member.
113
114
115Member write access ~
116
117Now try to change an object member directly: >
118
119 pos.lnum = 9
120
121This will give you an error! That is because by default object members can be
122read but not set. That's why the class provides a method for it: >
123
124 pos.SetLnum(9)
125
126Allowing to read but not set an object member is the most common and safest
127way. Most often there is no problem using a value, while setting a value may
128have side effects that need to be taken care of. In this case, the SetLnum()
129method could check if the line number is valid and either give an error or use
130the closest valid value.
131
132If you don't care about side effects and want to allow the object member to be
133changed at any time, you can make it public: >
134
135 public this.lnum: number
136 public this.col number
137
138Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
139"pos.lnum" directly above will no longer give an error.
140
141
142Private members ~
143
144On the other hand, if you do not want the object members to be read directly,
145you can make them private. This is done by prefixing an underscore to the
146name: >
147
148 this._lnum: number
149 this._col number
150
151Now you need to provide methods to get the value of the private members.
152These are commonly call getters. We recommend using a name that starts with
153"Get": >
154
155 def GetLnum(): number
156 return this._lnum
157 enddef
158
159 def GetCol() number
160 return this._col
161 enddef
162
163This example isn't very useful, the members might as well have been public.
164It does become useful if you check the value. For example, restrict the line
165number to the total number of lines: >
166
167 def GetLnum(): number
168 if this._lnum > this._lineCount
169 return this._lineCount
170 endif
171 return this._lnum
172 enddef
173
174
175Simplifying the new() method ~
176
177Many constructors take values for the object members. Thus you very often see
178this pattern: >
179
180 this.lnum: number
181 this.col: number
182
183 def new(lnum: number, col: number)
184 this.lnum = lnum
185 this.col = col
186 enddef
187
188Not only is this text you need to write, it also has the type of each member
189twice. Since this is so common a shorter way to write new() is provided: >
190
191 def new(this.lnum, this.col)
192 enddef
193
194The semantics are easy to understand: Providing the object member name,
195including "this.", as the argument to new() means the value provided in the
196new() call is assigned to that object member. This mechanism is coming from
197the Dart language.
198
199The sequence of constructing a new object is:
2001. Memory is allocated and cleared. All values are zero/false/empty.
2012. For each declared member that has an initializer, the expression is
202 evaluated and assigned to the member. This happens in the sequence the
203 members are declared in the class.
2043. Arguments in the new() method in the "this.name" form are assigned.
2054. The body of the new() method is executed.
206
207TODO: for a sub-class the constructor of the parent class will be invoked
208somewhere.
209
210
211==============================================================================
212
2133. Using an abstract class *Vim9-abstract-class*
214
215An abstract class forms the base for at least one sub-class. In the class
216model one often finds that a few classes have the same properties that can be
217shared, but a class with those properties does not have enough state to create
218an object from. A sub-class must extend the abstract class and add the
219missing state and/or methods before it can be used to create objects for.
220
221An abstract class does not have a new() method.
222
223For example, a Shape class could store a color and thickness. You cannot
224create a Shape object, it is missing the information about what kind of shape
225it is. The Shape class functions as the base for a Square and a Triangle
226class, for which objects can be created. Example: >
227
228 abstract class Shape
229 this.color = Color.Black
230 this.thickness = 10
231 endclass
232
233 class Square extends Shape
234 this.size: number
235
236 def new(this.size)
237 enddef
238 endclass
239
240 class Triangle extends Shape
241 this.base: number
242 this.height: number
243
244 def new(this.base, this.height)
245 enddef
246 endclass
247<
248 *class-member* *:static*
249Class members are declared with "static". They are used by the name without a
250prefix: >
251
252 class OtherThing
253 this.size: number
254 static totalSize: number
255
256 def new(this.size)
257 totalSize += this.size
258 enddef
259 endclass
260<
261 *class-method*
262Class methods are also declared with "static". They have no access to object
263members, they cannot use the "this" keyword. >
264
265 class OtherThing
266 this.size: number
267 static totalSize: number
268
269 " Clear the total size and return the value it had before.
270 static def ClearTotalSize(): number
271 var prev = totalSize
272 totalSize = 0
273 return prev
274 enddef
275 endclass
276
277
278==============================================================================
279
2804. Using an interface *Vim9-using-interface*
281
282The example above with Shape, Square and Triangle can be made more useful if
283we add a method to compute the surface of the object. For that we create the
284interface called HasSurface, which specifies one method Surface() that returns
285a number. This example extends the one above: >
286
287 abstract class Shape
288 this.color = Color.Black
289 this.thickness = 10
290 endclass
291
292 interface HasSurface
293 def Surface(): number
294 endinterface
295
296 class Square extends Shape implements HasSurface
297 this.size: number
298
299 def new(this.size)
300 enddef
301
302 def Surface(): number
303 return this.size * this.size
304 enddef
305 endclass
306
307 class Triangle extends Shape implements HasSurface
308 this.base: number
309 this.height: number
310
311 def new(this.base, this.height)
312 enddef
313
314 def Surface(): number
315 return this.base * this.height / 2
316 enddef
317 endclass
318
319The interface name can be used as a type: >
320
321 var shapes: list<HasSurface> = [
322 Square.new(12),
323 Triangle.new(8, 15),
324 ]
325 for shape in shapes
326 echo $'the surface is {shape.Surface()}'
327 endfor
328
329
330==============================================================================
331
3325. More class details *Vim9-class*
333
334Defining a class ~
335 *:class* *:endclass* *:abstract*
336A class is defined between `:class` and `:endclass`. The whole class is
337defined in one script file. It is not possible to add to a class later.
338
339It is possible to define more than one class in a script file. Although it
340usually is better to export only one main class. It can be useful to define
341types, enums and helper classes though.
342
343The `:abstract` keyword may be prefixed and `:export` may be used. That gives
344these variants: >
345
346 class ClassName
347 endclass
348
349 export class ClassName
350 endclass
351
352 abstract class ClassName
353 endclass
354
355 export abstract class ClassName
356 endclass
357<
358 *E1314*
359The class name should be CamelCased. It must start with an uppercase letter.
360That avoids clashing with builtin types.
361
362After the class name these optional items can be used. Each can appear only
363once. They can appear in any order, although this order is recommended: >
364 extends ClassName
365 implements InterfaceName, OtherInterface
366 specifies SomeInterface
367< *extends*
368A class can extend one other class.
369 *implements*
370A class can implement one or more interfaces.
371 *specifies*
372A class can declare it's interface, the object members and methods, with a
373named interface. This avoids the need for separately specifying the
374interface, which is often done an many languages, especially Java.
375
376
377Defining an interface ~
378 *:interface* *:endinterface*
379An interface is defined between `:interface` and `:endinterface`. It may be
380prefixed with `:export`: >
381
382 interface InterfaceName
383 endinterface
384
385 export interface InterfaceName
386 endinterface
387
388An interface can declare object members, just like in a class but without any
389initializer.
390
391An interface can declare methods with `:def`, including the arguments and
392return type, but without the body and without `:enddef`. Example: >
393
394 interface HasSurface
395 this.size: number
396 def Surface(): number
397 endinterface
398
399The "Has" prefix can be used to make it easier to guess this is an interface
400name, with a hint about what it provides.
401
402
403Default constructor ~
404
405In case you define a class without a new() method, one will be automatically
406defined. This default constructor will have arguments for all the object
407members, in the order they were specified. Thus if your class looks like: >
408
409 class AutoNew
410 this.name: string
411 this.age: number
412 this.gender: Gender
413 endclass
414
415Then The default constructor will be: >
416
417 def new(this.name, this.age, this.gender)
418 enddef
419
420All object members will be used, also private access ones.
421
422
423Multiple constructors ~
424
425Normally a class has just one new() constructor. In case you find that the
426constructor is often called with the same arguments you may want to simplify
427your code by putting those arguments into a second constructor method. For
428example, if you tend to use the color black a lot: >
429
430 def new(this.garment, this.color, this.size)
431 enddef
432 ...
433 var pants = new(Garment.pants, Color.black, "XL")
434 var shirt = new(Garment.shirt, Color.black, "XL")
435 var shoes = new(Garment.shoes, Color.black, "45")
436
437Instead of repeating the color every time you can add a constructor that
438includes it: >
439
440 def newBlack(this.garment, this.size)
441 this.color = Color.black
442 enddef
443 ...
444 var pants = newBlack(Garment.pants, "XL")
445 var shirt = newBlack(Garment.shirt, "XL")
446 var shoes = newBlack(Garment.shoes, "9.5")
447
448Note that the method name must start with "new". If there is no method called
449"new()" then the default constructor is added, even though there are other
450constructor methods.
451
452
453==============================================================================
454
4556. Type definition *Vim9-type* *:type*
456
457A type definition is giving a name to a type specification. For Example: >
458
459 :type ListOfStrings list<string>
460
461TODO: more explanation
462
463
464==============================================================================
465
4667. Enum *Vim9-enum* *:enum* *:endenum*
467
468An enum is a type that can have one of a list of values. Example: >
469
470 :enum Color
471 White
472 Red
473 Green
474 Blue
475 Black
476 :endenum
477
478TODO: more explanation
479
480
481==============================================================================
482
4839. Rationale
484
485Most of the choices for |Vim9| classes come from popular and recently
486developed languages, such as Java, TypeScript and Dart. The syntax has been
487made to fit with the way Vim script works, such as using `endclass` instead of
488using curly braces around the whole class.
489
490Some common constructs of object-oriented languages were chosen very long ago
491when this kind of programming was still new, and later found to be
492sub-optimal. By this time those constructs were widely used and changing them
493was not an option. In Vim we do have the freedom to make different choices,
494since classes are completely new. We can make the syntax simpler and more
495consistent than what "old" languages use. Without diverting too much, it
496should still mostly look like what you know from existing languages.
497
498Some recently developed languages add all kinds of fancy features that we
499don't need for Vim. But some have nice ideas that we do want to use.
500Thus we end up with a base of what is common in popular languages, dropping
501what looks like a bad idea, and adding some nice features that are easy to
502understand.
503
504The main rules we use to make decisions:
505- Keep it simple.
506- No surprises, mostly do what other languages are doing.
507- Avoid mistakes from the past.
508- Avoid the need for the script writer to consult the help to understand how
509 things work, most things should be obvious.
510- Keep it consistent.
511- Aim at an average size plugin, not at a huge project.
512
513
514Using new() for the constructor ~
515
516Many languages use the class name for the constructor method. A disadvantage
517is that quite often this is a long name. And when changing the class name all
518constructor methods need to be renamed. Not a big deal, but still a
519disadvantage.
520
521Other languages, such as TypeScript, use a specific name, such as
522"constructor()". That seems better. However, using "new" or "new()" to
523create a new object has no obvious relation with "constructor()".
524
525For |Vim9| script using the same method name for all constructors seemed like
526the right choice, and by calling it new() the relation between the caller and
527the method being called is obvious.
528
529
530No overloading of the constructor ~
531
532In Vim script, both legacy and |Vim9| script, there is no overloading of
533functions. That means it is not possible to use the same function name with
534different types of arguments. Therefore there also is only one new()
535constructor.
536
537With |Vim9| script it would be possible to support overloading, since
538arguments are typed. However, this gets complicated very quickly. Looking at
539a new() call one has to inspect the types of the arguments to know which of
540several new() methods is actually being called. And that can require
541inspecting quite a bit of code. For example, if one of the arguments is the
542return value of a method, you need to find that method to see what type it is
543returning.
544
545Instead, every constructor has to have a different name, starting with "new".
546That way multiple constructors with different arguments are possible, while it
547is very easy to see which constructor is being used. And the type of
548arguments can be properly checked.
549
550
551No overloading of methods ~
552
553Same reasoning as for the constructor: It is often not obvious what type
554arguments have, which would make it difficult to figure out what method is
555actually being called. Better just give the methods a different name, then
556type checking will make sure it works as you intended. This rules out
557polymorphism, which we don't really need anyway.
558
559
560Using "this.member" everywhere ~
561
562The object members in various programming languages can often be accessed in
563different ways, depending on the location. Sometimes "this." has to be
564prepended to avoid ambiguity. They are usually declared without "this.".
565That is quite inconsistent and sometimes confusing.
566
567A very common issue is that in the constructor the arguments use the same name
568as the object member. Then for these members "this." needs to be prefixed in
569the body, while for other members this is not needed and often omitted. This
570leads to a mix of members with and without "this.", which is inconsistent.
571
572For |Vim9| classes the "this." prefix is always used. Also for declaring the
573members. Simple and consistent. When looking at the code inside a class it's
574also directly clear which variable references are object members and which
575aren't.
576
577
578Single inheritance and interfaces ~
579
580Some languages support multiple inheritance. Although that can be useful in
581some cases, it makes the rules of how a class works quite complicated.
582Instead, using interfaces to declare what is supported is much simpler. The
583very popular Java language does it this way, and it should be good enough for
584Vim. The "keep it simple" rule applies here.
585
586Explicitly declaring that a class supports an interface makes it easy to see
587what a class is intended for. It also makes it possible to do proper type
588checking. When an interface is changed any class that declares to implement
589it will be checked if that change was also changed. The mechanism to assume a
590class implements an interface just because the methods happen to match is
591brittle and leads to obscure problems, let's not do that.
592
593
594Using class members ~
595
596Using "static member" to declare a class member is very common, nothing new
597here. In |Vim9| script these can be accessed directly by their name. Very
598much like how a script-local variable can be used in a function. Since object
599members are always accessed with "this." prepended, it's also quickly clear
600what kind of member it is.
601
602TypeScript prepends the class name before the class member, also inside the
603class. This has two problems: The class name can be rather long, taking up
604quite a bit of space, and when the class is renamed all these places need to
605be changed too.
606
607
608Using "ClassName.new()" to construct an object ~
609
610Many languages use the "new" operator to create an object, which is actually
611kind of strange, since the constructor is defined as a method with arguments,
612not a command. TypeScript also has the "new" keyword, but the method is
613called "constructor()", it is hard to see the relation between the two.
614
615In |Vim9| script the constructor method is called new(), and it is invoked as
616new(), simple and straightforward. Other languages use "new ClassName()",
617while there is no ClassName() method, it's a method by another name in the
618class called ClassName. Quite confusing.
619
620
621Default read access to object members ~
622
623Some users will remark that the access rules for object members are
624asymmetric. Well, that is intentional. Changing a value is a very different
625action than reading a value. The read operation has no side effects, it can
626be done any number of times without affecting the object. Changing the value
627can have many side effects, and even have a ripple effect, affecting other
628objects.
629
630When adding object members one usually doesn't think much about this, just get
631the type right. And normally the values are set in the new() method.
632Therefore defaulting to read access only "just works" in most cases. And when
633directly writing you get an error, which makes you wonder if you actually want
634to allow that. This helps writing code with fewer mistakes.
635
636
637Making object membes private with an underscore ~
638
639When an object member is private, it can only be read and changed inside the
640class (and in sub-classes), then it cannot be used outside of the class.
641Prepending an underscore is a simple way to make that visible. Various
642programming languages have this as a recommendation.
643
644In case you change your mind and want to make the object member accessible
645outside of the class, you will have to remove the underscore everywhere.
646Since the name only appears in the class (and sub-classes) they will be easy
647to find and change.
648
649The other way around is much harder: you can easily prepend an underscore to
650the object member inside the class to make it private, but any usage elsewhere
651you will have to track down and change. You may have to make it a "set"
652method call. This reflects the real world problem that taking away access
653requires work to be done for all places where that access exists.
654
655An alternative would have been using the "private" keyword, just like "public"
656changes the access in the other direction. Well, that's just to reduce the
657number of keywords.
658
659
660No protected object members ~
661
662Some languages provide several ways to control access to object members. The
663most known is "protected", and the meaning varies from language to language.
664Others are "shared", "private" and even "friend".
665
666These rules make life more difficult. That can be justified in projects where
667many people work on the same, complex code where it is easy to make mistakes.
668Especially when refactoring or other changes to the class model.
669
670The Vim scripts are expected to be used in a plugin, with just one person or a
671small team working on it. Complex rules then only make it more complicated,
672the extra safety provide by the rules isn't really needed. Let's just keep it
673simple and not specify access details.
674
675
676==============================================================================
677
67810. To be done later
679
680Can a newSomething() constructor invoke another constructor? If yes, what are
681the restrictions?
682
683Thoughts:
684- Generics for a class: `class <Tkey, Tentry>`
685- Generics for a function: `def <Tkey> GetLast(key: Tkey)`
686- Mixins: not sure if that is useful, leave out for simplicity.
687
688Some things that look like good additions:
689- For testing: Mock mechanism
690
691An important class to be provided is "Promise". Since Vim is single
692threaded, connecting asynchronous operations is a natural way of allowing
693plugins to do their work without blocking the user. It's a uniform way to
694invoke callbacks and handle timeouts and errors.
695
696
697 vim:tw=78:ts=8:noet:ft=help:norl: