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