You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generics have basic support in methods and classes.
10
+
> [!IMPORTANT]
11
+
> Generics are syntactic sugar for copy-pasting code followed by a search-and-replace of type names.
12
+
>
13
+
> Everything that the generic syntax provides can be achieved without it by writing repetitive code.
11
14
12
-
## Generic Functions
15
+
This repetition is error-prone and tedious, however, and thus the generic syntax keeps the code DRY[^1].
16
+
17
+
The generic syntax introduces *type parameters* / *type variables* whose *type-values* exist during compilation, as opposed to regular parameters and their values that exist during run-time only.
18
+
19
+
Procedures, **Class**es and **Type**s (UDTs) can be made generic.
20
+
21
+
> [!WARNING]
22
+
> Generic **Type**s (UDTs) don't yet support member procedures (error TB5124).
The type variables from the definition's *parameter-list* are substituted with concrete or arguments types provided in the *argument-list*, unless provided explicitly as a type argument in the *type-argument-list*.
42
+
The type variables that were not referenced in the *parameter-list* have to be provided in the *type-argument-list* as *type-arguments*.
43
+
44
+
In the definition, the *type-variable-list*, i.e. **(Of***type-var* ... **)**, introduces genericity. The type variables (*type-var*) introduce identifiers of arbitrary types that can be referenced within:
45
+
46
+
-*parameter-list*,
47
+
-*return-type*, and
48
+
- the body of the procedure.
49
+
50
+
In the invocation, the *type-argument-list*, i.e. **(Of***type-arg* ... **)**, is optional as needed to provide types arguments for those type variables that don't appear in the *parameter-list* of the definition. The type variables that are used within the *parameter-list* are assigned type values of the respective arguments at the call site *unless their values are explicitly provided* in the *type-argument-list*.
51
+
52
+
### Call site type arguments
53
+
54
+
Type variables that correspond to types that could be deduced from the call argument types must form a trailer of the *type-variable-list*:
55
+
56
+
```vb
57
+
SubMySub1(OfT,U,V)(arguAsU,argvAsV):EndSub
58
+
MySub1(OfLong)(33%,42%)' Valid: deduced U, V = Integer
59
+
MySub1(OfLong,Single)(33%,42%)' Valid: provided U = Single, deduced V = Integer
60
+
MySub1(OfLong,Single,Double)(33%,42%)' Valid: provided U = Single, provided V = Double
61
+
MySub1(OfLong,,Double)(33%,42%)' Invalid: omitted deduced type must be trailing
62
+
```
63
+
64
+
Thus, to suppress deduction, put the type variable in the type list *before* the non-deducible type parameters:
13
65
14
66
```vb
15
-
PublicFunctionTCast(OfT)(ByRefExpressionAsT)AsT
16
-
ReturnExpression
67
+
' T must be provided, it won't be deduced
68
+
FunctionMyFn1(OfT,U)(arguAsT)AsU:EndFunction
69
+
MyFn1(OfSingle,String)(10%)' Valid: provided T = Single, U = String
70
+
MyFn1(Of,String)(10%)' Invalid: T is not trailing so it can't be omitted
71
+
' Effectively, the definition of MyFn1
72
+
' suppresses deduction of T
73
+
```
74
+
75
+
Only the unused type variables may have their arguments omitted at positions *after the first* in the *type-variable-list*.:
76
+
77
+
```vb
78
+
SubMySub2(OfT,U,V)(argtAsT,argvAsV):EndSub
79
+
SubMySub3(OfU,V)(argvAsV):EndSub
80
+
81
+
MySub2(OfSingle,,Double)(1%,2%)' Valid: unused U can be omitted as it's not the first
82
+
' in the type-parameter-list
83
+
MySub3(Of,Single)(22%)' Invalid: unused U can't be omitted as it's the first
84
+
' variable in the type-parameter-list
85
+
```
86
+
87
+
### Example 1
88
+
89
+
In this example, the invocations of the generic **First** and **Last** subs don't need to explicitly provide type argument values using the *type-argument-list*, i.e. **(Of** ... **)**, since they can be deduced from the argument types.
This could be used e.g. to return a `Date` typed variable with `TCast(Of Date)("2021-01-01")`
107
+
Without the generic syntax, the procedure would have had to be written for every type *T* it's used on. In the example below, that would be `T=String` and `T=Integer`:
The function **Caster** introduces three type variables within its scope:
149
+
150
+
-**T** is by default deduced from the type of the **value** argument, or can be provided on invocation,
151
+
-**R** is the result type and must be provided on invocation,
152
+
-**U** is a type used in the body of the function and must be provided on invocation.
153
+
154
+
> [!TIP]
155
+
> The order of the type variables in the definition can be chosen so that the trailing variable(s) are used in the *parameter-list*. The type-values of those type variable can thus be omitted if the types inferred from the argument types at the call site are appropriate.
156
+
157
+
1. In the invocation `Example(Of String, Integer)(1.23!)`,
158
+
*T* is deduced to be **Single**, *U* is provided and set to **Integer**, and **R** is provided and set to **String**.
159
+
160
+
2. In the invocation `Example(Of String, Integer, Double)(1.23!)`,
161
+
*T* is provided and set to **Double**, *U* is provided and set to **Integer**, and *R* is provided and set to **String**.
162
+
* First, the compiler will cast `1.23!` to the type of the formal parameter, that is to a **Double**`1.23#`.
163
+
* Then, in the body of the function, the *value* is cast to **Integer** when it's assigned to **intermediate**.
164
+
* Finally, also in the body, the **intermediate** is cast to the result type of **String**, and returned.
A generic class enables substitution of type variables with type arguments provided in an instantiation. Every utterance of a generic class name with type arguments instantiates the generic class type into a regular class type.
206
+
207
+
> [!NOTE]
208
+
> Compile Time: A generic class is instantiated by calling out its name with arguments.
209
+
>
210
+
> Run Time: Objects of those instantiated types can be created.
211
+
212
+
In the example below, two class types are instantiated: **MyClass**(**Integer**) and **MyClass**(**String**). This happens at compile time. No instances of **MyClass** are created at runtime, since both variables default to **Nothing**:
213
+
214
+
```vb
215
+
ClassMyClass(OfT)' ...
216
+
217
+
SubTest()
218
+
DimintVarAsMyClass(Integer)
219
+
DimstrVarAsMyClass(String)
220
+
Debug.AssertintVarIsNothingAndAlsostrVarIsNothing
221
+
EndSub
222
+
```
223
+
224
+
### List Class Example
225
+
226
+
A Class generic allows the type in methods throughout the class. The following example shows this to make a generic List class:
227
+
228
+
```vb
27
229
[COMCreatable(False)]
28
230
ClassList(OfT)
29
-
Private src() As T
30
-
Private c As Long
31
-
Sub New(p() As T)
32
-
src = p
231
+
PrivatemData()AsT
232
+
233
+
SubNew(preset()AsT)
234
+
mData=preset
33
235
EndSub
236
+
34
237
[DefaultMember]
35
-
Function GetAt(ByVal idx As Long) As T
36
-
Return src(idx)
238
+
FunctionGetAt(ByValindex&)AsT
239
+
ReturnmData(index)
37
240
EndFunction
38
-
Public Property Get Count() As Long
39
-
Return c
40
-
End Property
41
241
EndClass
242
+
243
+
SubTest()
244
+
DimliAsAny=NewList(OfInteger)(Array(5,6,7))
245
+
Debug.Assertli(0)=5AndAlsoli(2)=7
246
+
EndSub
247
+
42
248
```
43
249
44
-
## Usage Example
250
+
### List UDT Example
251
+
252
+
While generic UDTs don't support member procedures yet in twinBASIC, the data members are supported:
0 commit comments