内容简介:This post intends to showcase programming patterns, orAll of these code snippets should be complete as shown and compilable/runnable in the state presented. The Alef examples might have subtle typos, but this is due to the fact I had to manually re-type th
Go’s History in Code
This post intends to showcase programming patterns, or stuff , which is common between Newsqueak, Alef, Plan9 C, Limbo, and Go.
All of these code snippets should be complete as shown and compilable/runnable in the state presented. The Alef examples might have subtle typos, but this is due to the fact I had to manually re-type them out from VirtualBox since I didn’t have copy/paste available to the guest.
Commentary has been inserted before examples where I believe it will be helpful.
If I missed a feature or misreported a fact, feel free to open an issue/PR against the blog on GitHub .
Motivation
Articles or posts talking about Go’s predecessors have a habit of referring to the languages listed above, but can fail to provide concrete resources for seeing how these languages work. This post aims to provide a reference for such languages.
I find being able to play with languages and see complete programs showing how the language fits together as a whole rather than just small snippets extremely valuable for learning.
In particular with older languages, it can be difficult to find ways to practically experiment with the language and get a feel for the ergonomics of the error reporting, building of complex programs, etc. without having access to the compiler/interpreter/environment itself. As such, paths to being able to write in these languages have been provided for each language presented.
Note that this reference is not intended to be exhaustive.
Building and running examples
Newsqueak
The unix port of squint is found at https://github.com/rwos/newsqueak .
The papers describing Newsqueak:
To run a program from a prompt:
$ squint foo.nq # output, if any $
Alef
Your best bet at trying Alef is installing a Plan9 2nd edition (2e) virtual machine. A text guide for this process is ina prior blog post and a complementary video guide for installation .
Papers on Alef: http://doc.cat-v.org/plan_9/2nd_edition/papers/alef/
More resources on 2e: http://9.postnix.pw/hist/2e/
Direct download to a VirtualBox image of 2e: http://9.postnix.pw/hist/2e/plan92e_vbox.tgz
There’s also a work-in-progress port of Alef from 2e to 9front/386 which can be found on the public grid
griddisk at /burnzez/rep/alef/root
and maybe /burnzez/alef
. Griddisk is accessible over 9p via tcp!45.63.75.148!9564
. You can more easily access the grid from unix via the gridnix scripts
.
From a prompt on a complete Plan9 2e installation:
term% 8al foo.l term% 8l foo.8 term% 8.out # output, if any term%
Plan9 C
The most actively maintained Plan9 fork is 9front .
A more Bell Labs-like experience may be found in 9legacy . Instructions and workflow should be similar.
Papers describing the Plan9 C dialect:
- http://doc.cat-v.org/plan_9/programming/c_programming_in_plan_9
- http://doc.cat-v.org/plan_9/4th_edition/papers/compiler
The Plan9 C dialect was partially described with a narrative in a previous blog post .
From a 386 9front system:
term% 8c foo.c term% 8l foo.8 term% 8.out # output, if any term%
From an amd64 9front system:
term% 6c foo.c term% 6l foo.6 term% 6.out # output, if any term%
Arm uses 5c
and 5l
for compiling/linking respectively as per the manuals 2c(1)
and 2l(1)
.
Limbo
The official Inferno repository: https://bitbucket.org/inferno-os/inferno-os/
The purgatorio Inferno fork: https://code.9front.org/hg/purgatorio
There are a variety of other resources for Inferno and Limbo available.
Papers describing Limbo:
- http://doc.cat-v.org/inferno/4th_edition/limbo_language/descent
- http://doc.cat-v.org/inferno/4th_edition/limbo_language/limbo
- http://doc.cat-v.org/inferno/4th_edition/limbo_language/addendum
From a prompt inside the Inferno virtual machine (or native):
; limbo foo.b ; foo # output, if any ;
Go
Go can be acquired from https://golang.org .
The specification for Go: https://golang.org/ref/spec
To run a single file program:
$ go run foo.go # output, if any $
Intro - tokenizing
This section demonstrates standard library naïve tokenizing.
Newsqueak
Nope.
Alef
#include <alef.h> #define NTOKS 9 #define MAXTOK 512 #define str "abc » 'test 1 2 3' !" void main(void) { int n, i; byte *toks[MAXTOK]; print("%s\n", str); n = tokenize(str, toks, NTOKS); for(i = 0; i < n; i++) print("%s\n", toks[i]); exits(nil); }
Output
abc » 'test 1 2 3' ! abc » 'test 1 2 3' !
Plan9 C
Related reading: tokenize(2)
#include <u.h> #include <libc.h> #define NTOKS 9 #define MAXTOK 512 char *str = "abc ☺ 'test 1 2 3' !"; void main(int, char*[]) { int n, i; char *toks[MAXTOK]; print("%s\n", str); n = tokenize(str, (char**)toks, NTOKS); for(i = 0; i < n; i++) print("%s\n", toks[i]); exits(nil); }
Output
abc ☺ 'test 1 2 3' ! abc ☺ test 1 2 3 !
Limbo
Related reading: sys-tokenize(2)
implement Tokenizing; include "sys.m"; sys: Sys; include "draw.m"; Tokenizing: module { init: fn(nil: ref Draw->Context, nil: list of string); }; str: con "abc ☺ 'test 1 2 3' !"; init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; sys->print("%s\n", str); (n, toks) := sys->tokenize(str, "\n\t "); for(; toks != nil; toks = tl toks) { sys->print("%s\n", hd toks); } exit; }
Output
abc ☺ 'test 1 2 3' ! abc ☺ 'test 1 2 3' !
Go
package main import ( "fmt" "strings" ) const str = "abc ☺ 'test 1 2 3' !" func main() { fmt.Println(str) fields := strings.Fields(str) for _, f := range fields { fmt.Println(f) } }
Output
abc ☺ 'test 1 2 3' ! abc ☺ 'test 1 2 3' !
Asynchronous spawning
Many of the languages which inspired Go contained simple abstractions for running functions in asychronous coroutines, processes, or threads.
Newsqueak
double := prog(n : int) { print(2*n, "\n"); }; # Begin main logic begin double(7); begin double(9); begin double(11);
Output
Alef
#include <alef.h> void double(int n) { print("%d\n", 2*n); } void main(void) { task double(7); /* A coroutine */ proc double(9); /* A process */ par { double(11); /* A process */ double(13); /* A process */ } sleep(5); }
Output
Plan9 C
Related reading: thread(2)
#include <u.h> #include <libc.h> #include <thread.h> void doubleN(void *n) { print("%d\n", 2*(*(int*)n)); } void threadmain(int, char*[]) { int s₀ = 7, s₁ = 9; threadcreate(doubleN, &s₁, 4096); // A thread proccreate(doubleN, &s₀, 4096); // A process sleep(5); threadexitsall(nil); }
Output
Limbo
implement Coroutines; include "sys.m"; sys: Sys; include "draw.m"; Coroutines: module { init: fn(nil: ref Draw->Context, nil: list of string); }; double(n: int) { sys->print("%d\n", 2*n); } init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; spawn double(7); sys->sleep(5); exit; }
Output
Go
package main import ( "fmt" "time" ) func double(n int) { fmt.Println(2*n) } func main() { go double(7) time.Sleep(5 * time.Millisecond) }
Output
Sending and receiving on channels
Channels are a core part of the Communicating Sequential Processes (CSP)model for concurrent synchronization and communication.
Go and its predecessors support standard library/primitive-level tooling for working with channels.
Newsqueak
max := 10; # Prints out numbers as they're received printer := prog(c: chan of int) { i : int; for(i = 0; i < max; i++){ n := <- c; print(n, " "); } print("\n"); }; # Pushes values into the channel pusher := prog(c: chan of int) { i : int; for(i = 0; i < max; i++){ c <-= i * i; } }; # Begin main logic printChan := mk(chan of int); begin printer(printChan); begin pusher(printChan);
Output
Alef
Note the ?
and [n]
syntax for channels as defined in the “Alef User’s Guide”
.
#include <alef.h> int max = 10; void pusher(chan(int) printchan) { int i; for(i = 0; i < max; i++) printchan <-= i * i; } void printer(chan(int) printchan) { int n, i; for(i = 0; i < max; i++){ n = <-printchan; print("%d\n", n); } } void main(void) { chan(int) printchan; chan(int)[2] bufchan; alloc printchan, bufchan; par { pusher(printchan); printer(printchan); } while(bufchan?){ i = ++i * ++i; bufchan <- = i; print("sent %d\n", i); } while(?bufchan) print("received %d\n", <-bufchan); }
Output
0 1 4 9 16 25 36 49 64 81 sent 2 sent 12 received 2 received 12
Plan9 C
Related reading: thread(2)
#include <u.h> #include <libc.h> #include <thread.h> const max = 10; void printer(void *v) { Channel *printchan = (Channel*) v; int i, n; for(i = 0; i < max; i++){ recv(printchan, &n); print("received → %d\n", n); } threadexits(nil); } void pusher(void *v) { Channel *printchan = (Channel*) v; int i, *n; n = calloc(1, sizeof (int)); for(i = 0; i < max; i++){ *n = i * i; send(printchan, n); } threadexits(nil); } void threadmain(int, char*[]) { int bufsize = 2; Channel *printchan = chancreate(sizeof (int), 0); proccreate(printer, printchan, 4096); proccreate(pusher, printchan, 4096); sleep(100); threadexitsall(nil); }
Output
received → 0 received → 1 received → 4 received → 9 received → 16 received → 25 received → 36 received → 49 received → 64 received → 81
Limbo
implement Channels; include "sys.m"; sys: Sys; include "draw.m"; Channels: module { init: fn(nil: ref Draw->Context, nil: list of string); }; max : con 10; printer(c: chan of int) { i : int; for(i = 0; i < max; i++){ n := <- c; sys->print("%d ", n); } sys->print("\n"); } pusher(c: chan of int) { i : int; for(i = 0; i < max; i++){ c <-= i * i; } } init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; printChan := chan of int; spawn printer(printChan); spawn pusher(printChan); sys->sleep(1); exit; }
Output
Go
Note that Go is not the only language to support the optional ok
value when reading out of a channel, the Plan9 C functions send(2)
and recv(2)
have a return value: “Send and recv return 1 on success, -1 if interrupted.”
package main import ( "fmt" ) const max = 10 func printer(c chan int, done chan bool) { for { n, ok := <- c if !ok { break } fmt.Print(n, " ") } fmt.Println() done <- true } func pusher(c chan int) { for i := 0; i < max; i++ { c <- i * i } close(c) } func main() { c := make(chan int, 2) done := make(chan bool) go printer(c, done) go pusher(c) <- done }
Output
Selecting on multiple channels
Continuing with the CSP channel communication model, Go and its predecessors support primitives for alternating - or selecting - on multiple channels, typically in switch/case-like statement specifying a case of ready to send/receive.
Newsqueak
max := 2; # Selects on two channels for both being able to receive and send selector := prog(prodChan : chan of int, recChan : chan of int, n : int){ i : int; for(;;) select{ case i =<- prodChan: print("case recv ← ", i, "\n"); case recChan <-= n: print("case send → ", n, "\n"); } }; # Pushes `max` values into `prodChan` producer := prog(n : int, prodChan : chan of int){ i : int; for(i = 0; i < max; i++){ print("pushed → ", n, "\n"); prodChan <-= n; } }; # Reads `max` values out of `recChan` receiver := prog(recChan : chan of int){ i : int; # Stop receiving, manually for(i = 0; i < max; i++) print("received → ", <- recChan, "\n"); }; # Begin main logic prodChan := mk(chan of int); recChan := mk(chan of int); begin producer(123, prodChan); begin receiver(recChan); begin selector(prodChan, recChan, 456);
Output
pushed → 123 pushed → 123 case recv ← 123 case send → 456 received → 456 case send → 456 received → 456 case recv ← 123
Alef
#include <alef.h> int max = 2; void selector(chan(int) prodchan, chan(int) recchan, int n) { int i; for(;;) alt{ case i =<- prodchan: print("case recv ← %d\n", i); case recchan <-= n: print("case send → %d\n", n); } } void producer(int n, chan(int) prodchan) { int i; for(i = 0; i < max; i++){ print("pushed → %d\n", n); prodchan <-= n; } } void receiver(chan(int) recchan) { int i; for(i = 0; i < max; i++){ int n; n = <- recchan; print("received → %d\n", n); } } void main(void) { chan(int) prodchan; chan(int) recchan; alloc prodchan; alloc recchan; proc producer(123, prodchan); proc receiver(recchan); proc selector(prodchan, recchan, 456); sleep(15); }
Output
pushed → 123 case send → 456 case recv ← 123 received → 456 received → 456 pushed → 123 case send → 456 case recv ← 123
Plan9 C
Related reading: thread(2)
#include <u.h> #include <libc.h> #include <thread.h> const int max = 2; typedef struct Tuple Tuple; struct Tuple { Channel *a; Channel *b; }; void selector(void *v) { Tuple *t = (Tuple*)v; Channel *prodchan = t->a; Channel *recchan = t->b; // Set up vars for alt int pn; int *rn = malloc(1 * sizeof (int)); *rn = 456; // Set up alt Alt alts[] = { {prodchan, &pn, CHANRCV}, {recchan, rn, CHANSND}, {nil, nil, CHANEND}, }; for(;;) switch(alt(alts)){ case 0: // prodchan open for reading recv(prodchan, &pn); print("case recv ← %d\n", pn); break; case 1: // recchan open for writing send(recchan, rn); print("case send → %d\n", *rn); break; default: break; } } void producer(void *v) { Channel *prodchan = (Channel*)v; int *n = malloc(1 * sizeof (int)); *n = 123; int i; for(i = 0; i < max; i++){ print("pushed → %d\n", *n); send(prodchan, n); } chanclose(prodchan); } void receiver(void *v) { Channel *recchan = (Channel*)v; int i; int n; for(i = 0; i < max; i++){ recv(recchan, &n); print("received → %d\n", n); } chanclose(recchan); } void threadmain(int, char*[]) { // Set up channels Channel *prodchan = chancreate(sizeof (int), max); Channel *recchan = chancreate(sizeof (int), max); Tuple *chans = malloc(1 * sizeof (Tuple)); chans->a = prodchan; chans->b = recchan; // Start processes proccreate(producer, prodchan, 4096); proccreate(receiver, recchan, 4096); proccreate(selector, chans, 4096); sleep(1000); threadexitsall(nil); }
Output
pushed → 123 received → 456 case send → 456 pushed → 123 received → 456 case send → 456 case recv ← 123 case send → 456 case recv ← 123
Limbo
implement Select; include "sys.m"; sys: Sys; print: import sys; include "draw.m"; Select: module { init: fn(nil: ref Draw->Context, nil: list of string); }; max : con 2; selector(prodChan: chan of int, recChan: chan of int, n: int) { for(;;) alt { i := <- prodChan => print("case recv ← %d\n", i); recChan <-= n => print("case send → %d\n", n); * => break; } } producer(n: int, prodChan: chan of int) { for(i := 0; i < max; i++){ print("pushed → %d\n", n); prodChan <-= n; } } receiver(recChan: chan of int) { for(i := 0; i < max; i++) print("received → %d\n", <- recChan); } init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; prodChan := chan of int; recChan := chan of int; spawn producer(123, prodChan); spawn receiver(recChan); spawn selector(prodChan, recChan, 456); sys->sleep(1000); exit; }
Output
pushed → 123 case send → 456 received → 456 case recv ← 123 pushed → 123 case recv ← 123 case send → 456 received → 456
Go
package main import ( "fmt" "time" ) func printer(intChan chan int, strChan chan string, stopChan chan bool) { strClosed := false loop: for { select { case n := <- intChan: fmt.Println(n) case s, ok := <- strChan: if !ok { strClosed = true } else { fmt.Println(s) } case stopChan <- true: if strClosed { break loop } } } fmt.Println("done.") } func makeInts(intChan chan int, stopChan chan bool) { for i := 0; i < 3; i++ { intChan <- i*i } <- stopChan } func makeStrings(strChan chan string) { strings := []string{"a", "b", "☺"} for _, s := range strings { strChan <- s } close(strChan) } func main() { stopChan := make(chan bool, 1) stopChan <- true intChan := make(chan int) size := 3 strChan := make(chan string, size) go printer(intChan, strChan, stopChan) go makeInts(intChan, stopChan) go makeStrings(strChan) time.Sleep(10 * time.Millisecond) }
Output
0 a 1 b ☺ 4 done.
Unnamed struct members and promotion
Go, including some of its predecessors, includes the ability to have unnamed sub-structures which can be contextually promoted.
The details of how this promotion works, if present, is found under each language’s respective specification/documentation.
Pay attention to how and where different elements of different structures are called in these examples.
Note that gcc supports this feature in a similar way under the -fplan9-extensions
flag.
Newsqueak
Nope.
Alef
#include <alef.h> aggr Point { int x; int y; }; aggr Line { Point p1; Point p2; }; aggr Circle { Point; int radius; }; aggr Shape { int type; union { Circle; Line; }; }; adt Person { extern int age; extern void ageup(*Person, int); extern Person init(); }; adt Worker { extern Person; extern byte *position; extern Worker init(Person); }; Worker Worker.init(Person p) { Worker w; w.position = "none"; w.Person = p; return w; } void Person.ageup(Person *p, int n) { p->age += n; } Person Person.init() { Person p; p.age = 1; return p; } int equal(Point p1, Point p2) { return p1.x == p2.x && p1.y == p2.y; } Point mirror(Point p) { p.x *= -1; p.y *= -1; return p; } void main(void) { Point p0, p1; Circle c; Shape s; Line l; Person sean, ana; Worker engineer; p0 = (Point)(3, -1); c.Point = p0; p0 = c; p1 = mirror(c); if(equal(p0, c)) print("p0 = c\n"); else print("p0 ≠ c\n"); print("c's point = (%d,%d)\n", c.x, c.y); print("p1 = (%d,%d)\n", p1.x, p1.y); l = (Line)(p0, p1); s.Line = l; s.type = 0; /* a line */ if(s.type == 0) print("Shape is line (%d,%d) → (%d,%d)\n, s.p1.x, s.p1.y, s.p2.x, s.p2.y); sean = .Person.init(); engineer = .Worker.init(sean); engineer.ageup(2); print("engineer position \"%s\" is %d years old\n", engineer.position, engineer.age); ana = engineer; print("ana age = %d\n", ana.age); }
Output
p0 = c c's point = (3,-1) p1 = (-3,1) Shape is line (3,-1) → (-3,1) engineer position "none" is 3 years old ana age = 3
Plan9 C
This example is partially derived from the unnamed subunion example presented in the ‘Plan 9 C Compilers’ paper.
#include <u.h> #include <libc.h> double π = 3.14; typedef struct Point Point; typedef struct Circle Circle; typedef struct Number Number; typedef struct Value Value; struct Number { union { double dval; float fval; long lval; }; }; struct Value { Number; }; struct Point { int x; int y; }; struct Circle { Point; int radius; }; Point mirror(Point p) { return (Point) {-1 * p.x, -1 * p.y}; } void main(int, char*[]) { Point p₀ = {.x = 3, .y = -1}; Circle c = {p₀, 12}; Point p₁ = c.Point; print("p₀ = (%d,%d)\nradius = %d\n", c.x, c.y, c.radius); print("p₁ = (%d,%d)\n", p₁.x, p₁.y); Point p₂ = mirror((Point){c.x, c.y}); print("p₂ = (%d,%d)\n", p₂.x, p₂.y); Value v = {π}; print("value = %f\nd = %p\nf = %p\nl = %p\n", v.dval, &v.dval, &v.fval, &v.lval); exits(nil); }
Output
p₀ = (3,-1) radius = 12 p₁ = (3,-1) p₂ = (-3,1) value = 3.140000 d = 7fffffffeed0 f = 7fffffffeed0 l = 7fffffffeed0
Limbo
Nope.
Go
package main import ( "fmt" ) type Point struct { x int y int } type Circle struct { Point radius uint } func mirror(p Point) Point { return Point{-1*p.x, -1*p.y} } func (p *Point) mirror() { p.x *= -1 p.y *= -1 } func main() { p := Point{x: 3, y: -1} c := Circle{p, 12} p2 := c fmt.Println(p) fmt.Println(c) fmt.Println(p2) p3 := mirror(Point{c.x, c.y}) fmt.Println(p3) fmt.Println(c.Point, c.Point.x, c.Point.y) c.mirror() fmt.Println(c) }
Output
{3 -1} {{3 -1} 12} {{3 -1} 12} {-3 1} {3 -1} 3 -1 {{-3 1} 12}
Multiple returns
C in particular is an offender of not enabling returns without a named type ( struct
) or allocated memory in place to facilitate multiple values being returned to a caller.
Go and some of the languages that influence it attempt to solve this issue while maintaining fairly C-like semantics.
Newsqueak
Nope.
Alef
This example is derived from the tuple example found in the “Alef User’s Guide” .
#include <alef.h> tuple(int, byte*, byte*) foo() { return (7, "fußbol", "skål"); } void main(void) { int n; byte *game, *cheer; (n, game, cheer) = foo(); print("(%d %s %s)\n", n, game, cheer); }
Output
(7 fußbol skål)
Plan9 C
Nope.
Limbo
implement MultRet; include "sys.m"; sys: Sys; include "draw.m"; MultRet: module { init: fn(nil: ref Draw->Context, nil: list of string); }; swap(a, b: int): (int, int) { return (b, a); } init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; (x, y) := swap(3, 7); sys->print("3, 7 → %d, %d\n", x, y); exit; }
Output
3, 7 → 7, 3
Go
package main import ( "fmt" ) func foo()(n int, s string) { n = 4 s = "☺" return } func bar() (a, b, c string) { return "a", "b", "c" } func main() { n, s := foo() a, b, c := bar() fmt.Println(n, s, a, b, c) }
Output
4 ☺ a b c
Break and continue overloading
Go and some of its predecessors support some form of overloading on top of break/continue to allow more precise flow control in nested iterative/branching logic.
Newsqueak
Nope.
Alef
This example is derived from an example in the “Alef User’s Guide” .
Note that there is no overloaded form of continue
.
#include <alef.h> void main(void) { chan(int)[1] dummy; chan(int)[2] ch; int a; alloc ch, dummy; dummy <-= 1; ch <-= 3; ch <-= 4; while(?ch) alt { case a = <-ch; print("got %d\n", a); break 2; case <- dummy; print("dummy\n"); dummy <-= 1; break; } }
Output
dummy dummy dummy dummy got 3
Plan9 C
Nope.
Limbo
implement BreakContinueTag; include "sys.m"; sys: Sys; include "draw.m"; BreakContinueTag: module { init: fn(nil: ref Draw->Context, nil: list of string); }; init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; i := 0; loop: for(;;){ i++; case i { 11 => break loop; * => if(i % 2 == 0) continue loop; } sys->print("%d\n", i); } exit; }
Output
Go
package main import ( "fmt" ) func main() { i := 0 loop: for { i++ switch { case i % 2 == 0: continue loop case i > 10: break loop } fmt.Println(i) } }
Output
Lists / iteration
This section demonstrates syntactic properties regarding lists or list-like iterative logic.
Newsqueak
Nope.
Alef
This example is derived from the iterator example in the “Alef User’s Guide” .
#include <alef.h> void main(void) { int i, arr[10]; arr[i = 0::10] = i * i; print("%d ", arr[0::10]); print("\n"); }
Output
Plan9 C
Nope.
Limbo
This is a modified version of the ‘Lists’ example in LimboByExample.
implement Lists; include "sys.m"; sys: Sys; print: import sys; include "draw.m"; Lists: module { init: fn(nil: ref Draw->Context, nil: list of string); }; init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; names: list of string; ages: list of int; persons: list of (string, int); print("Lens: %d, %d, %d\n", len names, len ages, len persons); names = "Spike" :: names; ages = 27 :: ages; names = "Ed" :: "Jet" :: names; ages = 13 :: 36 :: ages; print("Lens: %d, %d, %d\n", len names, len ages, len persons); n := names; a := ages; while(n != nil && a != nil) { persons = (hd n, hd a) :: persons; n = tl n; a = tl a; } print("Persons:\n"); for(; persons != nil; persons = tl persons) { (name, age) := hd persons; print("\t%s: %d\n", name, age); } print("Tmp lens: %d, %d\n", len n, len a); print("Lens: %d, %d, %d\n", len names, len ages, len persons); exit; }
Output
Lens: 0, 0, 0 Lens: 3, 3, 0 Persons: Spike: 27 Jet: 36 Ed: 13 Tmp lens: 0, 0 Lens: 3, 3, 0
Go
package main import ( "fmt" ) func main() { nums := make([]int, 0, 10) fmt.Printf("Length = %d\nCapacity = %d\n", len(nums), cap(nums)) nums = append(nums, 1) nums = append(nums, 2, 3, 4) for i, n := range nums { fmt.Printf("%d: %d\n", i, n) } fmt.Printf("Length = %d\nCapacity = %d\n", len(nums), cap(nums)) }
Output
Length = 0 Capacity = 10 0: 1 1: 2 2: 3 3: 4 Length = 4 Capacity = 10
Modules / packages / separable compilation
The idea of separating source across files is fairly universal in modern programming languages. The semantics of this process is demonstrated below for Go and some of its predecessors.
Newsqueak
Newsqueak can include files as text. This text can be assigned to a value or otherwise is inserted into the calling file as text and potentially interpreted as source.
include "util.nq"; print("Hello "); smiley();
smiley := prog() { smile := include "smile"; print(smile); }; ;
"☺"
Output
Hello ☺
Alef
Alef does not differ significantly from Plan9 C and this example is omitted.
Alef uses headers on its own, but does not allow the inclusion of C header files.
Related reading: alef(1)
Plan9 C
There are several compiler features which show themselves in the header: #pragma src
and #pragma lib
. Further reading on these can be found in the ‘Plan 9 C Compilers’
paper.
Note that these #pragma
directives typically are found with full system paths provided.
#include <u.h> #include <libc.h> #include "./libutil/util.h" void main(int, char*[]) { print("Hello "); smiley(); exits(nil); }
#pragma src "./libutil" #pragma lib "./libutil/libutil.a" void smiley(void);
#include <u.h> #include <libc.h> #include "util.h" void smiley(void) { print("☺\n"); }
</$objtype/mkfile BIN = ./ TARG = modules-example OFILES = main.$O CFLAGS = $CFLAGS -I ./libutil </sys/src/cmd/mkone
</$objtype/mkfile LIB = ./libutil.a HFILES = util.h OFILES = util.$O </sys/src/cmd/mklib
Output
For this example, to build and run from 9front you’ll use mk(1) :
tenshi% lc libutil/ main.c mkfile tenshi% cd libutil tenshi% mk ./libutil.a doesn't exist: assuming it will be an archive 6c -FTVw util.c ar vu ./libutil.a util.6 ar: creating ./libutil.a a - util.6 tenshi% cd .. tenshi% mk 6c -FTVw -I ./libutil main.c 6l -o 6.out main.6 tenshi% 6.out Hello ☺ tenshi%
Limbo
This is a slightly reduced version of the ‘Modules’ example in LimboByExample.
implement Modules; include "sys.m"; include "draw.m"; # Note the lack of `include "persons.m";` include "towns.m"; sys: Sys; print: import sys; persons: Persons; Person: import persons; towns: Towns; Town: import towns; Modules: module { init: fn(nil: ref Draw->Context, nil: list of string); }; init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; persons = load Persons "./persons.dis"; towns = load Towns "./towns.dis"; persons->init(); towns->init(); print("%d\n", persons->getpop()); print("%d\n", towns->persons->getpop()); p := persons->mkperson(); p.name = "Spike"; p.age = 27; print("%d\n", persons->getpop()); print("%d\n", towns->persons->getpop()); t := towns->mktown(); t.pop = array[] of {p, ref Person(13, "Ed")}; t.name = "Mars"; print("%s\n", t.stringify()); exit; }
implement Persons; include "persons.m"; population: int; init() { population = 0; } getpop(): int { return population; } mkperson(): ref Person { population++; return ref Person; } Person.stringify(p: self ref Person): string { return p.name; }
include "persons.m"; Towns: module { init: fn(); mktown: fn(): ref Town; persons: Persons; Town: adt { pop: array of ref Persons->Person; name: string; stringify: fn(t: self ref Town): string; }; };
implement Towns; include "towns.m"; init() { persons = load Persons "./persons.dis"; } mktown(): ref Town { return ref Town; } Town.stringify(t: self ref Town): string { Person: import persons; s := "Name: " + t.name + "\nSize: " + string len t.pop + "\nMembers:"; for(i := 0; i < len t.pop; i++) s += "\n→ " + t.pop[i].stringify(); return s; }
</mkconfig DISBIN = ./ TARG=\ modules.dis\ persons.dis\ towns.dis\ </mkfiles/mkdis
Output
For this example, to build and run from Inferno you’ll use mk(1) :
; mk limbo -I/module -gw modules.b limbo -I/module -gw persons.b limbo -I/module -gw towns.b ; modules 0 0 1 0 Name: Mars Size: 2 Members: → Spike → Ed ;
Go
This example just shows including a local package.
Modern Go recommends using the module systemand most public Go projects will have import paths in forms such as "github.com/foo/bar"
.
package main import ( util "./util" "fmt" ) func main() { fmt.Print("Hello ") util.Smile() }
package util import ( "fmt" ) func Smile() { fmt.Println("☺") }
Output
Hello ☺
References
以上所述就是小编给大家介绍的《Go's History in Code》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
结构化计算机组成
Andrew S.Tanenbaum / 刘卫东 / 机械工业出版社 / 2001-10-1 / 46.00
AndrewcS.Tanenbaum获得过美国麻省理工学院的理学学士学位和加利福尼亚大学伯克利分校的哲学博士学位,目前是荷兰阿姆斯特丹Vrije大学计算机科学系的教授,并领导着一个计算机系统的研究小组.同时,他还是一所计算与图像处理学院的院长,这是由几所大学合作成立的研究生院.尽管社会工作很多,但他并没有中断学术研究. 多年来,他在编译技术.操作系统.网络及局域分布式系统方面进行了大量的一起来看看 《结构化计算机组成》 这本书的介绍吧!