diff --git a/data/corefork.telegram.org/constructor/decryptedMessageActionAbortKey.html b/data/corefork.telegram.org/constructor/decryptedMessageActionAbortKey.html new file mode 100644 index 0000000000..d63b4eec26 --- /dev/null +++ b/data/corefork.telegram.org/constructor/decryptedMessageActionAbortKey.html @@ -0,0 +1,148 @@ + + +
+ +Abort rekeying
+===20===
+decryptedMessageActionAbortKey#dd05ec6b exchange_id:long = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
exchange_id | +long | +Exchange ID | +
Request for the other party in a Secret Chat to automatically resend a contiguous range of previously sent messages, as explained in Sequence number is Secret Chats.
+===17===
+decryptedMessageActionResend#511110b0 start_seq_no:int end_seq_no:int = DecryptedMessageAction;
+Name | +Type | +Description | +
---|---|---|
start_seq_no | +int | +out_seq_no of the first message to be resent, with correct parity |
+
end_seq_no | +int | +out_seq_no of the last message to be resent, with same parity. |
+
Audio file attached to a secret chat message.
+===8===
+decryptedMessageMediaAudio#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===17===
+decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
duration | +int | +Audio duration in seconds | +
mime_type | +string | +MIME-type of the audio file Parameter added in Layer 13. |
+
size | +int | +File size | +
key | +bytes | +Key to decrypt the attached media file | +
iv | +bytes | +Initialization vector | +
Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
Document attached to a message in a secret chat.
+===8===
+decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===45===
+decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
thumb | +bytes | +Thumbnail-file contents (JPEG-file, quality 55, set in a 90x90 square) | +
thumb_w | +int | +Thumbnail width | +
thumb_h | +int | +Thumbnail height | +
mime_type | +string | +File MIME-type | +
size | +int | +Document size | +
key | +bytes | +Key to decrypt the attached document file | +
iv | +bytes | +Initialization | +
attributes | +Vector<DocumentAttribute> | +Document attributes for media types | +
caption | +string | +Caption | +
Empty constructor, no media content.
+===8===
+decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
+This constructor does not require any parameters.
+GeoPont attached to an encrypted message.
+===8===
+decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
lat | +double | +Latitude of point | +
long | +double | +Longtitude of point | +
Video attached to an encrypted message.
+===8===
+decryptedMessageMediaVideo#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===17===
+decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
+
+===45===
+decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
+Name | +Type | +Description | +
---|---|---|
thumb | +bytes | +Content of thumbnail file (JPEG file, quality 55, set in a square 90x90) | +
thumb_w | +int | +Thumbnail width | +
thumb_h | +int | +Thumbnail height | +
duration | +int | +Duration of video in seconds | +
mime_type | +string | +MIME-type of the video file Parameter added in Layer 17. |
+
w | +int | +Image width | +
h | +int | +Image height | +
size | +int | +File size | +
key | +bytes | +Key to decrypt the attached video file | +
iv | +bytes | +Initialization vector | +
caption | +string | +Caption | +
Below you will find information on scheme changes. For more details on the use of layers, see Invoking API methods.
Forwards single messages.
+{scheme}
+Name | +Type | +Description | +
---|---|---|
peer | +InputPeer | +User or chat where a message will be forwarded | +
id | +int | +Forwarded message ID | +
random_id | +long | +Unique client message ID required to prevent message resending | +
Code | +Type | +Description | +
---|---|---|
400 | +CHAT_ID_INVALID | +The provided chat id is invalid | +
400 | +MESSAGE_ID_INVALID | +The provided message id is invalid | +
400 | +PEER_ID_INVALID | +The provided peer id is invalid | +
400 | +YOU_BLOCKED_USER | +You blocked this user | +
TL Language defines abstract data types in the spirit of a general theory of types (more accurately, Martin-Löf’s theories of dependent intuitionistic types) without specifying the values of these types should be represented in memory, when saved to disk, or transmitted over a network. In contrast, the article on binary serialization discusses the problem of effective serialization of values of abstract types. To this end, the concept of a concrete or serialized type has been defined as the sets of serializations of all possible values of the corresponding abstract type. In this case, the serializations take values in the set A of words in the alphabet A*, which consists of 2^32 characters -- 32-bit integers.
+In order to use a TL schema (e.g. “program”) in the TL language to describe the serialization of values of abstract types, we should explain how the concrete type [T] (subset [T] of A^) is associated with the abstract type T (defined in TL), and how the values of the abstract type T correspond to the values of the concrete type [T] (i.e. the elements of [T]*).
+Serialization is the process of constructing an element of [T] based on a value of the abstract type T. The reverse process is deserialization.
+Values of the abstract type T may be represented in a different way. Typically, some sort of trees or graphs are used in memory or, if desired, a set of nodes may be used, each of which contains a certain tag (“node type”) and several pointers to other nodes and/or values of built-in primitive types such as int
. However, for general discussions it is useful to write the values of abstract type T as a string, more specifically, an S-expression. Recall that an S-expression is either an atom (the value of a primitive type, for example, an integer or a string constant in quotation marks; or an identifier that corresponds to a built-in or defined function) or a space-delimited list of S-expressions ending in parentheses. In our case, we use S-expressions, the first element of which is a combinator identifier, while the remaining elements (the number of which depends on the combinator’s arity) are S-expressions representing elements of the chosen combinator’s fields (or parameters). Moreover, the type of the arguments’ S-expressions and the type of the S-expressions of the result (e.g. the associated expression) must match.
For example, for the schema
+pair x:int y:int = Pair;
+pnil = PairList;
+pcons hd:Pair tl:PairList = PairList;
+the following are examples of the abstract type PairList
, written as S-expressions:
(pnil)
+(pcons (pair 2 3) (pcons (pair 9 4) (pnil)))
+We usually write E : T (read "E of type T”) when we want to say that E is a value of type T. We assume there is a built-in type Type whose values are types. Thus, writing T : Type means that T is a type.
+For example, we can write:
+PairList : Type;
+(pcons (pair 2 3) (pcons (pair 9 4) (pnil))) : PairList;
+Converting an abstract value to a serialized value, generally speaking, is straightforward (and, if desired, can be defined by induction):
+It is the serialization of values n of the primitive type int
(as a single-symbol word in the alphabet A)
The serialization of a string constant (a value of the primitive type string) is a sequence of the 32-bit numbers defined in Binary serialization.
+The serialization of the S-expression (C E1 ... Er) : T
, where C
is a combinator with arity r with argument types T1, ..., Tr and result type T (e.g. C : T1->T2->...->Tr->T) is the concatenation of the combinator number C (a 32-bit number that unambiguously identifies the combinator, usually equal to the CRC-32 of the string of its TL description) and the serializations of the values E1 of type T1, E2 of type T2, ..., Er of type Tr.
If we use [T] to denote the concrete type corresponding to the abstract T, and [E] to denote an element of [T] corresponding to the value E of type T, then the last rule may be written as:
+Values of the built-in clothed types Int
and String
and serialized as if they were defined using int x:int = Int;
and string s:string = String;
, i.e. the serialization of integer constant or a string is preceded by number of the int
or string
combinator (constructor). In S-expressions, this may be written as (int 5)
or (string "Test")
.
However, what has been described above does not account for certain subtleties, such as the existence of naked types, or the difference between functions (active combinators whose application may be reduced, e.g. calculated) and constructors (passive combinators for which there are not and cannot be reduction rules). Furthermore, we have not explained how to handle polymorphic types and optional combinator parameters. We will attempt to explain this now.
+By dividing combinators into constructors and functions, we can introduce the following classes of expressions (values) of the abstract type T:
+Constant expressions: for the types int
and string
, these are all integer/string constants; for T, these are all expressions like (C E1 ... Er) : T, where the combinator C : T1->T2->...->Tr->T is a constructor, and Ei : Ti is constant expressions of types Ti. In other words, a constant expression is an S-expression consisting of only constructors and constant of primitive types.
Surface expressions are expressions that outwardly contain a functional combinator whose arguments, however, are constant expressions of the appropriate types. In other words, the functional combinator is resolved only at the outer level. (This is not entirely true; see the full explanation below).
+Functional expressions: These are expressions that may contain any combinators or constants at all levels.
+In practice, we most frequently need constant values (for storage and passing any data structures, in particular, responses to RPC queries) and surface expressions (for example, as RPC queries: then the functional combinator of the outer level is the name of the RPC function that we want to call, while its parameters are the arguments, which are constant values, for invoking the function). In some cases, arbitrary functional expressions are helpful (for example, it we want to remotely transmit the result of one RPC query to a different RPC query).
+We will use c(T) to denote a subtype of the abstract type T, whose values are constant expressions of type T. Clearly, c(T) possesses approximately the same constructors as T itself (with the types of all arguments Ti replaced by c(Ti), but it does not have functional combinators.
+Analogously, we will use f(T) to denote a subtype of T, whose values are surface expressions of type T. Clearly, the combinators of f(T) are essentially functional combinators of type T, but c() applies to the types of these combinators’ arguments: The combinator A : T1->...->Tr->T turns into A' : c(T1)->...->c(Tr)->f(T). (See the clarification of this rule below.)
+Thus, we have defined two “functionals” c : Type -> Type and f : Type -> Type, such that forall T : Type, c(T) :- T and forall T : Type, f(T) :- T (writing T :- T' means that T is contained in T', or that T is a subtype of T').
+We will assume that c and f are idempotent.
+From the perspective of abstract type theory, naked types (in contrast to built-in primitive types like int
and string
are unnecessary. However, they are extremely useful in practice.
Therefore, TL introduces the (partially defined) idempotent unary operator %
, which turns a standard functional (e.g. an expression of type ...->Type or simply Type) into a different standard functional of the same type. If T is a type, then from an abstract theoretical point of view, %T
is equivalent to c(T). In other words, the values of %T
are the constant values of T. If T is a k-arity standard expression, then T : S1 -> ... -> Sk -> Type, where each Si=Type or #, then by definition %T
is a k-arity standard expression with the same arity, which is defined by the equation (%T) a1 ... ak = % (T a1 ... ak).
When a constant value of type %T
is serialized, it is first serialized as a value of type T (assuming that T is not already a naked type itself). Then the first character of the serialization is discarded (e.g. the name of the enclosing combinator). Therefore, %T
is a only a valid type expression if there is not more than one constructor for %T
. The expression %T
, where T : S1 -> ... -> Sk -> Type, is valid, if for any choice of parameters a1 : S1, ... , ak : Sk, the type T a1 ... ak does not have more than one constructor. Using %
in other instances is incorrect.
If for every value of the parameters a1 : S1, ..., ak : Sk, there is exactly one constructor C for T a1 ... ak, then TL allows writing C a1 ... ak
instead of %T a1 ... ak
or %(T a1 .. ak)
. In other words, in certain situations the identifier C
is a synonym for %T
. This is only allowed in the context of a type (when specifying the type of a combinator’s field or result).
Moreover, it is assumed that %Int = int
and %String = string
.
!
modifierIn TL, the idempotent operator !
can modify any type, actually making surface values be allowed when its constant values are serialized. However, if T is a standard function like S1->..->Sr->Type, then !T is defined using the equation (!T) a1 ... ar = !(T a1 ... ar)
, for any a1:S1, ..., ar:Sr.
The !
operator is only allowed in a definition of the types of fields of functional combinators. It is usually used as a type prefix, for example:
set_timeout {X:Type} timeout:int f:!X = X;
+In this case, the set_timeout
“wrapper” is defined. It takes two explicit parameters: the integer timeout
and a surface expression of type X
. X : Type is itself an implicit parameter (it is not explicitly stated, rather it is inferred from the values of the other parameters and their types). A similar kind of wrapper may be helpful for modifying the action of RPC queries (which are surface expressions of various types). For example, suppose we have the function
factorial n:int = int;
+then we can wrap the RPC query (factorial 100)
as follows: (set_timeout 200 (factorial 100))
. This expression is still a surface value of type int
, which means it can be passed as an RPC query.
A consecutive pair of two computations is another example:
+pair {X Y : Type} x:X y:Y = Pair X Y; // constructor
+seq_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for sequential computation
+par_pair {X Y : Type} x:!X y:!Y = Pair X Y; // functional wrapper for parallel computation
+Now the RPC query (seq_pair (factorial 2) (factorial 3)) : Pair int int
first calculates factorial 2, then factorial 3, and returns the pair (pair 2 6)
. In this case, the sequence of operations isn’t important, because they do not have side effects. It would have been just as well to use (par_pair (factorial 2) (factorial 3))
. However, this is not always the case.
We can also define an analogy to a “comma” operation:
+comma {X Y : Type} x:!X y:!Y = Y;
+For example, this operation could first calculate x
, then forget the result, calculate y
, and return y
.
Note that the semantics of the seq_pair
, par_pair
and comma
wrappers are indeed defined where they are implemented (like the semantics of all other functional combinators), not by their TL declaration.
In principle, polymorphic wrappers like set_timeout
can also be applied, for example, to “annotate” a RPC response’s constant values. For example, the server might return a response to a query together with the time it was calculated. However, a value of type !X must be constant, because that is what is expected as the enclosing expression’s value. In other words, set_timeout 239 E
is a constant/surface value of type X if and only if E is such itself.
$
modifierThe idempotent modifier $
permits the use of arbitrary functional values of an appropriate type in contexts where only constants or surface values are usually allowed. It recursively transforms all combinators for all of the types involved, canceling the action of %
and affixing $
to the parameter types and result of all combinators ($
is also added to the front of the transformed combinators). Moreover, built-in types are also transformed (in the final stage): $int = Int
and $string = String
.
This may be useful to create an RPC query that performs a “deep computation” of the expression passed to it:
+compute {X:Type} expr:$X = X;
+For example, now we can transmit the following as an RPC query:
+(compute ($factorial ($factorial (int 3)))) : int
+(Note that the three has become clothed; the combinator $factorial has type $int -> $int).
+This is very powerful tool. It does not have to be implemented in very simple versions of TL. $
is not encountered in currently used TL schemas.
In fact, at least in terms of its application to serialization, the TL language by default implies the c() modifier around all combinators’ parameter types and results, while !
and $
cancel it (more accurately, !
only cancels, and in some sense $
reverses the meaning). This is why there is no explicit c()
modifier in TL and why it is assumed that all functions only accept constant values and return constant results, unless otherwise specified.
You may think that some functional combinators may have a type such as partial_factorial n:int = $int;
and that the RPC query (partial_factorial 3)
might then unexpectedly return ($product (int 3) ($product (int 2) ($product (int 1) (int 1)))) : $int
...
It is probably more correct to think about the !
modifier as follows. All types initially include only constant values (and only constructors). The !
modifier makes a new type (it’s twin) out of each type. This new type has no inherent constructors. Functional combinators differ from constructors in that !
is implicitly added in front of their result’s type. After this, the (local or remote) process of calculating the expression can be represented using the polymorphic function eval : !X -> X
.
Main article: TL Language.
+In certain cases, types may depend not only on other types (polymorphism), but also on the parameters of another type (dependent types). The TL language provides very limited support for this functionality: dependence is only allowed on a natural parameter whose type is designated using #
(alias nat
, but this is private -- TL doesn’t currently support this synonym). Values of type # are serialized as 32-bit signed numbers from 0 to 2^31-1.
Suppose we want to use induction to define the types “one integer”, “two integers”, and “three integers”. We could try to define them as follows:
+empty = Empty;
+single x:int = Single;
+pair x:int y:int = Pair;
+triple x:int y:int z:int = Triple;
+quadruple x:int y:int z:int t:int = Quadruple;
+...
+or as:
+empty = Empty;
+single x:int empty = Single;
+pair x:int y:single = Pair;
+triple x:int yz:pair = Triple;
+quadruple x:int yzt:triple = Quadruple;
+or as:
+tnil = Tuple0;
+tcons0 hd:int tl:Tuple0 = Tuple1;
+tcons1 hd:int tl:Tuple1 = Tuple2;
+tcons2 hd:int tl:Tuple2 = Tuple3;
+...
+tcons_n hd:int tl:Tuple_n = Tuple_(n+1)
+The first two variations lead to the same serialization. For example, (2 3 9):%triple
and (2 (3 9)):%triple
serialize as three 32-bit numbers: 2 3 9
. The last variation better emphasizes the inductive version of the definition, but it uses boxed types. This is good from a theoretical perspective, but it leads to “superfluous” constructor names in serialization.
Therefore, we will write %Type-Ident
to indicate the bare type that corresponds to the boxed type Type-Ident
with a single constructor. If this constructor is named constructor
, then according to the definition %Type-Ident
= %constructor
. Now we can write our definition like this:
tnil = Tuple0;
+tcons_n hd:int tl:%Tuple_n = Tuple_(n+1)
+If we now abstract n out of the name of the type name and make it like a parameter for a polymorphic (dependent, to be more exact) type, then something like the following can be written in a suitable functional language:
+NewType Tuple (n : #) :=
+| tnil = Tuple 0
+| tcons n:# hd:int tl:%(Tuple n) = Tuple (S n)
+EndType;
+In the TL language, it looks like this:
+tnil = Tuple 0;
+tcons {n:#} hd:int tl:%(Tuple n) = Tuple (S n);
+The function S : # -> #
and the constant O : #
(it is 0
) are the function for the next natural number (S n = n + 1
) and the constant null. Therefore, the type #
(alias nat
) behaves as if it were defined in TL using the constructors
O = nat;
+S nat = nat;
+or, using syntax more typical of other functional languages,
+NewType nat :=
+| O
+| S nat
+EndType;
+Types of all defined combinators:
+O : #
+S : # -> #
+Tuple : # -> Type
+tnil : Tuple 0
+tcons : forall n : #, int -> Tuple n -> Tuple (S n)
+or
+Tuple : forall n : #, Type;
+tcons : forall n : #, forall hd : int, forall tl : Tuple n, Tuple (S n)
+Note that in this case the constructor tnil
does not depend on the parameter n, while tcons
does.
In an analogous manner, it is possible to define a complete binary tree of height h with strings in the leaf nodes:
+tleaf value:string = BinTree 0;
+tnode {h:#} left:(BinTree h) right:(BinTree h) = BinTree (S h);
+Or a random tree whose leaf nodes are all a distance of h from the root and whose nodes are all labeled with integers:
+hleaf value:int = Tree 0;
+hnode {n:#} left:(Tree n) next:(Tree (S n)) = Tree (S n)
+hnil {n:#} = Tree (S n)
+Another version:
+hleaf' value:int = Tree' 0;
+hnode' {n:#} children:(list (Tree' n)) = Tree' (S n)
+Let us try to define a type Tuple X n
whose values are n-tuples of type X
values. In this way, Tuple
will be simultaneously polymorphic and dependent:
Tuple : Type -> # -> Type;
+In the familiar syntax of functional languages:
+NewType Tuple {X : Type} {n : #} :=
+| vnil : Tuple X 0
+| vcons {n:#} hd:X tl:%(Tuple X n) : Tuple X (S n)
+EndType
+or, in TL syntax,
+vnil {X:Type} = Tuple X 0;
+vcons {X:Type} {n:#} tl:(%Tuple X n) = Tuple X S n
+In the end we obtain terms for the following types:
+vnil : forall X : Type, Tuple X 0
+vcons : forall X : Type, forall n : #, X -> Tuple X n -> Tuple X (S n)
+or
+vnil : forall X : Type, Tuple X 0
+vcons : forall X : Type, forall n : #, forall hd : X, forall tl : Tuple X n, Tuple X (S n)
+The Tuple
we just defined differs from the built-in Vector
type. Specifically, the Vector
type formally depends on a single argument (a type), but our Tuple
depends on two (a type and a number):
Tuple : Type -> # -> Type;
+Vector : Type -> Type;
+The built-in Vector
could be defined in terms of our Tuple
using “summing across all n : #":
vector {X:Type} n:# v:(%Tuple X n) = Vector X;
+Nevertheless, our Tuple
has its advantages. For example, we can define data types such as:
matrix_10x10 a:(%Tuple (%Tuple double 10) 10) = Matrix_10x10;
+In any event, remember that during calculation of the matrix_10x10
combinator’s number, all parentheses must be removed and the CRC32 of the string matrix_10x10 a:%Tuple %Tuple double 10 10 = Matrix_10x10
must be computed.
Moreover, we can define arbitrarily-sized matrices:
+matrix {X:Type} m:# n:# a:(%Tuple (%Tuple X m) n) = Matrix X;
+In this case using vector would result in storing the length of a row (m) in each row, e.g. n times.
+Note that the serializations of values of type %Tuple X n
and vector X
(also known as %vector X
and %Vector X
) nearly match when n > 0: in both cases we obtain a single 32-bit number (equal to n-1 or n depending on the version) followed by the serializations of n objects of type X. (This is slightly untrue: values of type %Tuple X n
can only be serialized if n is a constant or value known from one of the preceding fields of the enclosing entry; but then this n won’t be serialized explicitly anywhere).
In view of the importance of the construction presented above, it is built into the TL language in the following manner. A substructure in the form of [ array-field-name ":" ] [ nat-ident "" ] "[" field-descr ... "]” may be used in the declaration of any combinator, where nat-ident* is the name of any previously encountered field of type # (if it is not explicitly indicated, the most recent is used). In abstract, this substructure is equivalent to:
+aux_type *field-descr* ... = AuxType;
+*current_constructor* ... [ *array-field-name* ":" ] (%Tuple aux_type *nat-ident*)
+For example, 10x10 matrices, vectors, and arbitrary matrices may be defined in the following way:
+matrix {X:Type} m:# n:# a:n*[ m*[ X ] ] = Matrix X;
+matrix_10x10 a:10*[ 10*[ double ]] = Matrix_10x10;
+vector {X:Type} # [ X ] = Vector X;
+We have already encountered the last version as a “definition” of the “built-in type” Vector
.
Of course, several fields, as complex as desired, may be within the repeating part. Furthermore, besides using n as a repeat counter, one may use expressions of the form (n+const) and (const+n), where const is a small nonnegative constant, which are shorthand for S (S ( ... (S n) ... )):
+repeat_np1 n:# a:(S n)*[ key:string value:string ] = Dictionary;
+To calculate the CRC32 these expressions are converted to expressions of the form (const+X)
without internal spaces. Additionally, the *
in this case is not set off by spaces on the left and right.
Serialization of dependent types and polymorphic types is not a fundamental challenge: we have combinators with non-zero arity with Type values. For example, the type Tuple double 10 : Type
serializes to 'Tuple' '%double' 10
. Note that at present in practice there is virtually no need to serialize types, whether dependent or not.
Optional combinator parameters in TL must possess the following properties:
+Optional parameters must be precisely ythe combinator’s first several arguments;
+The value of any optional parameter must be entirely determined by the combinator’s result type.
+For example, in cons {X:Type} hd:X tl:(list X) = list X
the parameter X
may be made optional, because it is located at the very beginning of the argument list and is unambiguously determined by the list X
result type. Similarly, in tcons {X:Type} {n:#} hd:X tl:(%Tuple X n) = Tuple X (S n)
the values of X and n are completely determined based on the Tuple X (S n)
result type, therefore they made be made optional parameters.
It usually makes sense to move all of a constructor’s arguments satisfying the second condition to the beginning of the list, arrange them in the order they appear in the result type’s parameters, and make them optional. Given such an approach, the full version of a constructor is rarely needed -- only when we want to transmit the value of the polymorphic or dependent type as a value of type Object. In all other cases, the type of the expected value from the context is already known, which means that all optional parameters can be recovered during decomposition.
+