-
Notifications
You must be signed in to change notification settings - Fork 10
/
interop design.txt
129 lines (88 loc) · 3.53 KB
/
interop design.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# .NET Interop design
In current design, interop is done mainly via runtime reflection,
without any specialization or optimization happening at compile time.
## `dotdot` operator
Interop is accomplished mainly via the special `..` operator which
uses reflection to dereference the methods/properties/fields by name,
and then potentially call them or retrieve their values.
All .net interop functions are also prefixed with `.`, e.g. `.new`
```
;; dotdot retrieves a type
(.. 'System) ;; => object representing System namespace
(.. 'System.DateTime) ;; => object representing type DateTime
;; dotdot calls an instance method
(.. "foobar" 'ToUpper) ;; => "FOOBAR"
;; dotdot as field or property getter
(.. "foobar" 'Length) ;; => 6
;; dotdot calls a static method
(.. 'System.Int32.Parse "123") ;; => 123
;; dotdot as a static field or property getter
(.. 'System.DateTime.Now) ;; => [new DateTime object]
;; special .net interop functions
;; create an instance of a type
(.new 'System.DateTime 2021 1 1) ;; => [new DateTime object]
(.new (.. 'System.DateTime) 2021 1 1) ;; => [new DateTime object]
;; set field or property
(let ((array (.new 'System.Collections.ArrayList 10)))
(.! array 'Capacity 100)
array) ;; => array with capacity of 100
;; indexed getter and setter field or property
;; uses the special Item property as defined by .Net
(let ((array (.new 'System.Collections.ArrayList 10)))
(.! array 'Item 0 42)
(.. array 'Item 0)) ;; => 42
```
### dotdot dereference chaining
#### Special treatment of symbols
Because .Net APIs never consume Lisp symbols, dotdot will treat any symbol
inside dotdot expression as a reference to a .Net name of a
method / property / field, and try to dereference it via reflection.
If the symbol itself contains dots, dotdot flattens it out like this:
```
(.. "foobar" 'ToUpper.ToLower) => (.. "foobar" 'ToUpper 'ToLower)
(.. 'System.DateTime.Now) => (.. 'System 'DateTime 'Now)
```
Any arguments that are not symbols will be treated as function call arguments
so that function calls can be inlined like so:
```
(.. "foobar" 'ToUpper 'Substring 0 3) ;; => FOO
(.. "foobar" 'ToUpper 'Substring 0 3 'IndexOf "F") ;; => 0
```
#### How this works
Internaly dotdot scans all arguments it receives, and first splits up any composite
symbols with dots inside them into separate ones. Second, it consumes those symbols
in order, also pulling in non-symbol elements as function call arguments. For example:
```
(.. "foobar" 'ToUpper 'Substring 0 3 'IndexOf "F")
-->
C# pseudocode:
var x1 = "foobar";
var x2 = x1.ToUpper();
var x3 = x2.Substring(0, 3);
var x4 = x3.IndexOf("F");
return x4;
```
```
(.. 'System.DateTime.Now.DayOfWeek.ToString)
-->
var x1 = (object representing namespace System)
var x2 = (get type DateTime from x1)
var x3 = (get value of property Now from x2)
var x4 = x3.DayOfWeek;
var x5 = x4.ToString();
return x5;
```
Dotdot can be embedded recursively, e.g. to process multiple arguments that require .Net,
in which case standard lisp evaluation will process the internal expressions first
and pass them in to the outer one:
```
(.. 123 'ToString (.. 'System.Globalization.CultureInfo.InvariantCulture))
-->
var y1 = (namespace System)
var y2 = (get namespace Globalization from y1)
var y3 = (get type CultureInfo from y2)
var y4 = (get value of property InvariantCulture from y3)
(.. 123 'ToString y4)
var x1 = 123;
var x2 = x1.ToString(y4);
```