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