blob: 7785fbe130e3f576946a698e1f5cdfc635a68634 [file] [log] [blame]
Bram Moolenaar7db29e42022-12-11 15:53:04 +00001*vim9class.txt* For Vim version 9.0. Last change: 2022 Dec 11
Bram Moolenaarc1c365c2022-12-04 20:13:24 +00002
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
Bram Moolenaar7db29e42022-12-11 15:53:04 +000099< *object* *Object*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000100You 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})'
Bram Moolenaar7db29e42022-12-11 15:53:04 +0000107< *E1317* *E1327*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000108If 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
Bram Moolenaar7db29e42022-12-11 15:53:04 +00003325. More class details *Vim9-class* *Class* *class*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000333
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
Bram Moolenaar7db29e42022-12-11 15:53:04 +0000339A class can only be defined in a |Vim9| script file. *E1316*
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000340A class cannot be defined inside a function.
341
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000342It is possible to define more than one class in a script file. Although it
343usually is better to export only one main class. It can be useful to define
344types, enums and helper classes though.
345
346The `:abstract` keyword may be prefixed and `:export` may be used. That gives
347these variants: >
348
349 class ClassName
350 endclass
351
352 export class ClassName
353 endclass
354
355 abstract class ClassName
356 endclass
357
358 export abstract class ClassName
359 endclass
360<
361 *E1314*
362The class name should be CamelCased. It must start with an uppercase letter.
363That avoids clashing with builtin types.
Bram Moolenaar7db29e42022-12-11 15:53:04 +0000364 *E1315*
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000365After the class name these optional items can be used. Each can appear only
366once. They can appear in any order, although this order is recommended: >
367 extends ClassName
368 implements InterfaceName, OtherInterface
369 specifies SomeInterface
370< *extends*
371A class can extend one other class.
372 *implements*
373A class can implement one or more interfaces.
374 *specifies*
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000375A class can declare its interface, the object members and methods, with a
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000376named interface. This avoids the need for separately specifying the
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000377interface, which is often done in many languages, especially Java.
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000378
379
Bram Moolenaar7db29e42022-12-11 15:53:04 +0000380Items in a class ~
381 *E1318* *E1325* *E1326*
382Inside a class, in betweeen `:class` and `:endclass`, these items can appear:
383- An object member declaration: >
384 this._memberName: memberType
385 this.memberName: memberType
386 public this.memberName: memberType
387- A constructor method: >
388 def new(arguments)
389 def newName(arguments)
390- An object method: >
391 def SomeMethod(arguments)
392
393
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000394Defining an interface ~
395 *:interface* *:endinterface*
396An interface is defined between `:interface` and `:endinterface`. It may be
397prefixed with `:export`: >
398
399 interface InterfaceName
400 endinterface
401
402 export interface InterfaceName
403 endinterface
404
405An interface can declare object members, just like in a class but without any
406initializer.
407
408An interface can declare methods with `:def`, including the arguments and
409return type, but without the body and without `:enddef`. Example: >
410
411 interface HasSurface
412 this.size: number
413 def Surface(): number
414 endinterface
415
416The "Has" prefix can be used to make it easier to guess this is an interface
417name, with a hint about what it provides.
418
419
420Default constructor ~
421
422In case you define a class without a new() method, one will be automatically
423defined. This default constructor will have arguments for all the object
424members, in the order they were specified. Thus if your class looks like: >
425
426 class AutoNew
427 this.name: string
428 this.age: number
429 this.gender: Gender
430 endclass
431
432Then The default constructor will be: >
433
Bram Moolenaar7db29e42022-12-11 15:53:04 +0000434 def new(this.name = void, this.age = void, this.gender = void)
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000435 enddef
436
437All object members will be used, also private access ones.
438
Bram Moolenaar7db29e42022-12-11 15:53:04 +0000439The "= void" default values make the arguments optional. Thus you can also
440call `new()` without any arguments. Since "void" isn't an actual value, no
441assignment will happen and the default value for the object members will be
442used. This is a more useful example, with default values: >
443
444 class TextPosition
445 this.lnum: number = 1
446 this.col: number = 1
447 endclass
448
449If you want the constructor to have mandatory arguments, you need to write it
450yourself. For example, if for the AutoNew class above you insist on getting
451the name, you can define the constructor like this: >
452
453 def new(this.name, this.age = void, this.gender = void)
454 enddef
455
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000456
457Multiple constructors ~
458
459Normally a class has just one new() constructor. In case you find that the
460constructor is often called with the same arguments you may want to simplify
461your code by putting those arguments into a second constructor method. For
462example, if you tend to use the color black a lot: >
463
464 def new(this.garment, this.color, this.size)
465 enddef
466 ...
467 var pants = new(Garment.pants, Color.black, "XL")
468 var shirt = new(Garment.shirt, Color.black, "XL")
469 var shoes = new(Garment.shoes, Color.black, "45")
470
471Instead of repeating the color every time you can add a constructor that
472includes it: >
473
474 def newBlack(this.garment, this.size)
475 this.color = Color.black
476 enddef
477 ...
478 var pants = newBlack(Garment.pants, "XL")
479 var shirt = newBlack(Garment.shirt, "XL")
480 var shoes = newBlack(Garment.shoes, "9.5")
481
482Note that the method name must start with "new". If there is no method called
483"new()" then the default constructor is added, even though there are other
484constructor methods.
485
486
487==============================================================================
488
4896. Type definition *Vim9-type* *:type*
490
491A type definition is giving a name to a type specification. For Example: >
492
493 :type ListOfStrings list<string>
494
495TODO: more explanation
496
497
498==============================================================================
499
5007. Enum *Vim9-enum* *:enum* *:endenum*
501
502An enum is a type that can have one of a list of values. Example: >
503
504 :enum Color
505 White
506 Red
507 Green
508 Blue
509 Black
510 :endenum
511
512TODO: more explanation
513
514
515==============================================================================
516
5179. Rationale
518
519Most of the choices for |Vim9| classes come from popular and recently
520developed languages, such as Java, TypeScript and Dart. The syntax has been
521made to fit with the way Vim script works, such as using `endclass` instead of
522using curly braces around the whole class.
523
524Some common constructs of object-oriented languages were chosen very long ago
525when this kind of programming was still new, and later found to be
526sub-optimal. By this time those constructs were widely used and changing them
527was not an option. In Vim we do have the freedom to make different choices,
528since classes are completely new. We can make the syntax simpler and more
529consistent than what "old" languages use. Without diverting too much, it
530should still mostly look like what you know from existing languages.
531
532Some recently developed languages add all kinds of fancy features that we
533don't need for Vim. But some have nice ideas that we do want to use.
534Thus we end up with a base of what is common in popular languages, dropping
535what looks like a bad idea, and adding some nice features that are easy to
536understand.
537
538The main rules we use to make decisions:
539- Keep it simple.
540- No surprises, mostly do what other languages are doing.
541- Avoid mistakes from the past.
542- Avoid the need for the script writer to consult the help to understand how
543 things work, most things should be obvious.
544- Keep it consistent.
545- Aim at an average size plugin, not at a huge project.
546
547
548Using new() for the constructor ~
549
550Many languages use the class name for the constructor method. A disadvantage
551is that quite often this is a long name. And when changing the class name all
552constructor methods need to be renamed. Not a big deal, but still a
553disadvantage.
554
555Other languages, such as TypeScript, use a specific name, such as
556"constructor()". That seems better. However, using "new" or "new()" to
557create a new object has no obvious relation with "constructor()".
558
559For |Vim9| script using the same method name for all constructors seemed like
560the right choice, and by calling it new() the relation between the caller and
561the method being called is obvious.
562
563
564No overloading of the constructor ~
565
566In Vim script, both legacy and |Vim9| script, there is no overloading of
567functions. That means it is not possible to use the same function name with
568different types of arguments. Therefore there also is only one new()
569constructor.
570
571With |Vim9| script it would be possible to support overloading, since
572arguments are typed. However, this gets complicated very quickly. Looking at
573a new() call one has to inspect the types of the arguments to know which of
574several new() methods is actually being called. And that can require
575inspecting quite a bit of code. For example, if one of the arguments is the
576return value of a method, you need to find that method to see what type it is
577returning.
578
579Instead, every constructor has to have a different name, starting with "new".
580That way multiple constructors with different arguments are possible, while it
581is very easy to see which constructor is being used. And the type of
582arguments can be properly checked.
583
584
585No overloading of methods ~
586
587Same reasoning as for the constructor: It is often not obvious what type
588arguments have, which would make it difficult to figure out what method is
589actually being called. Better just give the methods a different name, then
590type checking will make sure it works as you intended. This rules out
591polymorphism, which we don't really need anyway.
592
593
594Using "this.member" everywhere ~
595
596The object members in various programming languages can often be accessed in
597different ways, depending on the location. Sometimes "this." has to be
598prepended to avoid ambiguity. They are usually declared without "this.".
599That is quite inconsistent and sometimes confusing.
600
601A very common issue is that in the constructor the arguments use the same name
602as the object member. Then for these members "this." needs to be prefixed in
603the body, while for other members this is not needed and often omitted. This
604leads to a mix of members with and without "this.", which is inconsistent.
605
606For |Vim9| classes the "this." prefix is always used. Also for declaring the
607members. Simple and consistent. When looking at the code inside a class it's
608also directly clear which variable references are object members and which
609aren't.
610
611
612Single inheritance and interfaces ~
613
614Some languages support multiple inheritance. Although that can be useful in
615some cases, it makes the rules of how a class works quite complicated.
616Instead, using interfaces to declare what is supported is much simpler. The
617very popular Java language does it this way, and it should be good enough for
618Vim. The "keep it simple" rule applies here.
619
620Explicitly declaring that a class supports an interface makes it easy to see
621what a class is intended for. It also makes it possible to do proper type
622checking. When an interface is changed any class that declares to implement
623it will be checked if that change was also changed. The mechanism to assume a
624class implements an interface just because the methods happen to match is
625brittle and leads to obscure problems, let's not do that.
626
627
628Using class members ~
629
630Using "static member" to declare a class member is very common, nothing new
631here. In |Vim9| script these can be accessed directly by their name. Very
632much like how a script-local variable can be used in a function. Since object
633members are always accessed with "this." prepended, it's also quickly clear
634what kind of member it is.
635
636TypeScript prepends the class name before the class member, also inside the
637class. This has two problems: The class name can be rather long, taking up
638quite a bit of space, and when the class is renamed all these places need to
639be changed too.
640
641
642Using "ClassName.new()" to construct an object ~
643
644Many languages use the "new" operator to create an object, which is actually
645kind of strange, since the constructor is defined as a method with arguments,
646not a command. TypeScript also has the "new" keyword, but the method is
647called "constructor()", it is hard to see the relation between the two.
648
649In |Vim9| script the constructor method is called new(), and it is invoked as
650new(), simple and straightforward. Other languages use "new ClassName()",
651while there is no ClassName() method, it's a method by another name in the
652class called ClassName. Quite confusing.
653
654
655Default read access to object members ~
656
657Some users will remark that the access rules for object members are
658asymmetric. Well, that is intentional. Changing a value is a very different
659action than reading a value. The read operation has no side effects, it can
660be done any number of times without affecting the object. Changing the value
661can have many side effects, and even have a ripple effect, affecting other
662objects.
663
664When adding object members one usually doesn't think much about this, just get
665the type right. And normally the values are set in the new() method.
666Therefore defaulting to read access only "just works" in most cases. And when
667directly writing you get an error, which makes you wonder if you actually want
668to allow that. This helps writing code with fewer mistakes.
669
670
Bram Moolenaar00b28d62022-12-08 15:32:33 +0000671Making object members private with an underscore ~
Bram Moolenaarc1c365c2022-12-04 20:13:24 +0000672
673When an object member is private, it can only be read and changed inside the
674class (and in sub-classes), then it cannot be used outside of the class.
675Prepending an underscore is a simple way to make that visible. Various
676programming languages have this as a recommendation.
677
678In case you change your mind and want to make the object member accessible
679outside of the class, you will have to remove the underscore everywhere.
680Since the name only appears in the class (and sub-classes) they will be easy
681to find and change.
682
683The other way around is much harder: you can easily prepend an underscore to
684the object member inside the class to make it private, but any usage elsewhere
685you will have to track down and change. You may have to make it a "set"
686method call. This reflects the real world problem that taking away access
687requires work to be done for all places where that access exists.
688
689An alternative would have been using the "private" keyword, just like "public"
690changes the access in the other direction. Well, that's just to reduce the
691number of keywords.
692
693
694No protected object members ~
695
696Some languages provide several ways to control access to object members. The
697most known is "protected", and the meaning varies from language to language.
698Others are "shared", "private" and even "friend".
699
700These rules make life more difficult. That can be justified in projects where
701many people work on the same, complex code where it is easy to make mistakes.
702Especially when refactoring or other changes to the class model.
703
704The Vim scripts are expected to be used in a plugin, with just one person or a
705small team working on it. Complex rules then only make it more complicated,
706the extra safety provide by the rules isn't really needed. Let's just keep it
707simple and not specify access details.
708
709
710==============================================================================
711
71210. To be done later
713
714Can a newSomething() constructor invoke another constructor? If yes, what are
715the restrictions?
716
717Thoughts:
718- Generics for a class: `class <Tkey, Tentry>`
719- Generics for a function: `def <Tkey> GetLast(key: Tkey)`
720- Mixins: not sure if that is useful, leave out for simplicity.
721
722Some things that look like good additions:
723- For testing: Mock mechanism
724
725An important class to be provided is "Promise". Since Vim is single
726threaded, connecting asynchronous operations is a natural way of allowing
727plugins to do their work without blocking the user. It's a uniform way to
728invoke callbacks and handle timeouts and errors.
729
730
731 vim:tw=78:ts=8:noet:ft=help:norl: