From 23e8948b735f0ea5479e8e37150ed23d29acd6b5 Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Thu, 21 May 2026 13:02:15 +0200 Subject: [PATCH 1/6] Initial draft of updates to the .NET interface guide for v21.0 generics --- .../dotnet-classes/advanced-techniques.md | 218 +++++++++++++++--- 1 file changed, 183 insertions(+), 35 deletions(-) diff --git a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md index c84ab5e96c..c47ac53bc0 100644 --- a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md +++ b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md @@ -159,40 +159,117 @@ For example, if MyClass is a .NET class wit ## Generics -The .NET interface supports creating concrete versions of _generic classes_, instantiating them, and calling generic methods. This is done by calling `43⌶` with a right argument of `632` (for more information on `43⌶`, see the _Dyalog APL Language Reference Guide_). Note that this I-beam will be replaced with something that is better integrated into the language in a future Dyalog version. +In .NET, a method, interface, or class can be _generic_, which means it it is a template or recipe for a _concrete_ method, interface or class. What makes them generic is that they have a list of type parameters, and the user must apply a matching number of type arguments to get a concrete version. In the case of methods, it is not always necessary to apply type arguments, as the .NET interface can sometimes perform [type inference](#type-inference) to deduce the type arguments from the types of the method arguments. -A generic class is a class that has type parameters, which must be given values to create a concrete version of the class. Similarly, a generic method has type parameters that must be specified before the method can be called. +The syntax used to apply type arguments to both methods, classes, and interfaces, is the square brackets, as in `G[T]` where `G` is the generic thing, and `T` is a .NET type, or a vector of .NET types, which themselves might be the result of applying types to a generic .NET class. -The result of `43⌶632` is a monadic operator, which is used to apply the type arguments of generic classes or methods. +The square bracket syntax means that working with generics in Dyalog APL and C# looks visually similar, except that C# uses angle brackets instead, as illustrated by the example below: + +```C# +// Instantiate a concrete version of a generic class in C# +new System.Collections.Generic.List(); + +// Call a concrete version of a generic method in C# +System.Decimal.CreateChecked(5); +``` + +And the corresponding APL +```apl +⍝ Instantiate a concrete version of a generic class in APL +⎕NEW System.Collections.Generic.List[System.Int32] + +⍝ Call a concrete version of a generic method in APL +System.Decimal.CreateChecked[System.Int32] 5 +``` ### Creating a Concrete Version of a Generic Class -The class System.Collections.Generic.List is a generic class with one type parameter, which is the type of the elements of the list. +The class System.Collections.Generic.List is a generic class with one type parameter, which is the type of the elements of the list. The display form of the type indicates that it is generic: -A concrete version of the List class can be created using `43⌶632`. For example, a list class that contains integers can be created as follows: ```apl - IntList←System.Collections.Generic.List(43⌶632)System.Int32 - IntList -(System.Collections.Generic.List`1[System.Int32]) + ⎕USING←'' + System.Collections.Generic.List +(System.Collections.Generic.List[T]) +``` + +A concrete version of the List class can be created using square brackets. For example, a list class that contains integers can be created as follows: +```apl + ⎕USING←'' + IntList←System.Collections.Generic.List[System.Int32] + IntList +(System.Collections.Generic.List[System.Int32]) ``` The shared members of the IntList class can then be accessed, and the class instantiated using `⎕NEW`. It is not necessary to give the constructed class a name. -The operations can also be combined and multiple type argument specified. For example: +The operations can also be combined and multiple type arguments specified. For example: ```apl - types←System.Char System.Int32 - ⎕NEW System.Collections.Generic.Dictionary (43⌶632) types + ⎕USING←'' + types←System.Char System.Int32 + ⎕NEW System.Collections.Generic.Dictionary[types] System.Collections.Generic.Dictionary`2[System.Char,System.Int32] +``` +Attempting to instantiate a generic class without the expected number of type arguments generates an error. For example: +```apl + ⎕USING←'' + ⎕NEW System.Collections.Generic.List +LENGTH ERROR: No overload of the type expects the given number of generic type arguments: 0 + ⎕NEW System.Collections.Generic.List + ∧ ``` -Attempting to instantiate a generic class without the expected number of type arguments generates an exception. For example: +Similarly, applying too many type arguments also results in an error: + ```apl - ⎕USING←'' - ⎕NEW System.Collections.Generic.List -EXCEPTION: Invalid number of generic type arguments applied (expected 1, got 0) - ⎕NEW System.Collections.Generic.List - ∧ + ⎕USING←'' + System.Collections.Generic.List[3⍴System.Int32] +LENGTH ERROR: No overload of the type expects the given number of generic type arguments: 3 + System.Collections.Generic.List[3⍴System.Int32] +``` +### Creating a Concrete Version of a Generic Interface + +Applying type arguments to generic interfaces looks just like with generic classes. The example below defines a function `IsBoolCollection`, which checks whether a given .NET type implements the concrete version `ICollection[Boolean]` of the generic `ICollection` interface, which is often implemented by data structures that act as collections of elements of a specific type. + +```apl + ⎕USING←'System' 'System.Collections.Generic' + + IsBoolCollection←{ICollection[Boolean]∊∊⎕CLASS ⍵} + + a←⎕NEW HashSet[Int32] + b←⎕NEW List[Boolean] + c←⎕NEW Dictionary[Int32 Boolean] + + IsBoolCollection¨a b c +0 1 0 +``` + +### Multiple Overloads of .NET Classes and Interfaces +Some .NET classes and interfaces have multiple overloads, varying in the number of generic type parameters. The display form of the type makes this clear, and the the .NET interface will automatically use the appropriate overload based on context. + +```apl + ⎕USING←'System' + ValueTuple +(System.ValueTuple) +(System.ValueTuple[T1]) +(System.ValueTuple[T1,T2]) +(System.ValueTuple[T1,T2,T3]) +(System.ValueTuple[T1,T2,T3,T4]) +(System.ValueTuple[T1,T2,T3,T4,T5]) +(System.ValueTuple[T1,T2,T3,T4,T5,T6]) +(System.ValueTuple[T1,T2,T3,T4,T5,T6,T7]) +(System.ValueTuple[T1,T2,T3,T4,T5,T6,T7,TRest]) +``` + +The ValueTuple class has one non-generic overload, and 8 generic overloads. + +```apl + ⎕USING←'System' + ValueTuple[Int32] ⍝ Create concrete version of overload with 1 generic parameter +(System.ValueTuple[System.Int32]) + + ValueTuple[Int32 Boolean] ⍝ Create concrete version of overload with 2 generic parameters +(System.ValueTuple[System.Int32,System.Boolean]) ``` ### Calling a Generic Method @@ -206,34 +283,105 @@ System.Decimal CreateChecked[TOther](TOther) The CreateChecked function has one type parameter, shown in square brackets, and one regular parameter, shown in parentheses. -The generic type argument can be applied using `43⌶632`. When the monadic operator `43⌶632` is run with a .NET method as its left argument, and a scalar type or a vector of .NET types as its right argument, the result is a derived function that calls the specified .NET method with both the generic type arguments and regular arguments applied. For example: +The generic type argument can be applied using square brackets, and the result is concrete version of the generic method. The method can either be given a name, or evaluated directly. The display form indicates that the type parameters have been replaced by a concrete type. + ```apl - fn←System.Decimal.CreateChecked (43⌶632) System.Int32 - fn 123 -123 + ⎕USING←'System' + fn←Decimal.CreateChecked[Int32] + fn +System.Decimal CreateChecked[Int32](Int32) + fn 10 +10 + Decimal.CreateChecked[Int32] 50 +50 ``` -Attempting to call a method without specifying the type argument generates an exception. For example: +If a generic method has overloads with different numbers of type parameters, apply type arguments will narrow down the list of overloads which are applicable, as in the example below where only one overload expects a single type argument. + ```apl - System.Decimal.CreateChecked 123 -EXCEPTION: Invalid number of generic type arguments applied (expected 1, got 0) - System.Decimal.CreateChecked 123 - ∧ + ⎕USING←'System' + ValueTuple.Create +System.ValueTuple Create() +System.ValueTuple`1[T1] Create[T1](T1) +System.ValueTuple`2[T1,T2] Create[T1,T2](T1, T2) +System.ValueTuple`3[T1,T2,T3] Create[T1,T2,T3](T1, T2, T3) +... + + ValueTuple.Create[Boolean] +System.ValueTuple`1[System.Boolean] Create[Boolean](Boolean) ``` -### Calling a Niladic Generic Method +.NET methods with only a single overload which expects no arguments, are usually imported into APL as niladic functions, but when they are generic, they are imported as monadic functions so that the type arguments can be applied. -.NET methods that only export overloads with zero parameters are exported as niladic functions, which means they cannot be passed as an operand to `43⌶632` without being evaluated. To accomodate this, `43⌶632` also accepts a character vector as its left argument. In this case, it runs the .NET method with that name, with the generic type arguments applied. For example: ```apl - r←'System.Array.Empty'(43⌶632)System.Int32 - r≡⍬ + ⎕USING←'System' + Array.Empty +T[] Empty[T]() + + Array.Empty[Int32] +Int32[] Empty[Int32]() + + r←Array.Empty[Int32] ⍬ + r≡⍬ 1 ``` -Attempting to evaluate the niladic function without calling `43⌶632` generates an exception. For example: +Applying an incorrect number of type arguments to a method will generate an error: + ```apl - System.Array.Empty -EXCEPTION: Invalid number of generic type arguments applied (expected 1, got 0) - System.Array.Empty - ∧ + ⎕USING←'System' + Decimal.CreateChecked 50 +LENGTH ERROR: No overload of the method expects the given number of generic type arguments: 0 + Decimal.CreateChecked 50 + ∧ + + ValueTuple.Create[10⍴Int32] +LENGTH ERROR: No overload of the method expects the given number of generic type arguments: 10 + ValueTuple.Create[10⍴Int32] + ∧ ``` + +However, in some cases [type inference](#type-inference) can take place, and no error will be generated. + +#### Type Inference + +If the arguments to a generic method have a concrete .NET type, then their type information might be enough for the .NET bridge to unambiguously select a method overload, and to automatically apply the needed type arguments. But if there is any doubt about the type, such as when the arguments are regular APL arrays, such as the scalar `0`, which can be converted into a number of different .NET types, type inference will not take place. + +``` + ⎕USING←'System' 'System.Threading.Tasks' + Task.FromResult +System.Threading.Tasks.Task`1[TResult] FromResult[TResult](TResult) + + Task.FromResult 123 +LENGTH ERROR: No overload of the method expects the given number of generic type arguments: 0 + Task.FromResult 123 + ∧ + + ⍝ Explicitly apply type arguments + Task.FromResult[Int128] 123 +System.Threading.Tasks.Task`1[System.Int128] + + ⍝ Explicitly apply type arguments, but pass in a .NET object + i128←Int128.Parse ⊂'123' + Task.FromResult[Int128] i128 +System.Threading.Tasks.Task`1[System.Int128] + + ⍝ Let the bridge infer the type argument from argument's .NET type + Task.FromResult i128 +System.Threading.Tasks.Task`1[System.Int128] +``` + +Type inference can save the programmer from doing some typing which feels unnecessary, as shown in the last lines of the example above, but it is still allowed to manually apply type arguments. + +If the user has [specified an overload](#specifying-overloads), the type information is taken into account, which means another way of doing the above would be: + +```apl + ⎕USING←'System' 'System.Threading.Tasks' + Task.FromResult +System.Threading.Tasks.Task`1[TResult] FromResult[TResult](TResult) + + Task.FromResult⍠Int128⊢123 +System.Threading.Tasks.Task`1[System.Int128] +``` + +which works because we tell the .NET bridge that we want the overload that takes an Int128 as its argument, which makes the type parameter `TResult` *must* be Int128, and therefore there is no need to explicitly apply the type arguments using square brackets. \ No newline at end of file From 6e9cef3d6c99da4941d49ff6265e31c2769f884c Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Tue, 26 May 2026 10:33:07 +0200 Subject: [PATCH 2/6] Small tweaks to .NET generics docs --- .../docs/dotnet-classes/advanced-techniques.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md index c47ac53bc0..4cba76dbb1 100644 --- a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md +++ b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md @@ -200,9 +200,9 @@ A concrete version of the List class can be (System.Collections.Generic.List[System.Int32]) ``` -The shared members of the IntList class can then be accessed, and the class instantiated using `⎕NEW`. It is not necessary to give the constructed class a name. +The shared members of the IntList class can then be accessed, and the class instantiated using `⎕NEW`. -The operations can also be combined and multiple type arguments specified. For example: +It is not necessary to give the constructed class a name before creating instances of it. Multiple type arguments can also be specified. For example: ```apl ⎕USING←'' types←System.Char System.Int32 @@ -296,7 +296,7 @@ System.Decimal CreateChecked[Int32](Int32) 50 ``` -If a generic method has overloads with different numbers of type parameters, apply type arguments will narrow down the list of overloads which are applicable, as in the example below where only one overload expects a single type argument. +If a generic method has overloads with different numbers of type parameters, applying type arguments will narrow down the list of overloads which are applicable, as in the example below where only one overload expects a single type argument. ```apl ⎕USING←'System' @@ -341,7 +341,7 @@ LENGTH ERROR: No overload of the method expects the given number of generic type ∧ ``` -However, in some cases [type inference](#type-inference) can take place, and no error will be generated. +However when no type arguments are applied, in some cases [type inference](#type-inference) can take place, and no error will be generated. #### Type Inference @@ -361,7 +361,7 @@ LENGTH ERROR: No overload of the method expects the given number of generic type Task.FromResult[Int128] 123 System.Threading.Tasks.Task`1[System.Int128] - ⍝ Explicitly apply type arguments, but pass in a .NET object + ⍝ Explicitly apply type arguments, and pass in a .NET object of that type i128←Int128.Parse ⊂'123' Task.FromResult[Int128] i128 System.Threading.Tasks.Task`1[System.Int128] @@ -384,4 +384,4 @@ System.Threading.Tasks.Task`1[TResult] FromResult[TResult](TResult) System.Threading.Tasks.Task`1[System.Int128] ``` -which works because we tell the .NET bridge that we want the overload that takes an Int128 as its argument, which makes the type parameter `TResult` *must* be Int128, and therefore there is no need to explicitly apply the type arguments using square brackets. \ No newline at end of file +which works because we tell the .NET bridge that we want the overload that takes an Int128 as its argument, which means the type parameter `TResult` *must* be Int128, and therefore there is no need to explicitly apply the type arguments using square brackets. \ No newline at end of file From 9d6c360129f7bf01f993ddf7159ed5f233694475 Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Wed, 27 May 2026 12:21:58 +0200 Subject: [PATCH 3/6] Fix "it it" typo --- .../docs/dotnet-classes/advanced-techniques.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md index 4cba76dbb1..2fed5f793c 100644 --- a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md +++ b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md @@ -159,7 +159,7 @@ For example, if MyClass is a .NET class wit ## Generics -In .NET, a method, interface, or class can be _generic_, which means it it is a template or recipe for a _concrete_ method, interface or class. What makes them generic is that they have a list of type parameters, and the user must apply a matching number of type arguments to get a concrete version. In the case of methods, it is not always necessary to apply type arguments, as the .NET interface can sometimes perform [type inference](#type-inference) to deduce the type arguments from the types of the method arguments. +In .NET, a method, interface, or class can be _generic_, which means it is a template or recipe for a _concrete_ method, interface or class. What makes them generic is that they have a list of type parameters, and the user must apply a matching number of type arguments to get a concrete version. In the case of methods, it is not always necessary to apply type arguments, as the .NET interface can sometimes perform [type inference](#type-inference) to deduce the type arguments from the types of the method arguments. The syntax used to apply type arguments to both methods, classes, and interfaces, is the square brackets, as in `G[T]` where `G` is the generic thing, and `T` is a .NET type, or a vector of .NET types, which themselves might be the result of applying types to a generic .NET class. From 67d46043282806db0bc189aa7c110053abee3c6c Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Wed, 27 May 2026 13:35:16 +0200 Subject: [PATCH 4/6] Apply suggestions from code review Some suggestions seems to have been created in such a way that they suggest replacing a big chunk of text with an updated version of only the last bit. I will commit those suggestions manually. Co-authored-by: johndaintree --- .../docs/dotnet-classes/advanced-techniques.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md index 2fed5f793c..30b70c7d39 100644 --- a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md +++ b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md @@ -161,9 +161,9 @@ For example, if MyClass is a .NET class wit In .NET, a method, interface, or class can be _generic_, which means it is a template or recipe for a _concrete_ method, interface or class. What makes them generic is that they have a list of type parameters, and the user must apply a matching number of type arguments to get a concrete version. In the case of methods, it is not always necessary to apply type arguments, as the .NET interface can sometimes perform [type inference](#type-inference) to deduce the type arguments from the types of the method arguments. -The syntax used to apply type arguments to both methods, classes, and interfaces, is the square brackets, as in `G[T]` where `G` is the generic thing, and `T` is a .NET type, or a vector of .NET types, which themselves might be the result of applying types to a generic .NET class. +The syntax used to apply type arguments to methods, classes, and interfaces, is square brackets, for example `G[T]` where `G` is the generic entity, and `T` is a .NET type, or a vector of .NET types. These types themselves might be the result of applying types to a generic .NET class. -The square bracket syntax means that working with generics in Dyalog APL and C# looks visually similar, except that C# uses angle brackets instead, as illustrated by the example below: +The square bracket syntax means that working with generics in Dyalog APL and C# looks visually similar, except that C# uses angle brackets, as illustrated by the example below: ```C# // Instantiate a concrete version of a generic class in C# @@ -283,7 +283,7 @@ System.Decimal CreateChecked[TOther](TOther) The CreateChecked function has one type parameter, shown in square brackets, and one regular parameter, shown in parentheses. -The generic type argument can be applied using square brackets, and the result is concrete version of the generic method. The method can either be given a name, or evaluated directly. The display form indicates that the type parameters have been replaced by a concrete type. +The generic type argument can be applied using square brackets, and the result is a concrete version of the generic method. The method can either be given a name, or evaluated directly. The display form indicates that the type parameters have been replaced to form a concrete function. ```apl ⎕USING←'System' @@ -371,7 +371,7 @@ System.Threading.Tasks.Task`1[System.Int128] System.Threading.Tasks.Task`1[System.Int128] ``` -Type inference can save the programmer from doing some typing which feels unnecessary, as shown in the last lines of the example above, but it is still allowed to manually apply type arguments. +Type inference can save the programmer from doing unnecessary typing, as shown in the last lines of the example above, but it is still allowed to manually apply type arguments. If the user has [specified an overload](#specifying-overloads), the type information is taken into account, which means another way of doing the above would be: From 51bf80c47a8ddaf353c3d813204328a5ee95c1ec Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Wed, 27 May 2026 13:43:02 +0200 Subject: [PATCH 5/6] More suggestions from review --- .../docs/dotnet-classes/advanced-techniques.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md index 30b70c7d39..be9f635726 100644 --- a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md +++ b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md @@ -159,7 +159,7 @@ For example, if MyClass is a .NET class wit ## Generics -In .NET, a method, interface, or class can be _generic_, which means it is a template or recipe for a _concrete_ method, interface or class. What makes them generic is that they have a list of type parameters, and the user must apply a matching number of type arguments to get a concrete version. In the case of methods, it is not always necessary to apply type arguments, as the .NET interface can sometimes perform [type inference](#type-inference) to deduce the type arguments from the types of the method arguments. +In .NET, a method, interface, or class can be _generic_, which means that it is a template or recipe for a _concrete_ method, interface or class. What makes them generic is that they have a list of type parameters, and the user must apply a matching number of type arguments to create a concrete version. In the case of methods, it is not always necessary to apply type arguments, as the .NET interface can sometimes perform [type inference](#type-inference) to deduce the type arguments from the types of the method arguments. The syntax used to apply type arguments to methods, classes, and interfaces, is square brackets, for example `G[T]` where `G` is the generic entity, and `T` is a .NET type, or a vector of .NET types. These types themselves might be the result of applying types to a generic .NET class. @@ -245,7 +245,7 @@ Applying type arguments to generic interfaces looks just like with generic class ``` ### Multiple Overloads of .NET Classes and Interfaces -Some .NET classes and interfaces have multiple overloads, varying in the number of generic type parameters. The display form of the type makes this clear, and the the .NET interface will automatically use the appropriate overload based on context. +Some .NET classes and interfaces have multiple overloads, varying in the number of generic type parameters. The display form of the type makes this clear, and the .NET interface will automatically use the appropriate overload based on context. ```apl ⎕USING←'System' @@ -345,7 +345,7 @@ However when no type arguments are applied, in some cases [type inference](#type #### Type Inference -If the arguments to a generic method have a concrete .NET type, then their type information might be enough for the .NET bridge to unambiguously select a method overload, and to automatically apply the needed type arguments. But if there is any doubt about the type, such as when the arguments are regular APL arrays, such as the scalar `0`, which can be converted into a number of different .NET types, type inference will not take place. +If the arguments to a generic method have a concrete .NET type, then their type information might be enough for the .NET bridge to unambiguously select a method overload, and to automatically apply the needed type arguments. But if there is any doubt about the type, such as when the arguments are regular APL arrays, for example the scalar `0`, which can be converted into a number of different .NET types, type inference will not take place. ``` ⎕USING←'System' 'System.Threading.Tasks' From 2d6e6293d26cc205d25468049ac0099771395bb5 Mon Sep 17 00:00:00 2001 From: Peter Mikkelsen Date: Wed, 27 May 2026 14:03:48 +0200 Subject: [PATCH 6/6] Change DMX message and update the examples --- .../docs/dotnet-classes/advanced-techniques.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md index be9f635726..56d16096c8 100644 --- a/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md +++ b/dotnet-interface-guide/docs/dotnet-classes/advanced-techniques.md @@ -214,7 +214,7 @@ Attempting to instantiate a generic class without the expected number of type ar ```apl ⎕USING←'' ⎕NEW System.Collections.Generic.List -LENGTH ERROR: No overload of the type expects the given number of generic type arguments: 0 +LENGTH ERROR: No overload of the type expects the given number (0) of generic type arguments ⎕NEW System.Collections.Generic.List ∧ ``` @@ -224,7 +224,7 @@ Similarly, applying too many type arguments also results in an error: ```apl ⎕USING←'' System.Collections.Generic.List[3⍴System.Int32] -LENGTH ERROR: No overload of the type expects the given number of generic type arguments: 3 +LENGTH ERROR: No overload of the type expects the given number (3) of generic type arguments System.Collections.Generic.List[3⍴System.Int32] ``` ### Creating a Concrete Version of a Generic Interface @@ -331,12 +331,12 @@ Applying an incorrect number of type arguments to a method will generate an erro ```apl ⎕USING←'System' Decimal.CreateChecked 50 -LENGTH ERROR: No overload of the method expects the given number of generic type arguments: 0 +LENGTH ERROR: No overload of the method expects the given number (0) of generic type arguments Decimal.CreateChecked 50 ∧ ValueTuple.Create[10⍴Int32] -LENGTH ERROR: No overload of the method expects the given number of generic type arguments: 10 +LENGTH ERROR: No overload of the method expects the given number (10) of generic type arguments ValueTuple.Create[10⍴Int32] ∧ ``` @@ -353,7 +353,7 @@ If the arguments to a generic method have a concrete .NET type, then their type System.Threading.Tasks.Task`1[TResult] FromResult[TResult](TResult) Task.FromResult 123 -LENGTH ERROR: No overload of the method expects the given number of generic type arguments: 0 +LENGTH ERROR: No overload of the method expects the given number (0) of generic type arguments Task.FromResult 123 ∧