tangled
alpha
login
or
join now
oeiuwq.com
/
godnet
1
fork
atom
A Golang runtime and compilation backend for Delta Interaction Nets.
1
fork
atom
overview
issues
pulls
pipelines
native
oeiuwq.com
3 months ago
17feb4d0
b6e0a97c
+396
-5
3 changed files
expand all
collapse all
unified
split
pkg
deltanet
deltanet.go
native_test.go
trace.go
+189
-5
pkg/deltanet/deltanet.go
···
16
16
NodeTypeEraser
17
17
NodeTypeReplicator
18
18
NodeTypeVar // Wire/Interface
19
19
+
NodeTypeData
20
20
+
NodeTypeNative
19
21
)
20
22
21
23
func (t NodeType) String() string {
···
28
30
return "Replicator"
29
31
case NodeTypeVar:
30
32
return "Var"
33
33
+
case NodeTypeData:
34
34
+
return "Data"
35
35
+
case NodeTypeNative:
36
36
+
return "Native"
31
37
default:
32
38
return "Unknown"
33
39
}
···
44
50
SetDead() bool
45
51
IsDead() bool
46
52
Revive()
53
53
+
// For Data nodes
54
54
+
GetValue() interface{}
55
55
+
// For Native nodes
56
56
+
GetName() string
47
57
}
48
58
49
59
// Port represents a connection point on a node.
···
69
79
dead int32
70
80
}
71
81
72
72
-
func (n *BaseNode) Type() NodeType { return n.typ }
73
73
-
func (n *BaseNode) ID() uint64 { return n.id }
74
74
-
func (n *BaseNode) Ports() []*Port { return n.ports }
75
75
-
func (n *BaseNode) Level() int { return 0 }
76
76
-
func (n *BaseNode) Deltas() []int { return nil }
82
82
+
func (n *BaseNode) Type() NodeType { return n.typ }
83
83
+
func (n *BaseNode) ID() uint64 { return n.id }
84
84
+
func (n *BaseNode) Ports() []*Port { return n.ports }
85
85
+
func (n *BaseNode) Level() int { return 0 }
86
86
+
func (n *BaseNode) Deltas() []int { return nil }
87
87
+
func (n *BaseNode) GetValue() interface{} { return nil }
88
88
+
func (n *BaseNode) GetName() string { return "" }
77
89
78
90
func (n *BaseNode) SetDead() bool {
79
91
return atomic.CompareAndSwapInt32(&n.dead, 0, 1)
···
97
109
func (n *ReplicatorNode) Level() int { return n.level }
98
110
func (n *ReplicatorNode) Deltas() []int { return n.deltas }
99
111
112
112
+
// DataNode holds opaque data values.
113
113
+
type DataNode struct {
114
114
+
BaseNode
115
115
+
value interface{}
116
116
+
}
117
117
+
118
118
+
func (n *DataNode) GetValue() interface{} { return n.value }
119
119
+
120
120
+
// NativeNode represents a registered native function.
121
121
+
type NativeNode struct {
122
122
+
BaseNode
123
123
+
name string
124
124
+
}
125
125
+
126
126
+
func (n *NativeNode) GetName() string { return n.name }
127
127
+
128
128
+
// NativeFunc is a pure function that takes data and returns data or error.
129
129
+
type NativeFunc func(interface{}) (interface{}, error)
130
130
+
100
131
// Network manages the graph of nodes and interactions.
101
132
type Network struct {
102
133
nextID uint64
···
122
153
nodes map[uint64]Node
123
154
nodesMu sync.Mutex
124
155
156
156
+
// Native function registry
157
157
+
natives map[string]NativeFunc
158
158
+
nativesMu sync.RWMutex
159
159
+
125
160
traceBuf []TraceEvent
126
161
traceCap uint64
127
162
traceIdx uint64
···
148
183
scheduler: NewScheduler(),
149
184
workers: runtime.NumCPU(),
150
185
nodes: make(map[uint64]Node),
186
186
+
natives: make(map[string]NativeFunc),
151
187
phase: 1,
152
188
}
153
189
return n
···
277
313
return node
278
314
}
279
315
316
316
+
func (n *Network) NewData(value interface{}) Node {
317
317
+
id := n.nextNodeID()
318
318
+
node := &DataNode{
319
319
+
BaseNode: BaseNode{
320
320
+
id: id,
321
321
+
typ: NodeTypeData,
322
322
+
ports: make([]*Port, 1), // 0: Connection
323
323
+
},
324
324
+
value: value,
325
325
+
}
326
326
+
node.ports[0] = &Port{Node: node, Index: 0}
327
327
+
n.nodesMu.Lock()
328
328
+
if n.nodes == nil {
329
329
+
n.nodes = make(map[uint64]Node)
330
330
+
}
331
331
+
n.nodes[node.id] = node
332
332
+
n.nodesMu.Unlock()
333
333
+
return node
334
334
+
}
335
335
+
336
336
+
func (n *Network) NewNative(name string) Node {
337
337
+
id := n.nextNodeID()
338
338
+
node := &NativeNode{
339
339
+
BaseNode: BaseNode{
340
340
+
id: id,
341
341
+
typ: NodeTypeNative,
342
342
+
ports: make([]*Port, 1), // 0: Connection (for application)
343
343
+
},
344
344
+
name: name,
345
345
+
}
346
346
+
node.ports[0] = &Port{Node: node, Index: 0}
347
347
+
n.nodesMu.Lock()
348
348
+
if n.nodes == nil {
349
349
+
n.nodes = make(map[uint64]Node)
350
350
+
}
351
351
+
n.nodes[node.id] = node
352
352
+
n.nodesMu.Unlock()
353
353
+
return node
354
354
+
}
355
355
+
356
356
+
func (n *Network) RegisterNative(name string, fn NativeFunc) {
357
357
+
n.nativesMu.Lock()
358
358
+
defer n.nativesMu.Unlock()
359
359
+
n.natives[name] = fn
360
360
+
}
361
361
+
362
362
+
func (n *Network) GetNative(name string) (NativeFunc, bool) {
363
363
+
n.nativesMu.RLock()
364
364
+
defer n.nativesMu.RUnlock()
365
365
+
fn, ok := n.natives[name]
366
366
+
return fn, ok
367
367
+
}
368
368
+
280
369
// Canonicalize prunes all nodes not reachable from the given root (node, port).
281
370
// For every unreachable node, all its connected wires are replaced by erasers.
282
371
func (n *Network) Canonicalize(root Node, rootPort int) {
···
556
645
n.commuteFanReplicator(b, a, depth)
557
646
}
558
647
}
648
648
+
case (a.Type() == NodeTypeFan && b.Type() == NodeTypeNative) || (a.Type() == NodeTypeNative && b.Type() == NodeTypeFan):
649
649
+
// Fan-Native interaction: Application of native function
650
650
+
rule = RuleFanNative
651
651
+
if a.Type() == NodeTypeFan {
652
652
+
n.applyNative(a, b, depth)
653
653
+
} else {
654
654
+
n.applyNative(b, a, depth)
655
655
+
}
656
656
+
case (a.Type() == NodeTypeFan && b.Type() == NodeTypeData) || (a.Type() == NodeTypeData && b.Type() == NodeTypeFan):
657
657
+
// Fan-Data: should not happen in normal reduction (Data comes after Native)
658
658
+
// But if it does, treat Data as inert (like Var)
659
659
+
rule = RuleUnknown
660
660
+
fmt.Printf("Warning: Fan-Data interaction (Fan %d <-> Data %d)\n", a.ID(), b.ID())
661
661
+
a.Revive()
662
662
+
b.Revive()
559
663
default:
560
664
fmt.Printf("Unknown interaction: %v <-> %v\n", a.Type(), b.Type())
561
665
}
···
838
942
839
943
func (n *Network) createReplicatorCopyWithLevel(original Node, newLevel int) Node {
840
944
return n.NewReplicator(newLevel, original.Deltas())
945
945
+
}
946
946
+
947
947
+
// applyNative executes a native function application: Fan-Native interaction
948
948
+
// Fan represents application: Fan.0 = function, Fan.2 = argument, Fan.1 = result
949
949
+
// Native is the function node
950
950
+
func (n *Network) applyNative(fan, native Node, depth uint64) {
951
951
+
// Get the native function
952
952
+
nativeName := native.GetName()
953
953
+
fn, ok := n.GetNative(nativeName)
954
954
+
if !ok {
955
955
+
fmt.Printf("Error: Native function %q not registered\n", nativeName)
956
956
+
// Create error data node
957
957
+
errData := n.NewData(fmt.Errorf("native function %q not found", nativeName))
958
958
+
// Connect result to error
959
959
+
if fan.Ports()[1].Wire.Load() != nil {
960
960
+
n.splice(errData.Ports()[0], fan.Ports()[1])
961
961
+
}
962
962
+
n.removeNode(fan)
963
963
+
n.removeNode(native)
964
964
+
return
965
965
+
}
966
966
+
967
967
+
// Get the argument from Fan.2
968
968
+
argNode, _ := n.GetLink(fan, 2)
969
969
+
970
970
+
// Check if argument is Data
971
971
+
if argNode == nil {
972
972
+
fmt.Printf("Error: Native %q applied to nil argument\n", nativeName)
973
973
+
errData := n.NewData(fmt.Errorf("nil argument"))
974
974
+
if fan.Ports()[1].Wire.Load() != nil {
975
975
+
n.splice(errData.Ports()[0], fan.Ports()[1])
976
976
+
}
977
977
+
n.removeNode(fan)
978
978
+
n.removeNode(native)
979
979
+
return
980
980
+
}
981
981
+
982
982
+
if argNode.Type() == NodeTypeData {
983
983
+
// Execute native function with data
984
984
+
value := argNode.GetValue()
985
985
+
result, err := fn(value)
986
986
+
987
987
+
var resultNode Node
988
988
+
if err != nil {
989
989
+
// Return error as data
990
990
+
resultNode = n.NewData(err)
991
991
+
} else {
992
992
+
// Check if result is a function (for currying)
993
993
+
if resultFn, isFn := result.(func(interface{}) (interface{}, error)); isFn {
994
994
+
// Result is a partially applied function - create new Native node
995
995
+
// Register it with a unique name
996
996
+
partialName := fmt.Sprintf("%s$partial$%d", nativeName, n.nextNodeID())
997
997
+
n.RegisterNative(partialName, resultFn)
998
998
+
resultNode = n.NewNative(partialName)
999
999
+
} else {
1000
1000
+
// Result is data
1001
1001
+
resultNode = n.NewData(result)
1002
1002
+
}
1003
1003
+
}
1004
1004
+
1005
1005
+
// Connect result to Fan.1
1006
1006
+
if fan.Ports()[1].Wire.Load() != nil {
1007
1007
+
n.splice(resultNode.Ports()[0], fan.Ports()[1])
1008
1008
+
}
1009
1009
+
1010
1010
+
// Remove processed nodes
1011
1011
+
n.removeNode(fan)
1012
1012
+
n.removeNode(native)
1013
1013
+
n.removeNode(argNode)
1014
1014
+
} else {
1015
1015
+
// Argument is not Data yet - the argument needs to reduce first
1016
1016
+
// This shouldn't happen in normal execution since we reduce arguments before functions
1017
1017
+
// But if it does, we treat this as an error case
1018
1018
+
fmt.Printf("Warning: Native %q applied to non-Data argument (type %v)\n", nativeName, argNode.Type())
1019
1019
+
1020
1020
+
// For now, just leave the structure as-is
1021
1021
+
// The reduction will continue with other active wires
1022
1022
+
fan.Revive()
1023
1023
+
native.Revive()
1024
1024
+
}
841
1025
}
842
1026
843
1027
func (n *Network) SetPhase(p int) {
+206
pkg/deltanet/native_test.go
···
1
1
+
package deltanet
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
"testing"
6
6
+
)
7
7
+
8
8
+
// TestStringConcat tests concatenating two strings using native functions
9
9
+
func TestStringConcat(t *testing.T) {
10
10
+
net := NewNetwork()
11
11
+
12
12
+
// Register concat native function
13
13
+
net.RegisterNative("concat", func(a interface{}) (interface{}, error) {
14
14
+
// This returns a function that captures 'a' (first string)
15
15
+
s1, ok := a.(string)
16
16
+
if !ok {
17
17
+
return nil, fmt.Errorf("concat: first arg must be string, got %T", a)
18
18
+
}
19
19
+
20
20
+
// Return a native function that takes the second argument
21
21
+
return func(b interface{}) (interface{}, error) {
22
22
+
s2, ok := b.(string)
23
23
+
if !ok {
24
24
+
return nil, fmt.Errorf("concat: second arg must be string, got %T", b)
25
25
+
}
26
26
+
return s1 + s2, nil
27
27
+
}, nil
28
28
+
})
29
29
+
30
30
+
// Build net structure for: concat "hello" "world"
31
31
+
// This is: (concat "hello") "world"
32
32
+
// Structure: Fan(App) where:
33
33
+
// Fan.0 -> connects to another Fan(App) for (concat "hello")
34
34
+
// Fan.2 -> Data("world")
35
35
+
// Fan.1 -> result
36
36
+
37
37
+
// Inner application: concat "hello"
38
38
+
innerFan := net.NewFan()
39
39
+
concatNode := net.NewNative("concat")
40
40
+
helloData := net.NewData("hello")
41
41
+
42
42
+
net.Link(innerFan, 0, concatNode, 0) // Function
43
43
+
net.Link(innerFan, 2, helloData, 0) // Argument
44
44
+
// innerFan.1 is the result of (concat "hello")
45
45
+
46
46
+
// Outer application: (concat "hello") "world"
47
47
+
outerFan := net.NewFan()
48
48
+
worldData := net.NewData("world")
49
49
+
50
50
+
net.Link(outerFan, 0, innerFan, 1) // Function (result of inner app)
51
51
+
net.Link(outerFan, 2, worldData, 0) // Argument
52
52
+
// outerFan.1 is the final result
53
53
+
54
54
+
// Connect result to output var
55
55
+
output := net.NewVar()
56
56
+
net.Link(outerFan, 1, output, 0)
57
57
+
58
58
+
// Reduce
59
59
+
net.ReduceAll()
60
60
+
61
61
+
// Check result
62
62
+
resultNode, resultPort := net.GetLink(output, 0)
63
63
+
if resultNode == nil {
64
64
+
t.Fatal("Result node is nil")
65
65
+
}
66
66
+
if resultNode.Type() != NodeTypeData {
67
67
+
t.Errorf("Expected result to be Data node, got %v", resultNode.Type())
68
68
+
}
69
69
+
70
70
+
result := resultNode.GetValue()
71
71
+
expected := "helloworld"
72
72
+
if result != expected {
73
73
+
t.Errorf("Expected %q, got %v", expected, result)
74
74
+
}
75
75
+
76
76
+
t.Logf("Result: %v (port %d)", result, resultPort)
77
77
+
}
78
78
+
79
79
+
// TestStringLength tests computing the length of a string
80
80
+
func TestStringLength(t *testing.T) {
81
81
+
net := NewNetwork()
82
82
+
83
83
+
// Register length native function
84
84
+
net.RegisterNative("length", func(v interface{}) (interface{}, error) {
85
85
+
s, ok := v.(string)
86
86
+
if !ok {
87
87
+
return nil, fmt.Errorf("length: arg must be string, got %T", v)
88
88
+
}
89
89
+
return len(s), nil
90
90
+
})
91
91
+
92
92
+
// Build net structure for: length "hello"
93
93
+
// Structure: Fan(App) where:
94
94
+
// Fan.0 -> Native("length")
95
95
+
// Fan.2 -> Data("hello")
96
96
+
// Fan.1 -> result
97
97
+
98
98
+
fan := net.NewFan()
99
99
+
lengthNode := net.NewNative("length")
100
100
+
helloData := net.NewData("hello")
101
101
+
102
102
+
net.Link(fan, 0, lengthNode, 0) // Function
103
103
+
net.Link(fan, 2, helloData, 0) // Argument
104
104
+
105
105
+
// Connect result to output var
106
106
+
output := net.NewVar()
107
107
+
net.Link(fan, 1, output, 0)
108
108
+
109
109
+
// Reduce
110
110
+
net.ReduceAll()
111
111
+
112
112
+
// Check result
113
113
+
resultNode, resultPort := net.GetLink(output, 0)
114
114
+
if resultNode == nil {
115
115
+
t.Fatal("Result node is nil")
116
116
+
}
117
117
+
if resultNode.Type() != NodeTypeData {
118
118
+
t.Errorf("Expected result to be Data node, got %v", resultNode.Type())
119
119
+
}
120
120
+
121
121
+
result := resultNode.GetValue()
122
122
+
expected := 5
123
123
+
if result != expected {
124
124
+
t.Errorf("Expected %d, got %v", expected, result)
125
125
+
}
126
126
+
127
127
+
t.Logf("Result: %v (port %d)", result, resultPort)
128
128
+
}
129
129
+
130
130
+
// TestConcatLength tests composing concat and length
131
131
+
// length (concat "hello" "world") should return 10
132
132
+
func TestConcatLength(t *testing.T) {
133
133
+
net := NewNetwork()
134
134
+
135
135
+
// Register both natives
136
136
+
net.RegisterNative("concat", func(a interface{}) (interface{}, error) {
137
137
+
s1, ok := a.(string)
138
138
+
if !ok {
139
139
+
return nil, fmt.Errorf("concat: first arg must be string, got %T", a)
140
140
+
}
141
141
+
return func(b interface{}) (interface{}, error) {
142
142
+
s2, ok := b.(string)
143
143
+
if !ok {
144
144
+
return nil, fmt.Errorf("concat: second arg must be string, got %T", b)
145
145
+
}
146
146
+
return s1 + s2, nil
147
147
+
}, nil
148
148
+
})
149
149
+
150
150
+
net.RegisterNative("length", func(v interface{}) (interface{}, error) {
151
151
+
s, ok := v.(string)
152
152
+
if !ok {
153
153
+
return nil, fmt.Errorf("length: arg must be string, got %T", v)
154
154
+
}
155
155
+
return len(s), nil
156
156
+
})
157
157
+
158
158
+
// Build: length (concat "hello" "world")
159
159
+
// Structure:
160
160
+
// 1. Inner: concat "hello" -> partial
161
161
+
// 2. Middle: partial "world" -> "helloworld"
162
162
+
// 3. Outer: length "helloworld" -> 10
163
163
+
164
164
+
// Build concat application
165
165
+
concatInnerFan := net.NewFan()
166
166
+
concatNode := net.NewNative("concat")
167
167
+
helloData := net.NewData("hello")
168
168
+
net.Link(concatInnerFan, 0, concatNode, 0)
169
169
+
net.Link(concatInnerFan, 2, helloData, 0)
170
170
+
171
171
+
concatOuterFan := net.NewFan()
172
172
+
worldData := net.NewData("world")
173
173
+
net.Link(concatOuterFan, 0, concatInnerFan, 1)
174
174
+
net.Link(concatOuterFan, 2, worldData, 0)
175
175
+
// concatOuterFan.1 is "helloworld"
176
176
+
177
177
+
// Build length application
178
178
+
lengthFan := net.NewFan()
179
179
+
lengthNode := net.NewNative("length")
180
180
+
net.Link(lengthFan, 0, lengthNode, 0)
181
181
+
net.Link(lengthFan, 2, concatOuterFan, 1) // Apply to concat result
182
182
+
183
183
+
// Connect result
184
184
+
output := net.NewVar()
185
185
+
net.Link(lengthFan, 1, output, 0)
186
186
+
187
187
+
// Reduce
188
188
+
net.ReduceAll()
189
189
+
190
190
+
// Check result
191
191
+
resultNode, _ := net.GetLink(output, 0)
192
192
+
if resultNode == nil {
193
193
+
t.Fatal("Result node is nil")
194
194
+
}
195
195
+
if resultNode.Type() != NodeTypeData {
196
196
+
t.Errorf("Expected result to be Data node, got %v", resultNode.Type())
197
197
+
}
198
198
+
199
199
+
result := resultNode.GetValue()
200
200
+
expected := 10
201
201
+
if result != expected {
202
202
+
t.Errorf("Expected %d, got %v", expected, result)
203
203
+
}
204
204
+
205
205
+
t.Logf("length (concat \"hello\" \"world\") = %v", result)
206
206
+
}
+1
pkg/deltanet/trace.go
···
14
14
RuleRepDecay
15
15
RuleRepMerge
16
16
RuleAuxFanRep
17
17
+
RuleFanNative
17
18
)
18
19
19
20
type TraceEvent struct {