-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1049 lines (815 loc) · 39.3 KB
/
index.html
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<title>Borgo Programming Language</title>
<meta charset="utf-8">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<header>
<h1>Borgo Programming Language</h1>
</header>
<main>
<div class="links-pane">
<section id="examples">
<a
href="#intro"
onClick="Borgo.selectExample('intro')"
>
Intro </a>
<a
href="#primitive-types"
onClick="Borgo.selectExample('primitive-types')"
>
Primitive Types </a>
<a
href="#control-flow"
onClick="Borgo.selectExample('control-flow')"
>
Control flow </a>
<a
href="#algebraic-data-types-and-pattern-matching"
onClick="Borgo.selectExample('algebraic-data-types-and-pattern-matching')"
>
Algebraic data types and pattern matching </a>
<a
href="#structs"
onClick="Borgo.selectExample('structs')"
>
Structs </a>
<a
href="#result-and-option"
onClick="Borgo.selectExample('result-and-option')"
>
Result and Option </a>
<a
href="#interoperability-with-go"
onClick="Borgo.selectExample('interoperability-with-go')"
>
Interoperability with Go </a>
<a
href="#package-definitions"
onClick="Borgo.selectExample('package-definitions')"
>
Package definitions </a>
<a
href="#pointers-and-references"
onClick="Borgo.selectExample('pointers-and-references')"
>
Pointers and References </a>
<a
href="#methods"
onClick="Borgo.selectExample('methods')"
>
Methods </a>
<a
href="#interfaces"
onClick="Borgo.selectExample('interfaces')"
>
Interfaces </a>
<a
href="#error-handling"
onClick="Borgo.selectExample('error-handling')"
>
Error handling </a>
<a
href="#zero-values-and-nil"
onClick="Borgo.selectExample('zero-values-and-nil')"
>
Zero values and nil </a>
<a
href="#concurrency-goroutines"
onClick="Borgo.selectExample('concurrency-goroutines')"
>
Concurrency (goroutines) </a>
<a
href="#channels"
onClick="Borgo.selectExample('channels')"
>
Channels </a>
<a
href="#select-statements"
onClick="Borgo.selectExample('select-statements')"
>
Select statements </a>
</section>
</div>
<section id="content" class="content-pane">
<article data-slug="intro">
<h2>
Intro </h2>
<div>
<p><img src="https://raw.githubusercontent.com/borgo-lang/borgo-lang.github.io/main/borgo.jpg" alt="Borgo sits between Go and Rust"></p>
<p>Borgo is a new programming language that compiles to Go.</p>
<p>For a high-level overview of the features and instructions on running the
compiler locally, check the
<a href="https://github.com/borgo-lang/borgo#readme">README</a>.</p>
<p>This playground runs the compiler as a wasm binary and then sends the transpiled
go output to the official Go playground for execution.</p>
</div>
<pre data-example=""use fmt\n\nenum NetworkState<T> {\n Loading,\n Failed(int),\n Success(T),\n}\n\nstruct Response {\n title: string,\n duration: int,\n}\n\nfn main() {\n let res = Response {\n title: \"Hello world\",\n duration: 0,\n }\n\n let state = NetworkState.Success(res)\n\n let msg = match state {\n NetworkState.Loading => \"still loading\",\n NetworkState.Failed(code) => fmt.Sprintf(\"Got error code: %d\", code),\n NetworkState.Success(res) => res.title,\n }\n\n fmt.Println(msg)\n}"">use fmt
enum NetworkState<T> {
Loading,
Failed(int),
Success(T),
}
struct Response {
title: string,
duration: int,
}
fn main() {
let res = Response {
title: "Hello world",
duration: 0,
}
let state = NetworkState.Success(res)
let msg = match state {
NetworkState.Loading => "still loading",
NetworkState.Failed(code) => fmt.Sprintf("Got error code: %d", code),
NetworkState.Success(res) => res.title,
}
fmt.Println(msg)
}</pre>
</article>
<article data-slug="primitive-types">
<h2>
Primitive Types </h2>
<div>
<p>Primitive types are the same as in Go.</p>
<p>Collections like slices and maps can be used without specifying the type of the
values.</p>
<p>For example, a slice of int elements would be declared as <code>[]int{1,2,3}</code> in Go,
whereas Borgo relies on type inference to determine the type, so you can just
write <code>[1, 2, 3]</code>.</p>
<p>Functions like <code>append()</code> and <code>len()</code> are available as methods.</p>
<p>Maps are initialized with the <code>Map.new()</code> function, which under the hood
compiles to a <code>map[K]V{}</code> expression, with the <code>K</code> and <code>V</code> types helpfully
filled in for you.</p>
<p>Borgo also has tuples! They work exactly like in Rust.</p>
<p>Multiline strings are defined by prefixing each line with <code>\\</code> like in Zig. This
has the benefit that no character needs escaping and allows more control over
whitespace.</p>
</div>
<pre data-example=""use fmt\n\nfn main() {\n let n = 1\n let s = \"hello\"\n let b = false\n\n fmt.Println(\"primitives: \", n, s, b)\n\n let mut xs = [1,2,3]\n fmt.Println(\"slice:\", xs)\n\n xs = xs.Append(10)\n fmt.Println(\"len after append:\", xs.Len())\n\n let mut m = Map.new()\n m.Insert(1, \"alice\")\n m.Insert(2, \"bob\")\n\n fmt.Println(\"map:\", m)\n\n let pair = (\"hey\", true)\n fmt.Println(\"second element in tuple:\", pair.1)\n\n let multi = \\\\a multi line\n \\\\ string with unescaped \"quotes\"\n \\\\ that ends here\n\n fmt.Println(\"multiline string:\", multi)\n}"">use fmt
fn main() {
let n = 1
let s = "hello"
let b = false
fmt.Println("primitives: ", n, s, b)
let mut xs = [1,2,3]
fmt.Println("slice:", xs)
xs = xs.Append(10)
fmt.Println("len after append:", xs.Len())
let mut m = Map.new()
m.Insert(1, "alice")
m.Insert(2, "bob")
fmt.Println("map:", m)
let pair = ("hey", true)
fmt.Println("second element in tuple:", pair.1)
let multi = \\a multi line
\\ string with unescaped "quotes"
\\ that ends here
fmt.Println("multiline string:", multi)
}</pre>
</article>
<article data-slug="control-flow">
<h2>
Control flow </h2>
<div>
<p>Like in Go, the only values that can be iterated over are slices, maps, channels
and strings.</p>
<p>However, loops always iterate over a single value, which is the element in the
slice (contrary to Go, where using a single iteration variable gives you the
index of the element).</p>
<p>To iterate over <code>(index, element)</code> pairs call the <code>.enumerate()</code> method on
slices. This has no runtime cost, it just aids the compiler in generating the
correct code.</p>
<p>When iterating over maps, you should always destructure values with
<code>(key, value)</code> pairs instead of a single value.</p>
<p>Like in Rust, infinite loops use the <code>loop {}</code> construct whereas loops with
conditions use <code>while {}</code>.</p>
<p>Expressions like <code>if</code>, <code>match</code> and blocks return a value, so you can assign
their result to a variable.</p>
</div>
<pre data-example=""use fmt\nuse math.rand\n\nfn main() {\n let xs = [\"a\", \"b\", \"c\"]\n\n fmt.Println(\"For loop over slices\")\n for letter in xs {\n fmt.Println(letter)\n }\n\n fmt.Println(\"Indexed for loop\")\n for (index, letter) in xs.Enumerate() {\n fmt.Println(index, letter)\n }\n\n let m = Map.new()\n m.Insert(1, \"alice\")\n m.Insert(2, \"bob\")\n\n fmt.Println(\"For loop over maps\")\n for (key, value) in m {\n fmt.Println(key, value)\n }\n\n fmt.Println(\"Loop with no condition\")\n loop {\n let n = rand.Float64()\n fmt.Println(\"looping...\", n)\n\n if n > 0.75 {\n break\n }\n }\n\n fmt.Println(\"While loop\")\n\n let mut count = 0\n while (count < 5) {\n fmt.Println(count)\n count = count + 1\n }\n\n fmt.Println(\"using if statements as expressions\")\n fmt.Println(if 5 > 3 { \"ok\" } else { \"nope\" })\n\n let block_result = {\n let a = 1\n let b = 2\n a + b\n }\n\n fmt.Println(\"block result:\", block_result)\n}"">use fmt
use math.rand
fn main() {
let xs = ["a", "b", "c"]
fmt.Println("For loop over slices")
for letter in xs {
fmt.Println(letter)
}
fmt.Println("Indexed for loop")
for (index, letter) in xs.Enumerate() {
fmt.Println(index, letter)
}
let m = Map.new()
m.Insert(1, "alice")
m.Insert(2, "bob")
fmt.Println("For loop over maps")
for (key, value) in m {
fmt.Println(key, value)
}
fmt.Println("Loop with no condition")
loop {
let n = rand.Float64()
fmt.Println("looping...", n)
if n > 0.75 {
break
}
}
fmt.Println("While loop")
let mut count = 0
while (count < 5) {
fmt.Println(count)
count = count + 1
}
fmt.Println("using if statements as expressions")
fmt.Println(if 5 > 3 { "ok" } else { "nope" })
let block_result = {
let a = 1
let b = 2
a + b
}
fmt.Println("block result:", block_result)
}</pre>
</article>
<article data-slug="algebraic-data-types-and-pattern-matching">
<h2>
Algebraic data types and pattern matching </h2>
<div>
<p>You can define algebraic data types with the <code>enum</code> keyword (pretty much like
Rust).</p>
<p>Pattern matches must be exhaustive, meaning the compiler will return an error
when a case is missing (try removing any case statement from the example and see
what happens!).</p>
<blockquote class="hint"><p>For now, variants can only be defined as tuples and not as structs.</p>
</blockquote> </div>
<pre data-example=""use fmt\nuse strings\n\n\nenum IpAddr {\n V4(uint8, uint8, uint8, uint8),\n V6(string),\n}\n\nfn isPrivate(ip: IpAddr) -> bool {\n match ip {\n IpAddr.V4(a, b, _, _) => {\n if a == 10 {\n return true\n }\n\n if a == 172 && b >= 16 && b <= 31 {\n return true\n }\n\n if a == 192 && b == 168 {\n return true\n }\n\n false\n }\n\n IpAddr.V6(s) => strings.HasPrefix(s, \"fc00::\")\n }\n}\n\nenum Coin {\n Penny,\n Nickel,\n Dime,\n Quarter,\n}\n\nfn valueInCents(coin: Coin) -> int {\n match coin {\n Coin.Penny => 1,\n Coin.Nickel => 5,\n Coin.Dime => 10,\n Coin.Quarter => 25,\n }\n}\n\nfn main() {\n let home = IpAddr.V4(127, 0, 0, 1)\n let loopback = IpAddr.V6(\"::1\")\n fmt.Println(\"home ip is private: \", home, isPrivate(home))\n fmt.Println(\"loopback: \", loopback)\n\n let cents = valueInCents(Coin.Nickel)\n fmt.Println(\"cents:\", cents)\n\n}"">use fmt
use strings
enum IpAddr {
V4(uint8, uint8, uint8, uint8),
V6(string),
}
fn isPrivate(ip: IpAddr) -> bool {
match ip {
IpAddr.V4(a, b, _, _) => {
if a == 10 {
return true
}
if a == 172 && b >= 16 && b <= 31 {
return true
}
if a == 192 && b == 168 {
return true
}
false
}
IpAddr.V6(s) => strings.HasPrefix(s, "fc00::")
}
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn valueInCents(coin: Coin) -> int {
match coin {
Coin.Penny => 1,
Coin.Nickel => 5,
Coin.Dime => 10,
Coin.Quarter => 25,
}
}
fn main() {
let home = IpAddr.V4(127, 0, 0, 1)
let loopback = IpAddr.V6("::1")
fmt.Println("home ip is private: ", home, isPrivate(home))
fmt.Println("loopback: ", loopback)
let cents = valueInCents(Coin.Nickel)
fmt.Println("cents:", cents)
}</pre>
</article>
<article data-slug="structs">
<h2>
Structs </h2>
<div>
<p>Defining and instantiating structs is similar to Rust.</p>
<p>Contrary to Go, all struct fields must be initialized. See the section on <code>nil</code>
and zero values for more information.</p>
</div>
<pre data-example=""use fmt\n\nstruct Person {\n name: string,\n hobbies: [Hobby],\n}\n\nenum Hobby {\n SkyDiving,\n StaringAtWall,\n Other(string),\n}\n\nfn main() {\n let mut p = Person {\n name: \"bob\",\n hobbies: [Hobby.StaringAtWall, Hobby.Other(\"sleep\")],\n }\n\n fmt.Println(\"person:\", p)\n\n p.hobbies = p.hobbies.Append(Hobby.SkyDiving)\n fmt.Println(\"with more hobbies:\", p)\n}"">use fmt
struct Person {
name: string,
hobbies: [Hobby],
}
enum Hobby {
SkyDiving,
StaringAtWall,
Other(string),
}
fn main() {
let mut p = Person {
name: "bob",
hobbies: [Hobby.StaringAtWall, Hobby.Other("sleep")],
}
fmt.Println("person:", p)
p.hobbies = p.hobbies.Append(Hobby.SkyDiving)
fmt.Println("with more hobbies:", p)
}</pre>
</article>
<article data-slug="result-and-option">
<h2>
Result and Option </h2>
<div>
<p>Sometimes it's helpful to deal with values that may or may not be there. This is
the idea behind the <code>Option<T></code> type.</p>
<p>For example, to get an element out of a slice or a map, you can use the
<code>.get(index)</code> method that will force you to handle the case where the element
isn't there.</p>
<p>Other times you may want to return a value <em>or</em> an error. In those cases use
<code>Result<T, E></code> to let the caller know that a function may return an error.</p>
<p>When you're sure that a value is <em>definitely</em> there, you can call <code>.unwrap()</code>.
Like in Rust, this is an unsafe operation and will panic.</p>
<p>A lot of methods are missing from both <code>Result</code> and <code>Option</code>, contributions to
the stdlib are welcome!</p>
</div>
<pre data-example=""use fmt\n\nstruct Person {\n name: string,\n age: int\n}\n\nfn validate(name: string, age: int) -> Result<Person, string> {\n if (age < 18) {\n return Err(\"too young\")\n }\n\n if (age > 98) {\n return Err(\"too old\")\n }\n\n Ok(Person { name, age })\n}\n\nfn main() {\n let xs = [\"a\", \"b\", \"c\"]\n let element = xs.Get(2) // Option<string>\n\n match element {\n Some(s) => fmt.Println(\"ok, the element was found:\", s),\n None => fmt.Println(\"element not found\"),\n }\n\n let result = validate(\"alice\", 33) // Result<Person, string>\n\n match result {\n Ok(p) => fmt.Println(\"got a person:\", p),\n Err(e) => fmt.Println(\"couldn't validate:\", e),\n }\n}"">use fmt
struct Person {
name: string,
age: int
}
fn validate(name: string, age: int) -> Result<Person, string> {
if (age < 18) {
return Err("too young")
}
if (age > 98) {
return Err("too old")
}
Ok(Person { name, age })
}
fn main() {
let xs = ["a", "b", "c"]
let element = xs.Get(2) // Option<string>
match element {
Some(s) => fmt.Println("ok, the element was found:", s),
None => fmt.Println("element not found"),
}
let result = validate("alice", 33) // Result<Person, string>
match result {
Ok(p) => fmt.Println("got a person:", p),
Err(e) => fmt.Println("couldn't validate:", e),
}
}</pre>
</article>
<article data-slug="interoperability-with-go">
<h2>
Interoperability with Go </h2>
<div>
<p>One ambitious goal of this project is to be fully compatible with the existing
Go ecosystem.</p>
<p>You've already seen how the <code>fmt</code> package was used in previous examples, but how
do we deal with functions that return multiple values?</p>
<p>This is where our trusty <code>Option</code> and <code>Result</code> types come in! The compiler will
handle the conversion <em>automatically</em> for you :)</p>
<p>A good mental model is to think of return types in Go functions as:</p>
<pre><code>when return type is (T, bool)
it becomes Option<T>
when return type is (T, error)
it becomes Result<T, E>
</code></pre>
<p>Let's take the <a href="https://pkg.go.dev/os#LookupEnv">os.LookupEnv</a> function as an
example:</p>
<pre><code>Go definition:
func LookupEnv(key string) (string, bool)
becomes:
fn LookupEnv(key: string) -> Option<string>
</code></pre>
<p>Or the <a href="https://pkg.go.dev/os#Stat">os.Stat</a> function from the same package:</p>
<pre><code>Go definition:
func Stat(name string) (FileInfo, error)
becomes:
fn Stat(name: string) -> Result<FileInfo>
</code></pre>
<blockquote class="hint"><p><code>Result<T></code> is short-hand for <code>Result<T, error></code> where <code>error</code> is the standard
Go interface.</p>
</blockquote>
<p>With this simple convention, pretty much any Go package can be used in Borgo
code! All is needed is a package declaration, which is discussed in the next
section.</p>
</div>
<pre data-example=""use fmt\nuse os\n\nfn main() {\n let key = os.LookupEnv(\"HOME\")\n\n match key {\n // Option<T>\n Some(s) => fmt.Println(\"home dir:\", s),\n None => fmt.Println(\"Not found in env\"),\n }\n\n let info = os.Stat(\"file-does-not-exist\")\n\n match info {\n // Result<T, E>\n Ok(_) => fmt.Println(\"The file exists\"),\n Err(err) => fmt.Println(\"Got error reading file\", err),\n }\n}"">use fmt
use os
fn main() {
let key = os.LookupEnv("HOME")
match key {
// Option<T>
Some(s) => fmt.Println("home dir:", s),
None => fmt.Println("Not found in env"),
}
let info = os.Stat("file-does-not-exist")
match info {
// Result<T, E>
Ok(_) => fmt.Println("The file exists"),
Err(err) => fmt.Println("Got error reading file", err),
}
}</pre>
</article>
<article data-slug="package-definitions">
<h2>
Package definitions </h2>
<div>
<p>In order to use existing Go packages, Borgo needs to know what types and
functions they contain. This is done in declaration files, which serve a similar
purpose to what you might see in Typescript with <code>d.ts</code> files.</p>
<p>Only a small part of the Go stdlib is currently available for use in Borgo --
check the <a href="https://github.com/borgo-lang/borgo/tree/main/std">std/</a> folder for
more information.</p>
<p>The example on the right uses the <code>regexp</code> package from the Go standard library.
The relevant bindings are defined in <code>std/regexp/regexp.brg</code> (here's a snippet):</p>
<pre><code>struct Regexp { }
fn Compile (expr: string) -> Result<*Regexp> { EXT }
fn CompilePOSIX (expr: string) -> Result<*Regexp> { EXT }
fn MustCompile (str: string) -> *Regexp { EXT }
fn MustCompilePOSIX (str: string) -> *Regexp { EXT }
fn Match (pattern: string, b: [byte]) -> Result<bool> { EXT }
// ... other stuff
</code></pre>
<p>Writing such declarations by hand is a pain! There's no reason why this process
couldn't be automated though. The compiler comes with an <code>importer</code> tool that
parses a Go package and generates corresponding bindings to be used in Borgo.</p>
</div>
<pre data-example=""use fmt\nuse regexp\n\nfn main() {\n let validID = regexp.MustCompile(\"^[a-z]+[[0-9]+]$\")\n\n fmt.Println(validID.MatchString(\"adam[23]\"))\n fmt.Println(validID.MatchString(\"eve[7]\"))\n}"">use fmt
use regexp
fn main() {
let validID = regexp.MustCompile("^[a-z]+[[0-9]+]$")
fmt.Println(validID.MatchString("adam[23]"))
fmt.Println(validID.MatchString("eve[7]"))
}</pre>
</article>
<article data-slug="pointers-and-references">
<h2>
Pointers and References </h2>
<div>
<p>Pointers and References work the same as in Go.</p>
<p>To dereference a pointer, use <code>foo.*</code> instead of <code>*foo</code> (like in Zig).</p>
</div>
<pre data-example=""use fmt\n\nstruct Foo {\n bar: int\n}\n\nstruct Bar {\n foo: *Foo\n}\n\nfn main() {\n let mut f = Foo { bar: 0 }\n let b = Bar { foo: &f }\n\n f.bar = 99\n\n fmt.Println(b.foo)\n\n // pointer dereference\n // In Go, this would be: *b.foo = ...\n b.foo.* = Foo { bar: 23 }\n\n fmt.Println(b.foo)\n}"">use fmt
struct Foo {
bar: int
}
struct Bar {
foo: *Foo
}
fn main() {
let mut f = Foo { bar: 0 }
let b = Bar { foo: &f }
f.bar = 99
fmt.Println(b.foo)
// pointer dereference
// In Go, this would be: *b.foo = ...
b.foo.* = Foo { bar: 23 }
fmt.Println(b.foo)
}</pre>
</article>
<article data-slug="methods">
<h2>
Methods </h2>
<div>
<p>To define methods on types, you can use <code>impl {}</code> blocks.</p>
<p>In Go, the method receiver must be specified at each function declaration. In
Borgo, this is specified only once at the beginning of the <code>impl</code> block
(<code>p: *Person</code>). All functions within the block will have that receiver.</p>
<p>It's also possible to declare static methods: functions can be declared with
dots in their name, so you can define a <code>Person.new</code> function like in the
example.</p>
</div>
<pre data-example=""use fmt\n\nstruct Person {\n name: string,\n hours_slept: int,\n}\n\nfn Person.new(name: string) -> Person {\n Person {\n name,\n hours_slept: 0,\n }\n}\n\nimpl (p: *Person) {\n fn sleep() {\n p.hours_slept = p.hours_slept + 1 \n }\n\n fn ready_for_work() -> bool {\n p.hours_slept > 5\n }\n\n fn ready_to_party() -> bool {\n p.hours_slept > 10\n }\n}\n\nfn main() {\n let mut p = Person.new(\"alice\")\n\n p.sleep()\n p.sleep()\n\n fmt.Println(\"is ready:\", p.ready_for_work())\n}"">use fmt
struct Person {
name: string,
hours_slept: int,
}
fn Person.new(name: string) -> Person {
Person {
name,
hours_slept: 0,
}
}
impl (p: *Person) {
fn sleep() {
p.hours_slept = p.hours_slept + 1
}
fn ready_for_work() -> bool {
p.hours_slept > 5
}
fn ready_to_party() -> bool {
p.hours_slept > 10
}
}
fn main() {
let mut p = Person.new("alice")
p.sleep()
p.sleep()
fmt.Println("is ready:", p.ready_for_work())
}</pre>
</article>
<article data-slug="interfaces">
<h2>
Interfaces </h2>
<div>
<p>Interfaces in Borgo work the same as in Go, it's all duck typing.</p>
<p>If a type implements the methods declared by the interface, then the type is an
instance of that interface.</p>
<p>Embedded interfaces are also supported, just list out the other interfaces
<em>implied</em> by the one being defined (prefixed by <code>impl</code>). For example, the
<code>ReadWriter</code> interface from the <code>io</code> package can be defined as:</p>
<pre><code>interface ReadWriter {
impl Reader
impl Writer
}
</code></pre>
<blockquote class="hint"><p><a href="https://go.dev/ref/spec#General_interfaces">type sets</a> are not supported.</p>
</blockquote> </div>
<pre data-example=""use fmt\nuse math\n\ninterface geometry {\n fn area() -> float64\n fn perim() -> float64\n}\n\nstruct rect {\n width: float64,\n height: float64,\n}\n\nimpl (r: rect) {\n fn area() -> float64 {\n r.width * r.height\n }\n\n fn perim() -> float64 {\n 2 * r.width + 2 * r.height\n }\n}\n\nstruct circle {\n radius: float64,\n}\n\nimpl (c: circle) {\n fn area() -> float64 {\n math.Pi * c.radius * c.radius\n }\n\n fn perim() -> float64 {\n 2 * math.Pi * c.radius\n }\n}\n\nfn measure(g: geometry) {\n fmt.Println(g)\n fmt.Println(g.area())\n fmt.Println(g.perim())\n}\n\nfn main() {\n let r = rect {\n width: 3,\n height: 4,\n }\n\n let c = circle { radius: 5 }\n\n measure(r)\n measure(c)\n}"">use fmt
use math
interface geometry {
fn area() -> float64
fn perim() -> float64
}
struct rect {
width: float64,
height: float64,
}
impl (r: rect) {
fn area() -> float64 {
r.width * r.height
}
fn perim() -> float64 {
2 * r.width + 2 * r.height
}
}
struct circle {
radius: float64,
}
impl (c: circle) {
fn area() -> float64 {
math.Pi * c.radius * c.radius
}
fn perim() -> float64 {
2 * math.Pi * c.radius
}
}
fn measure(g: geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
fn main() {
let r = rect {
width: 3,
height: 4,
}
let c = circle { radius: 5 }
measure(r)
measure(c)
}</pre>
</article>
<article data-slug="error-handling">
<h2>
Error handling </h2>
<div>
<p>In functions that return a <code>Result</code>, it's possible to propagate errors with the
<code>?</code> operator.</p>
<p>This is similar to what happens in Rust, refer to the section on
<a href="https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#propagating-errors">Propagating errors</a>
in the Rust book .</p>
<p>Currently the <code>?</code> operator only works with <code>Result</code>, but it will be extended to
also work with <code>Option</code>.</p>
</div>
<pre data-example=""use fmt\nuse io\nuse os\n\nfn copy_file(src: string, dst: string) -> Result<(), error> {\n let stat = os.Stat(src)?\n\n if !stat.Mode().IsRegular() {\n return Err(fmt.Errorf(\"%s is not a regular file\", src))\n }\n\n let source = os.Open(src)?\n defer source.Close()\n\n let destination = os.Create(dst)?\n defer destination.Close()\n\n // ignore number of bytes copied\n let _ = io.Copy(destination, source)?\n\n Ok(())\n}\n\nfn copy_all_files(folder: string) -> Result<int, error> {\n let mut n = 0\n\n for f in os.ReadDir(folder)? {\n if !f.IsDir() {\n let original = f.Name()\n let new_name = fmt.Sprintf(\"%s-copy\", original)\n\n fmt.Println(\"copying\", original, \"to\", new_name)\n\n copy_file(original, new_name)?\n n = n + 1\n }\n }\n\n Ok(n)\n}\n\nfn main() {\n match copy_all_files(\".\") {\n Ok(n) => fmt.Println(n, \"files copied\"),\n Err(err) => fmt.Println(\"Got error:\", err),\n }\n}"">use fmt
use io
use os
fn copy_file(src: string, dst: string) -> Result<(), error> {
let stat = os.Stat(src)?
if !stat.Mode().IsRegular() {
return Err(fmt.Errorf("%s is not a regular file", src))
}
let source = os.Open(src)?
defer source.Close()
let destination = os.Create(dst)?
defer destination.Close()
// ignore number of bytes copied
let _ = io.Copy(destination, source)?
Ok(())
}
fn copy_all_files(folder: string) -> Result<int, error> {
let mut n = 0
for f in os.ReadDir(folder)? {
if !f.IsDir() {
let original = f.Name()
let new_name = fmt.Sprintf("%s-copy", original)
fmt.Println("copying", original, "to", new_name)
copy_file(original, new_name)?
n = n + 1
}
}
Ok(n)
}
fn main() {
match copy_all_files(".") {
Ok(n) => fmt.Println(n, "files copied"),
Err(err) => fmt.Println("Got error:", err),
}
}</pre>
</article>
<article data-slug="zero-values-and-nil">
<h2>
Zero values and nil </h2>
<div>
<p>In Borgo, you can't create <code>nil</code> values.</p>
<p>The concept of <code>null</code> references (or <code>nil</code> in this case) is being referred to as
"The billion dollar mistake" and modern languages are moving away from it with
types like <code>Option<T></code>. Borgo tries to do the same.</p>
<p>You can still end up with null pointers if you're calling into existing Go code,
which is unfortunate. That should be solvable by writing better bindings, so
that functions that could return a null pointer, will instead return an
<code>Option<*T></code>, forcing you to handle all cases.</p>
<p>In Go, it's common to see types not needing to be initialized, as their <em>zero
value</em> is ready to be used (ie. <code>sync.Mutex</code> or <code>sync.WaitGroup</code>). Borgo goes in
the opposite direction, requiring that all values are explicitely initialized.</p>
<p>You can use the built-in function <code>zeroValue()</code> whenever you need the <em>zero
value</em> of a type. While you won't need to provide a type annotation in all cases
(as the type can be inferred), it's probably clearer to annotate variables that
are initialized with <code>zeroValue()</code>.</p>
<p>As mentioned in a previous section, this also applies to struct fields, which
always need to be initialized.</p>
</div>
<pre data-example=""use sync\nuse bytes\nuse fmt\n\nfn main() {\n // in Go:\n // var wg sync.WaitGroup\n let wg: sync.WaitGroup = zeroValue()\n\n // in Go:\n // var b bytes.Buffer\n let b: bytes.Buffer = zeroValue()\n\n fmt.Println(\"variables are initialized:\", wg, b)\n}"">use sync
use bytes
use fmt
fn main() {
// in Go:
// var wg sync.WaitGroup
let wg: sync.WaitGroup = zeroValue()
// in Go:
// var b bytes.Buffer
let b: bytes.Buffer = zeroValue()
fmt.Println("variables are initialized:", wg, b)
}</pre>
</article>
<article data-slug="concurrency-goroutines">
<h2>
Concurrency (goroutines) </h2>
<div>
<p>Borgo aims to support all concurrency primitives available in Go.</p>
<p>Use the <code>spawn</code> keyword (instead of <code>go</code>) to start a goroutine. The parameter
needs to be a function call.</p>
<p>Channels and <code>select {}</code> statements are discussed next.</p>
</div>
<pre data-example=""use sync\nuse fmt\n\nstruct Counter {\n count: int,\n mu: sync.Mutex,\n}\n\nfn Counter.new() -> Counter {\n Counter { count: 0, mu: zeroValue() }\n}\n\nimpl (c: *Counter) {\n fn Inc() {\n c.mu.Lock() \n c.count = c.count + 1\n c.mu.Unlock() \n }\n}\n\nfn main() {\n let desired = 1000\n let counter = Counter.new()\n\n let wg: sync.WaitGroup = zeroValue()\n wg.Add(desired)\n\n let mut i = 0\n\n while (i < desired) {\n\n // equivalent to: go func() { ... }()\n spawn (|| {\n counter.Inc()\n wg.Done()\n })()\n\n i = i + 1\n }\n\n wg.Wait()\n\n fmt.Println(\"Counter value:\", counter.count)\n}"">use sync
use fmt
struct Counter {
count: int,
mu: sync.Mutex,
}
fn Counter.new() -> Counter {
Counter { count: 0, mu: zeroValue() }
}
impl (c: *Counter) {
fn Inc() {
c.mu.Lock()
c.count = c.count + 1
c.mu.Unlock()
}
}
fn main() {
let desired = 1000
let counter = Counter.new()
let wg: sync.WaitGroup = zeroValue()
wg.Add(desired)
let mut i = 0
while (i < desired) {
// equivalent to: go func() { ... }()
spawn (|| {
counter.Inc()
wg.Done()
})()
i = i + 1
}
wg.Wait()
fmt.Println("Counter value:", counter.count)
}</pre>
</article>
<article data-slug="channels">
<h2>
Channels </h2>
<div>
<p>Borgo doesn't provide any extra syntax to send/receive from channels.</p>
<p>You use <code>Channel.new()</code> to create a <code>Sender<T></code> and <code>Receiver<T></code>.These are
roughly equivalent to send-only and receive-only channels in Go and will compile
to raw channels in the final Go output.</p>
<p>With a <code>Sender<T></code> you can call <code>send(value: T)</code> to send a value. With a
<code>Receiver<T></code> you can call <code>recv() -> T</code> to receive a value.</p>
<p>This design is somewhat inspired by the <code>sync::mspc::channel</code> module in the Rust
standard library.</p>
</div>
<pre data-example=""use fmt\n\nfn main() {\n let (sender, receiver) = Channel.new()\n\n spawn (|| {\n sender.Send(1)\n })()\n\n spawn (|| {\n sender.Send(2)\n })()\n\n let msg = receiver.Recv()\n let msg2 = receiver.Recv()\n\n fmt.Println(msg + msg2)\n}"">use fmt
fn main() {
let (sender, receiver) = Channel.new()
spawn (|| {
sender.Send(1)
})()
spawn (|| {
sender.Send(2)
})()
let msg = receiver.Recv()
let msg2 = receiver.Recv()
fmt.Println(msg + msg2)
}</pre>
</article>
<article data-slug="select-statements">
<h2>
Select statements </h2>
<div>
<p><code>select {}</code> works like in Go, however the syntax is slightly different.</p>
<pre><code>Reading from a channel
Go: case x := <- ch
Borgo: let x = ch.Recv()
Sending to a channel
Go: case ch <- x
Borgo: ch.Send(x)
Default case
Go: default
Borgo: _
</code></pre>
</div>
<pre data-example=""use fmt\nuse time\n\nfn main() {\n let (tx1, rx1) = Channel.new()\n let (tx2, rx2) = Channel.new()\n\n // dummy done channel\n let (_, done) = Channel.new()\n\n spawn (|| {\n tx1.Send(\"a\")\n })()\n\n spawn (|| {\n loop {\n select {\n // in Go:\n // case tx2 <- \"b\":\n tx2.Send(\"b\") => {\n fmt.Println(\"sending b\")\n time.Sleep(1 * time.Second)\n }\n\n let _ = done.Recv() => return\n }\n }\n })()\n\n select {\n // in Go:\n // case a := <- rx1:\n let a = rx1.Recv() => {\n fmt.Println(\"got\", a)\n },\n\n let b = rx2.Recv() => {\n fmt.Println(\"got\", b)\n },\n }\n}"">use fmt
use time
fn main() {
let (tx1, rx1) = Channel.new()
let (tx2, rx2) = Channel.new()
// dummy done channel
let (_, done) = Channel.new()
spawn (|| {
tx1.Send("a")
})()
spawn (|| {
loop {
select {
// in Go:
// case tx2 <- "b":
tx2.Send("b") => {
fmt.Println("sending b")
time.Sleep(1 * time.Second)
}
let _ = done.Recv() => return
}
}
})()
select {
// in Go:
// case a := <- rx1:
let a = rx1.Recv() => {
fmt.Println("got", a)
},