Skip to content

Commit 707136f

Browse files
committed
Create documentation for destructuring
1 parent 4d77997 commit 707136f

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,139 @@
11
# Destructuring
22

33
Destructuring is a game changer for developing robust macros without drowning yourself in verbose input validation code.
4+
5+
## Overview
6+
7+
Destructuring is a common concept in programming available in many languages from Javascript to Rust. It is the act of
8+
taking data and pulling it apart into its component parts; often with some pattern matching involved.
9+
10+
## A first look
11+
12+
The quickest way to demonstrate why you'll love destructuring is to compare a snippet that uses MacroKit and destructuring,
13+
to one that doesn't.
14+
15+
Notice how when we're using MacroToolkit, we simply describe what we expect the `returnType` to look like, and MacroToolkit
16+
performs all of the pattern matching and validation for us!
17+
18+
### Without MacroToolkit
19+
20+
```swift
21+
// We're expecting the return type to look like `Result<A, B>`
22+
guard
23+
let simpleReturnType = returnType.as(SimpleTypeIdentifierSyntax.self),
24+
simpleReturnType.name.description == "Result",
25+
let genericArguments = (simpleReturnType.genericArgumentClause?.arguments).map(Array.init),
26+
genericArguments.count == 2
27+
else {
28+
throw MacroError("Invalid return type")
29+
}
30+
let successType = genericArguments[0]
31+
let failureType = genericArguments[1]
32+
```
33+
34+
### With MacroToolkit
35+
36+
```swift
37+
// We're expecting the return type to look like `Result<A, B>`
38+
guard case let .simple("Result", (successType, failureType))? = destructure(returnType) else {
39+
throw MacroError("Invalid return type")
40+
}
41+
```
42+
43+
## General destructuring
44+
45+
MacroToolkit provides a set of functions that can be used to destructure any array-like data (e.g. argument lists,
46+
attribute lists, etc.). Lets use destructuring to parse an argument list.
47+
48+
```swift
49+
// We're expecting exactly two arguments; a name and an age
50+
guard
51+
let (nameExpr, ageExpr) = attribute.arguments,
52+
let name = nameExpr.asStringLiteral?.value,
53+
let age = ageExpr.asIntegerLiteral?.value
54+
else {
55+
throw MacroError("Usage: @MyMacro(\"stackotter\", 105)")
56+
}
57+
```
58+
59+
- 0 elements: ``destructure(_:)-12e8l``
60+
- 1 element: ``destructureSingle(_:)-1dg2k``
61+
- 2 elements: ``destructure(_:)-2c4y9``
62+
- 3 elements: ``destructure(_:)-65tob``
63+
- 4 elements: ``destructure(_:)-2vkcn``
64+
- 5 elements: ``destructure(_:)-2ooj8``
65+
- 6 elements: ``destructure(_:)-ztug``
66+
67+
At the moment a separate implementation is required for each number of arguments because variadic generics aren't
68+
yet ready for this use-case. In the future the 6 element limit will be lifted (and if you need it lifted now you
69+
can just ask and I can bump up the maximum with a bit of copy and pasting).
70+
71+
## Type destructuring
72+
73+
As you saw in [A first look](#A-first-look), type destructuring is a highly expressive way to parse and validate
74+
types.
75+
76+
### Destructuring arbitrary types
77+
78+
So far only the ``Type/simple(_:)`` and ``Type/function(_:)`` variants of ``Type`` can be destructured. But with minimal
79+
effort this can be expanded to variants such as ``Type/tuple(_:)``.
80+
81+
```swift
82+
// We're expecting the function to have a signature of the form `(parameterType1, parameterType2) -> returnType`.
83+
guard
84+
case .function((parameterType1, parameterType2), returnType) = destructure(functionType)
85+
else {
86+
throw MacroError("Invalid return type")
87+
}
88+
```
89+
90+
- 0 unknowns: ``destructure(_:)-6saio``
91+
- 1 unknown: ``destructureSingle(_:)-9tjyg``
92+
- 2 unknowns: ``destructure(_:)-1vwqf``
93+
- 3 unknowns: ``destructure(_:)-365tz``
94+
- 4 unknowns: ``destructure(_:)-867ko``
95+
- 5 unknowns: ``destructure(_:)-5joxh``
96+
- 6 unknowns: ``destructure(_:)-nbe1``
97+
98+
### Destructuring simple types (aka nominal types)
99+
100+
Simple types can be destructured into a name and a collection of generic type parameters.
101+
102+
```swift
103+
// We're expecting the return type to look like `Result<A, B>`.
104+
guard case let ("Result", (successType, failureType))? = destructure(simpleReturnType) else {
105+
throw MacroError("Invalid return type")
106+
}
107+
```
108+
109+
- 0 generic type parameters: ``destructure(_:)-3e860``
110+
- 1 generic type parameter: ``destructureSingle(_:)-chrb``
111+
- 2 generic type parameters: ``destructure(_:)-3xse5``
112+
- 3 generic type parameters: ``destructure(_:)-8aio2``
113+
- 4 generic type parameters: ``destructure(_:)-560mv``
114+
- 5 generic type parameters: ``destructure(_:)-1h0o8``
115+
- 6 generic type parameters: ``destructure(_:)-45hpd``
116+
117+
### Destructuring function types
118+
119+
Function types can be destructured into a collection of parameter types and a return type.
120+
121+
```swift
122+
// We're expecting the function to have a signature of the form `(first, _, third) -> Int`.
123+
guard
124+
// Destructure a function type
125+
case let ((first, _, third), returnType)? = destructure(functionType),
126+
// Destructure an arbitrary type
127+
case .simple("Int", ()) = destructure(returnType)
128+
else {
129+
throw MacroError("Invalid return type")
130+
}
131+
```
132+
133+
- 0 parameter types: ``destructure(_:)-180j9``
134+
- 1 parameter type: ``destructureSingle(_:)-5r8sv``
135+
- 2 parameter types: ``destructure(_:)-2idom``
136+
- 3 parameter types: ``destructure(_:)-8g4v5``
137+
- 4 parameter types: ``destructure(_:)-7o2hx``
138+
- 5 parameter types: ``destructure(_:)-8m59b``
139+
- 6 parameter types: ``destructure(_:)-9wc1b``

0 commit comments

Comments
 (0)