mirror of
https://github.com/golang/go
synced 2024-11-21 16:34:42 -07:00
doc/talks: remove talks, refer to talks.golang.org instead
These talks have been moved to the go.talks sub-repository: https://code.google.com/p/go.talks R=rsc, r CC=gobot, golang-dev https://golang.org/cl/6529052
This commit is contained in:
parent
413fbed341
commit
439183eb0a
@ -164,7 +164,7 @@ A panel discussion with David Symonds, Robert Griesemer, Rob Pike, Ken Thompson,
|
|||||||
A talk by Rob Pike and Andrew Gerrand presented at Google I/O 2011.
|
A talk by Rob Pike and Andrew Gerrand presented at Google I/O 2011.
|
||||||
It walks through the construction and deployment of a simple web application
|
It walks through the construction and deployment of a simple web application
|
||||||
and unveils the <a href="http://blog.golang.org/2011/05/go-and-google-app-engine.html">Go runtime for App Engine</a>.
|
and unveils the <a href="http://blog.golang.org/2011/05/go-and-google-app-engine.html">Go runtime for App Engine</a>.
|
||||||
See the <a href="/doc/talks/io2011/Writing_Web_Apps_in_Go.pdf">presentation slides</a>.
|
See the <a href="http://talks.golang.org/2011/Writing_Web_Apps_in_Go.pdf">presentation slides</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 id="go_programming"><a href="http://www.youtube.com/watch?v=jgVhBThJdXc">Go Programming</a><font color="red">*</font></h3>
|
<h3 id="go_programming"><a href="http://www.youtube.com/watch?v=jgVhBThJdXc">Go Programming</a><font color="red">*</font></h3>
|
||||||
|
Binary file not shown.
@ -1,411 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Go (January 12, 2010)</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta name="font-size-adjustment" content="-1" />
|
|
||||||
<link rel="stylesheet" href="slidy.css"
|
|
||||||
type="text/css" media="screen, projection, print" />
|
|
||||||
<script src="slidy.js" type="text/javascript">
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- this defines the slide background -->
|
|
||||||
|
|
||||||
<div class="background">
|
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<!-- sized and colored via CSS -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide titlepage">
|
|
||||||
<div style="height: 135px; width: 480px; overflow: hidden; position: fixed; top: auto; bottom: 10px; left: auto; right: 0; ">
|
|
||||||
<img src="../gordon/bumper480x270.png" style="margin: -135px 0 0 0;"/>
|
|
||||||
</div>
|
|
||||||
<!-- <img src="google.png" style="position: fixed; top: auto; bottom: 30px; left: 20px; right: auto;"/> -->
|
|
||||||
<br/>
|
|
||||||
<img src="../go-logo-white.png">
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<h1 style="padding-right: 0pt; margin-right: 0pt; color: #0066cc; font-size: 250%; border-bottom: 0px;">The Go Programming Language</h1>
|
|
||||||
<div style="color: #ffcc00;">
|
|
||||||
<h2>Russ Cox</h2>
|
|
||||||
<!-- <h3><i>rsc@google.com</i></h3> -->
|
|
||||||
<br/>
|
|
||||||
<h3>Stanford University<br/><br/>January 12, 2010</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go</h1>
|
|
||||||
|
|
||||||
<h2>New</h2>
|
|
||||||
<h2>Experimental</h2>
|
|
||||||
<h2>Concurrent</h2>
|
|
||||||
<h2>Garbage-collected</h2>
|
|
||||||
<h2>Systems</h2>
|
|
||||||
<h2>Language</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Hello, world</h1>
|
|
||||||
<pre>
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Printf("Hello, 世界\n")
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>History</h1>
|
|
||||||
|
|
||||||
<h2>Design started in late 2007.</h2>
|
|
||||||
<h2>Implementation starting to work mid-2008.</h2>
|
|
||||||
<h2>Released as an open source project in November 2009.</h2>
|
|
||||||
<h2>Work continues.<h2>
|
|
||||||
<h2>Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, Russ Cox, many others</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Why?</h1>
|
|
||||||
|
|
||||||
<h2>Go fast!</h2>
|
|
||||||
<h2>Make programming fun again.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Why isn't programming fun?</h1>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<h2>Compiled, statically-typed languages (C, C++, Java) require too much typing and too much typing:</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>verbose, lots of repetition</li>
|
|
||||||
<li>too much focus on type hierarchy</li>
|
|
||||||
<li>types get in the way as much as they help</li>
|
|
||||||
<li>compiles take far too long</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<h2>Dynamic languages (Python, JavaScript) fix these problems (no more types, no more compiler) but introduce others:</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>errors at run time that should be caught statically</li>
|
|
||||||
<li>no compilation means slow code</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="incremental">Can we combine the best of both?</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go</h1>
|
|
||||||
|
|
||||||
<h2>Make the language fast.</h2>
|
|
||||||
<h2>Make the tools fast.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Static Types</h1>
|
|
||||||
|
|
||||||
<h2>Static types, but declarations can infer type from expression:</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
var one, hi = 1, "hello"
|
|
||||||
|
|
||||||
var double = func(x int) int { return x*2 }
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Not full Hindley-Milner type inference.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Methods</h1>
|
|
||||||
|
|
||||||
<h2>Methods can be defined on any type.</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
type Point struct {
|
|
||||||
X, Y float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Point) Abs() float64 {
|
|
||||||
return math.Sqrt(p.X*p.X + p.Y*p.Y)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Methods</h1>
|
|
||||||
|
|
||||||
<h2>Methods can be defined on any type.</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
type MyFloat float64
|
|
||||||
|
|
||||||
func (f MyFloat) Abs() float64 {
|
|
||||||
v := float64(f)
|
|
||||||
if v < 0 {
|
|
||||||
v = -v
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Abstract Types</h1>
|
|
||||||
|
|
||||||
<h2>An interface type lists a set of methods. Any value with those methods satisfies the interface.</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
type Abser interface {
|
|
||||||
Abs() float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func AbsPrinter(a Abser)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Can use Point or MyFloat (or ...):</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
p := Point{3, 4}
|
|
||||||
AbsPrinter(p)
|
|
||||||
|
|
||||||
f := MyFloat(-10)
|
|
||||||
AbsPrinter(f)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Notice that Point never declared that it implements Abser. It just does. Same with MyFloat.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Packages</h1>
|
|
||||||
|
|
||||||
<h2>A Go program comprises one or more packages.</h2>
|
|
||||||
<h2>Each package is one or more source files compiled and imported as a unit.</h2>
|
|
||||||
<pre>
|
|
||||||
package draw
|
|
||||||
|
|
||||||
type Point struct {
|
|
||||||
X, Y int
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "draw"
|
|
||||||
|
|
||||||
var p draw.Point
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Visibility</h1>
|
|
||||||
|
|
||||||
<h2>Inside a package, all locally defined names are visible in all source files.</h2>
|
|
||||||
|
|
||||||
<h2>When imported, only the upper case names are visible.</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
package draw
|
|
||||||
|
|
||||||
type <span style="color: black;">Point</span> struct {
|
|
||||||
<span style="color: black;">X</span>, <span style="color: black;">Y</span> int
|
|
||||||
dist float64
|
|
||||||
}
|
|
||||||
|
|
||||||
type cache map[Point] float64
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Clients that <code>import "draw"</code> can use the black names only.</h2>
|
|
||||||
|
|
||||||
<h2>“Shift is the new <code>public</code>.”</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Concurrency</h1>
|
|
||||||
|
|
||||||
<h2>Cheap to create a new flow of control (goroutine):</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func main() {
|
|
||||||
go expensiveComputation(x, y, z)
|
|
||||||
anotherExpensiveComputation(a, b, c)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Two expensive computations in parallel.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Approach: Synchronization</h1>
|
|
||||||
|
|
||||||
<h2>Use explicit messages to communicate and synchronize.</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func computeAndSend(ch chan int, x, y, z int) {
|
|
||||||
ch <- expensiveComputation(x, y, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ch := make(chan int)
|
|
||||||
go computeAndSend(ch, x, y, z)
|
|
||||||
v2 := anotherExpensiveComputation(a, b, c)
|
|
||||||
v1 := <-ch
|
|
||||||
fmt.Println(v1, v2)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Notice communication of result in addition to synchronization.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Fast: Language</h1>
|
|
||||||
|
|
||||||
<h2 class="incremental">Static types: enough to compile well, but inferred much of the time.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Methods: on any type, orthogonal to type system.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Abstract types: interface values, relations inferred statically.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Visibility: inferred from case of name.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Concurrency: lightweight way to start new thread of control.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Synchronization: explicit, easy message passing.</h2>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<h2 class="incremental">Lightweight feel of a scripting language but compiled.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Compile fast</h1>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<h2>Observation: much of the compile time for a source file is spent processing
|
|
||||||
other, often unrelated files.</h2>
|
|
||||||
|
|
||||||
<h2>In C: <code>a.c</code> includes <code>b.h</code>, which includes <code>c.h</code>, which includes <code>d.h</code>.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<h2>Except that it's more often a tree instead of a chain.</h2>
|
|
||||||
|
|
||||||
<h2>On my Mac (OS X 10.5.8, gcc 4.0.1):</h2>
|
|
||||||
<ul>
|
|
||||||
<li>C: <code>#include <stdio.h></code> reads 360 lines from 9 files.
|
|
||||||
<li>C++: <code>#include <iostream></code> reads 25,326 lines from 131 files.
|
|
||||||
<li>Objective C: <code>#include <Carbon/Carbon.h></code> reads 124,730 lines from 689 files.
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>And we haven't done any real work yet!</h2>
|
|
||||||
|
|
||||||
<h2>Same story in Java, Python, but reading binaries instead of source files.</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Implementation: Summarize Dependencies</h1>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
package gui
|
|
||||||
|
|
||||||
import "draw"
|
|
||||||
|
|
||||||
type Mouse struct {
|
|
||||||
Loc draw.Point
|
|
||||||
Buttons uint
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Compiled form of <code>gui</code> summarizes the necessary part of <code>draw</code> (just <code>Point</code>).</h2>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Implementation: Summarize Dependencies</h1>
|
|
||||||
|
|
||||||
<h2>Compiled form of <code>gui</code> summarizes the necessary part of <code>draw</code> (just <code>Point</code>). Pseudo-object:</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
package gui
|
|
||||||
type draw.Point struct {
|
|
||||||
X, Y int
|
|
||||||
}
|
|
||||||
type gui.Mouse struct {
|
|
||||||
Loc draw.Point
|
|
||||||
Buttons uint
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>A file that imports <code>gui</code> compiles without consulting <code>draw</code> or its dependencies.</h2>
|
|
||||||
|
|
||||||
<h2>In Go: <code>import "fmt"</code> reads <i>one</i> file: 184 lines summarizing types from 7 packages.</h2>
|
|
||||||
|
|
||||||
<h2>Tiny effect in this program but can be exponential in large programs.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Compilation Demo</h1>
|
|
||||||
|
|
||||||
<h2>Build all standard Go packages: ~120,000 lines of code.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Status</h1>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<div>
|
|
||||||
<h2>Open source:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>released on November 10, 2009
|
|
||||||
<li>regular releases (~ weekly)
|
|
||||||
<li>all development done in public Mercurial repository
|
|
||||||
<li>outside contributions welcome
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h2>Portable:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>FreeBSD, Linux, OS X (x86, x86-64)
|
|
||||||
<li>(in progress) Linux arm, Native Client x86, Windows x86.
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h2>Still in progress, experimental. Yet to come:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>mature garbage collector
|
|
||||||
<li>generics?
|
|
||||||
<li>exceptions?
|
|
||||||
<li>unions or sum types?
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide titlepage">
|
|
||||||
<h1>Questions?</h1>
|
|
||||||
<br><br>
|
|
||||||
<center>
|
|
||||||
<img src="../gordon/bumper640x360.png">
|
|
||||||
</center>
|
|
||||||
<br><br>
|
|
||||||
<div style="color: #ffcc00;">
|
|
||||||
<!-- <h3><i>rsc@google.com</i></h3> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body></html>
|
|
@ -1,453 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Go, Networked (January 21, 2010)</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta name="font-size-adjustment" content="-1" />
|
|
||||||
<link rel="stylesheet" href="slidy.css"
|
|
||||||
type="text/css" media="screen, projection, print" />
|
|
||||||
<script src="slidy.js" type="text/javascript">
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- this defines the slide background -->
|
|
||||||
|
|
||||||
<div class="background">
|
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<!-- sized and colored via CSS -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide titlepage">
|
|
||||||
<div style="height: 135px; width: 480px; overflow: hidden; position: fixed; top: auto; bottom: 10px; left: auto; right: 0; ">
|
|
||||||
<img src="../gordon/bumper480x270.png" style="margin: -135px 0 0 0;"/>
|
|
||||||
</div>
|
|
||||||
<!-- <img src="../google.png" style="position: fixed; top: auto; bottom: 30px; left: 20px; right: auto;"/> -->
|
|
||||||
<br/>
|
|
||||||
<img src="../go-logo-white.png">
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<h1 style="padding-right: 0pt; margin-right: 0pt; color: #0066cc; font-size: 250%; border-bottom: 0px;">The Go Programming Language</h1>
|
|
||||||
<div style="color: #ffcc00;">
|
|
||||||
<h2>Russ Cox</h2>
|
|
||||||
<!-- <h3><i>rsc@google.com</i></h3> -->
|
|
||||||
<br/>
|
|
||||||
<h3>CNS Winter Research Review<br/><br/>January 21, 2010</h3>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<!--
|
|
||||||
<h4><i>click to start; then left/right arrow to change slides</i></h4> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go</h1>
|
|
||||||
|
|
||||||
<h2>New</h2>
|
|
||||||
<h2>Experimental</h2>
|
|
||||||
<h2>Concurrent</h2>
|
|
||||||
<h2>Garbage-collected</h2>
|
|
||||||
<h2>Systems</h2>
|
|
||||||
<h2>Language</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Hello, world</h1>
|
|
||||||
<pre>
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Printf("Hello, 世界\n")
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>History</h1>
|
|
||||||
|
|
||||||
<h2>Design started in late 2007.</h2>
|
|
||||||
<h2>Implementation starting to work mid-2008.</h2>
|
|
||||||
<h2>Released as an open source project in November 2009.</h2>
|
|
||||||
<h2>Work continues.<h2>
|
|
||||||
<h2>Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, Russ Cox, many others</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Goals and Motivation</h1>
|
|
||||||
|
|
||||||
<h2>Go fast!</h2>
|
|
||||||
<h2>Make programming fun again.</h2>
|
|
||||||
<h2>Targeted at systems software, broadly.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Why isn't programming fun?</h1>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<h2>Compiled, statically-typed languages (C, C++, Java) require too much typing and too much typing:</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>verbose, lots of repetition</li>
|
|
||||||
<li>too much focus on type hierarchy</li>
|
|
||||||
<li>types get in the way as much as they help</li>
|
|
||||||
<li>compiles take far too long</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<h2>Dynamic languages (Python, JavaScript) fix these problems (no more types, no more compiler) but introduce others:</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>errors at run time that should be caught statically</li>
|
|
||||||
<li>no compilation means slow code</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="incremental">Can we combine the best of both?</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Why a new language?</h1>
|
|
||||||
|
|
||||||
<div class="incremental">
|
|
||||||
<h2>No new systems language in 10+ years.</h2>
|
|
||||||
<h2>Current languages designed before ...</h2>
|
|
||||||
<h3>... rise of large-scale, networked and multicore computing</h3>
|
|
||||||
<h3>... rise of Internet-scale distributed development (many libraries)</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go</h1>
|
|
||||||
|
|
||||||
<h2>Make the language fast.</h2>
|
|
||||||
<h2>Make the tools fast.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Compilation Demo</h1>
|
|
||||||
|
|
||||||
<h2>Build all standard Go packages: ~120,000 lines of code.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go in one slide</h1>
|
|
||||||
|
|
||||||
<h2 class="incremental">Lightweight syntax.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Static types: enough to compile well, but inferred much of the time.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Methods: on any type, orthogonal to type system.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Abstract types: interface values, relations inferred statically.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Visibility: inferred from case of name.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">First-class functions.</h2>
|
|
||||||
|
|
||||||
<h2 class="incremental">Garbage collection.</h2>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<h2 class="incremental">Lightweight feel of a scripting language but compiled.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, concurrently</h1>
|
|
||||||
|
|
||||||
<h2>Cheap to create a new flow of control (goroutine):</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func main() {
|
|
||||||
go expensiveComputation(x, y, z)
|
|
||||||
anotherExpensiveComputation(a, b, c)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Two expensive computations in parallel.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, concurrently</h1>
|
|
||||||
|
|
||||||
<h2>Cheap to create a new flow of control (goroutine):</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
for {
|
|
||||||
rw := l.Accept()
|
|
||||||
conn := newConn(rw, handler)
|
|
||||||
go conn.serve()
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Concurrent web server.</h2>
|
|
||||||
<h2>Network connections multiplexed onto epoll.</h2>
|
|
||||||
<ul>
|
|
||||||
<li>many blocked Read calls != many blocked OS threads</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>Use explicit messages to communicate and synchronize.</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func computeAndSend(ch chan int, x, y, z int) {
|
|
||||||
ch <- expensiveComputation(x, y, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ch := make(chan int)
|
|
||||||
go computeAndSend(ch, x, y, z)
|
|
||||||
v2 := anotherExpensiveComputation(a, b, c)
|
|
||||||
v1 := <-ch
|
|
||||||
fmt.Println(v1, v2)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Notice communication of result in addition to synchronization.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) Call(method string, args, reply interface{}) os.Error {
|
|
||||||
// Send RPC message.
|
|
||||||
call := client.Go(method, args, reply, nil)
|
|
||||||
|
|
||||||
// Read reply from Done channel.
|
|
||||||
<-call.Done
|
|
||||||
|
|
||||||
return call.Error
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client demux</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) input() {
|
|
||||||
for {
|
|
||||||
resp := client.readResponse()
|
|
||||||
client.mutex.Lock()
|
|
||||||
c := client.pending[resp.Seq]
|
|
||||||
client.pending[resp.Seq] = c, false
|
|
||||||
client.mutex.Unlock()
|
|
||||||
if resp.Error != "" {
|
|
||||||
c.Error = os.ErrorString(resp.error)
|
|
||||||
}
|
|
||||||
resp.Decode(c.Reply)
|
|
||||||
c.Done <- c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client demux</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) input() {
|
|
||||||
for {
|
|
||||||
<font style="color: black;">resp := client.readResponse()</font>
|
|
||||||
client.mutex.Lock()
|
|
||||||
c := client.pending[resp.Seq]
|
|
||||||
client.pending[resp.Seq] = c, false
|
|
||||||
client.mutex.Unlock()
|
|
||||||
if resp.Error != "" {
|
|
||||||
c.Error = os.ErrorString(resp.error)
|
|
||||||
}
|
|
||||||
resp.Decode(c.Reply)
|
|
||||||
c.Done <- c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Read response from network.</h2
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client demux</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) input() {
|
|
||||||
for {
|
|
||||||
resp := client.readResponse()
|
|
||||||
<font style="color: black;">client.mutex.Lock()
|
|
||||||
c := client.pending[resp.Seq]
|
|
||||||
client.pending[resp.Seq] = c, false
|
|
||||||
client.mutex.Unlock()</font>
|
|
||||||
if resp.Error != "" {
|
|
||||||
c.Error = os.ErrorString(resp.error)
|
|
||||||
}
|
|
||||||
resp.Decode(c.Reply)
|
|
||||||
c.Done <- c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Look up request by sequence number.</h2
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client demux</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) input() {
|
|
||||||
for {
|
|
||||||
resp := client.readResponse()
|
|
||||||
client.mutex.Lock()
|
|
||||||
c := client.pending[resp.Seq]
|
|
||||||
client.pending[resp.Seq] = c, false
|
|
||||||
client.mutex.Unlock()
|
|
||||||
<font style="color: black;">if resp.Error != "" {
|
|
||||||
c.Error = os.ErrorString(resp.error)
|
|
||||||
}
|
|
||||||
resp.Decode(c.Reply)</font>
|
|
||||||
c.Done <- c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Decode response fields from payload.</h2
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client demux</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) input() {
|
|
||||||
for {
|
|
||||||
resp := client.readResponse()
|
|
||||||
client.mutex.Lock()
|
|
||||||
c := client.pending[resp.Seq]
|
|
||||||
client.pending[resp.Seq] = c, false
|
|
||||||
client.mutex.Unlock()
|
|
||||||
if resp.Error != "" {
|
|
||||||
c.Error = os.ErrorString(resp.error)
|
|
||||||
}
|
|
||||||
resp.Decode(c.Reply)
|
|
||||||
<font style="color: black;">c.Done <- c</font>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
<h2>Tell client that it finished.</h2
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go, synchronized</h1>
|
|
||||||
|
|
||||||
<h2>RPC client demux</h2>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func (client *Client) input() {
|
|
||||||
for {
|
|
||||||
resp := client.readResponse()
|
|
||||||
client.mutex.Lock()
|
|
||||||
c := client.pending[resp.Seq]
|
|
||||||
client.pending[resp.Seq] = c, false
|
|
||||||
client.mutex.Unlock()
|
|
||||||
if resp.Error != "" {
|
|
||||||
c.Error = os.ErrorString(resp.error)
|
|
||||||
}
|
|
||||||
resp.Decode(c.Reply)
|
|
||||||
c.Done <- c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Can create multiple Calls with same Done channel
|
|
||||||
and distinguish which finished by inspecting value sent on channel.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Goroutine demo</h1>
|
|
||||||
|
|
||||||
<h2>Chain together 100,000 goroutines connected by 100,001 channels.</h2>
|
|
||||||
|
|
||||||
<h2>Send a value to one end of the chain.</h2>
|
|
||||||
|
|
||||||
<h2>Each passes it along, increments.</h2>
|
|
||||||
|
|
||||||
<h2>Receive value out the other end of the chain.</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Status</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Status</h1>
|
|
||||||
|
|
||||||
<h2>Open source:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>released on November 10, 2009
|
|
||||||
<li>regular releases (~ weekly)
|
|
||||||
<li>all development done in public Mercurial repository
|
|
||||||
<li>outside contributions welcome
|
|
||||||
<li>two independent compiler implementations
|
|
||||||
<li>XML, JSON, HTTP, TLS/SSL, native RPC, (network channels,) ...
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Status</h1>
|
|
||||||
|
|
||||||
<h2>Open source</h2>
|
|
||||||
|
|
||||||
<h2>Portable:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>FreeBSD, Linux, OS X (x86, x86-64)
|
|
||||||
<li>(in progress) Linux arm, Native Client x86, Windows x86.
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go Status</h1>
|
|
||||||
|
|
||||||
<h2>Open source</h2>
|
|
||||||
<h2>Portable</h2>
|
|
||||||
|
|
||||||
<h2>Still in progress, experimental. Yet to come:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>production garbage collector
|
|
||||||
<li>generics?
|
|
||||||
<li>exceptions?
|
|
||||||
<li>unions or sum types?
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide titlepage">
|
|
||||||
<h1>Questions?</h1>
|
|
||||||
<br><br>
|
|
||||||
<center>
|
|
||||||
<img src="../gordon/bumper640x360.png">
|
|
||||||
</center>
|
|
||||||
<br><br>
|
|
||||||
<div style="color: #ffcc00;">
|
|
||||||
<!-- <h3><i>rsc@google.com</i></h3> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body></html>
|
|
@ -1,395 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Go Tech Talk</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta name="font-size-adjustment" content="-1" />
|
|
||||||
<link rel="stylesheet" href="slidy.css"
|
|
||||||
type="text/css" media="screen, projection, print" />
|
|
||||||
<script src="slidy.js" type="text/javascript">
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- this defines the slide background -->
|
|
||||||
|
|
||||||
<div class="background">
|
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<!-- sized and colored via CSS -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide titlepage">
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<img src="../go-logo-white.png" width="588px" height="217px">
|
|
||||||
<br/>
|
|
||||||
<h1 style="padding-right: 0pt; margin-right: 0pt; color: #0066cc; font-size: 250%; border-bottom: 0px;">The Go Programming Language</h1>
|
|
||||||
<div style="color: #ffcc00;">
|
|
||||||
<br/>
|
|
||||||
<h3>Sydney University<br/><br/>March 23, 2010</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go</h1>
|
|
||||||
|
|
||||||
<h2>New</h2>
|
|
||||||
<h2>Experimental</h2>
|
|
||||||
<h2>Concurrent</h2>
|
|
||||||
<h2>Garbage Collected</h2>
|
|
||||||
<h2>Systems Language</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Hello, world</h1>
|
|
||||||
<pre>
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Printf("Hello, 世界\n")
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Hello, world 2.0</h1>
|
|
||||||
|
|
||||||
<h2>Serving <a href="http://localhost:8080/world">http://localhost:8080/world</a></h2>
|
|
||||||
<pre>
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handler(c *http.Conn, r *http.Request) {
|
|
||||||
fmt.Fprintf(c, "Hello, %s.", r.URL.Path[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
http.ListenAndServe(":8080",
|
|
||||||
http.HandlerFunc(handler))
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>New</h1>
|
|
||||||
|
|
||||||
<h2>It's about two years old:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Design started in late 2007</li>
|
|
||||||
<li>Implementation starting to work mid-2008</li>
|
|
||||||
<li>Released as an open source project in November 2009</li>
|
|
||||||
<li>Development continues with an active community</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Why invent a new language? Older languages weren't designed for concurrency, but modern software needs it:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Large scale, networked computing, such as Google web search</li>
|
|
||||||
<li>Multi-core hardware</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>New</h1>
|
|
||||||
|
|
||||||
<h2>Older languages are also frustrating on a day-to-day basis</h2>
|
|
||||||
<h2>Statically-typed languages (C, C++, Java) have issues:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Edit-Compile-Run cycle takes far too long</li>
|
|
||||||
<li>Type hierarchy can hurt as much as it helps</li>
|
|
||||||
</ul>
|
|
||||||
<div style="text-align:center">
|
|
||||||
<img src="java-typing.png" width="800px" height="90px"><br>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Dynamic languages (Python, JavaScript) fix some issues but introduce others:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>No compilation means slow code</li>
|
|
||||||
<li>Runtime errors that should be caught statically</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Go has the lighter feel of a scripting language but is compiled</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>New</h1>
|
|
||||||
|
|
||||||
<h2>Large C++ programs (e.g. Firefox, OpenOffice, Chromium) have enormous build times:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>XKCD's #1 Programmer Excuse for Legitimately Slacking Off: "<a href="http://xkcd.com/303/">My Code's Compiling</a>"</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>On a Mac (OS X 10.5.8, gcc 4.0.1):</h2>
|
|
||||||
<ul>
|
|
||||||
<li>C: <code>#include <stdio.h></code> reads 360 lines from 9 files</li>
|
|
||||||
<li>C++: <code>#include <iostream></code> reads 25,326 lines from 131 files</li>
|
|
||||||
<li>Objective-C: <code>#include <Carbon/Carbon.h></code> reads 124,730 lines from 689 files</li>
|
|
||||||
<li>We haven't done any real work yet!</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>In Go: <code>import "fmt"</code> reads <i>one</i> file: 184 lines summarizing 7 packages</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>New</h1>
|
|
||||||
|
|
||||||
<h2>Compilation demo</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Experimental</h1>
|
|
||||||
|
|
||||||
<h2>Go is still unproven</h2>
|
|
||||||
<h2>Language is still evolving</h2>
|
|
||||||
<h2>Package library is incomplete</h2>
|
|
||||||
<h2>Concurrent garbage collection is an active research problem</h2>
|
|
||||||
<h2>Reviving forgotten concepts:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Go's concurrency is strongly influenced by <i>Communicating Sequential Processes</i> (Hoare, 1978)</li>
|
|
||||||
<li>Go has types and interfaces, but no inheritance. It is arguably more object-oriented than previously mentioned languages, being closer to the original Smalltalk meaning (1970s)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Concurrent</h1>
|
|
||||||
|
|
||||||
<h2>Unix philosophy: write <i>programs</i> that do one thing and do it well</h2>
|
|
||||||
<h2>Connect them with <i>pipes</i>:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>How many lines of test code are there in the Go standard library?</li>
|
|
||||||
<li><code>find ~/go/src/pkg | grep _test.go$ | xargs wc -l</code></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Unlike other languages, Go makes it easy to:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Launch <i>goroutines</i></li>
|
|
||||||
<li>Connect them with <i>channels</i></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Concurrent</h1>
|
|
||||||
|
|
||||||
<h2>Start a new flow of control with the <code>go</code> keyword</h2>
|
|
||||||
<h2>Parallel computation is easy:</h2>
|
|
||||||
<pre>
|
|
||||||
func main() {
|
|
||||||
go expensiveComputation(x, y, z)
|
|
||||||
anotherExpensiveComputation(a, b, c)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>Roughly speaking, a goroutine is like a thread, but lighter weight:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Goroutines have segmented stacks, and typically smaller stacks</li>
|
|
||||||
<li>This requires compiler support. Goroutines can't just be a C++ library on top of a thread library</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Concurrent</h1>
|
|
||||||
|
|
||||||
<h2>Consider web servers ("the C10k problem"):</h2>
|
|
||||||
<ul>
|
|
||||||
<li>"Thread per connection" approach is conceptually neat, but doesn't scale well in practice</li>
|
|
||||||
<li>What does scale well (event-driven callbacks, asynchronous APIs) are harder to understand, maintain, and debug</li>
|
|
||||||
<li>We think "goroutine per connection" can scale well, and is conceptually neat</li>
|
|
||||||
</ul>
|
|
||||||
<pre>
|
|
||||||
for {
|
|
||||||
rw := socket.Accept()
|
|
||||||
conn := newConn(rw, handler)
|
|
||||||
go conn.serve()
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Concurrent</h1>
|
|
||||||
|
|
||||||
<h2>Let's look again at our simple parallel computation:</h2>
|
|
||||||
<pre>
|
|
||||||
func main() {
|
|
||||||
go expensiveComputation(x, y, z)
|
|
||||||
anotherExpensiveComputation(a, b, c)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>This story is incomplete:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>How do we know when the two computations are done?</li>
|
|
||||||
<li>What are their values?</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Concurrent</h1>
|
|
||||||
|
|
||||||
<h2>Goroutines communicate with other goroutines via channels</h2>
|
|
||||||
<pre>
|
|
||||||
func computeAndSend(ch chan int, x, y, z int) {
|
|
||||||
ch <- expensiveComputation(x, y, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ch := make(chan int)
|
|
||||||
go computeAndSend(ch, x, y, z)
|
|
||||||
v2 := anotherExpensiveComputation(a, b, c)
|
|
||||||
v1 := <-ch
|
|
||||||
fmt.Println(v1, v2)
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Concurrent</h1>
|
|
||||||
|
|
||||||
<h2>In traditional concurrent programs, you <i>communicate by sharing memory</i>. In Go, you <i>share memory by communicating</i>:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Communication (the <code><-</code> operator) is sharing and synchronization</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Threads and locks are concurrency primitives; CSP is a concurrency model:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Analogy: "Go To Statement Considered Harmful" (Dijsktra, 1968)</li>
|
|
||||||
<li><code>goto</code> is a control flow primitive; structured programming (<code>if</code> statements, <code>for</code> loops, function calls) is a control flow model</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Learning CSP changes the way you think about concurrent programming:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Every language has its grain. If your Go program uses mutexes, you're probably working against the grain</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Garbage Collected</h1>
|
|
||||||
|
|
||||||
<h2>Automatic memory management makes writing (and maintaining) programs easier</h2>
|
|
||||||
<h2>Especially in a concurrent world:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Who "owns" a shared piece of memory, and is responsible for destroying it?</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Large C++ programs usually end up with semi-automatic memory management anyway, via "smart pointers"</h2>
|
|
||||||
<h2>Mixing the two models can be problematic:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Browsers can leak memory easily; DOM elements are C++ objects, but JavaScript is garbage collected</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Garbage Collected</h1>
|
|
||||||
|
|
||||||
<h2>Go is also a safer language:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Pointers but no pointer arithmetic</li>
|
|
||||||
<li>No dangling pointers</li>
|
|
||||||
<li>Variables are zero-initialized</li>
|
|
||||||
<li>Array access is bounds-checked</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>No buffer overflow exploits</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Systems Language</h1>
|
|
||||||
|
|
||||||
<h2>This just means you could write decently large programs in Go:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Web servers</li>
|
|
||||||
<li>Web browsers</li>
|
|
||||||
<li>Web crawlers</li>
|
|
||||||
<li>Search indexers</li>
|
|
||||||
<li>Databases</li>
|
|
||||||
<li>Word processors</li>
|
|
||||||
<li>Integrated Development Environments (IDEs)</li>
|
|
||||||
<li>Operating systems</li>
|
|
||||||
<li>...</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Systems Language</h1>
|
|
||||||
|
|
||||||
<h2>Garbage collection has a reputation for being "slower"</h2>
|
|
||||||
<h2>We're expecting Go to be slightly slower than optimized C, but faster than Java, depending on the task. Nonetheless:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Fast and buggy is worse than almost-as-fast and correct</li>
|
|
||||||
<li>It is easier to optimize a correct program than to correct an optimized program</li>
|
|
||||||
<li>Fundamentally, it's simply a trade-off we're willing to make</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Memory layout can drastically affect performance. These two designs are equivalent in Go, but significantly different in Java:</h2>
|
|
||||||
<pre>
|
|
||||||
type Point struct { X, Y int }
|
|
||||||
type Rect struct { P0, P1 Point }
|
|
||||||
|
|
||||||
// or ...
|
|
||||||
|
|
||||||
type Rect struct { X0, Y0, X1, Y1 int }
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Systems Language</h1>
|
|
||||||
|
|
||||||
<h2>Quote from http://loadcode.blogspot.com/2009/12/go-vs-java.html</h2>
|
|
||||||
|
|
||||||
<h2>
|
|
||||||
"[Git] is known to be very fast. It is written in C. A Java version
|
|
||||||
JGit was made. It was considerably slower. Handling of memory and lack
|
|
||||||
of unsigned types was some of the important reasons.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<h2>Shawn O. Pearce wrote on the git mailinglist:</h2>
|
|
||||||
<ul><li>"JGit struggles with not
|
|
||||||
having an efficient way to represent a SHA-1. C can just say "unsigned
|
|
||||||
char[20]" and have it inline into the container's memory allocation. A
|
|
||||||
byte[20] in Java will cost an *additional* 16 bytes of memory, and be
|
|
||||||
slower to access because the bytes themselves are in a different area
|
|
||||||
of memory from the container object. We try to work around it by
|
|
||||||
converting from a byte[20] to 5 ints, but that costs us machine
|
|
||||||
instructions"
|
|
||||||
</li></ul>
|
|
||||||
|
|
||||||
<h2>
|
|
||||||
Like C, Go does allow unsigned types and defining data structures
|
|
||||||
containing other data structures as continuous blocks of memory."
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide">
|
|
||||||
<h1>Go</h1>
|
|
||||||
|
|
||||||
<h2>New</h2>
|
|
||||||
<h2>Experimental</h2>
|
|
||||||
<h2>Concurrent</h2>
|
|
||||||
<h2>Garbage Collected</h2>
|
|
||||||
<h2>Systems Language</h2>
|
|
||||||
|
|
||||||
<h2>And more:</h2>
|
|
||||||
<ul>
|
|
||||||
<li>I haven't talked about the type system, interfaces, slices, closures, selects, ...</li>
|
|
||||||
<li>Documentation, mailing list, source code all online</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="slide titlepage">
|
|
||||||
<h1>Questions?</h1>
|
|
||||||
<br><br>
|
|
||||||
<center>
|
|
||||||
<img src="../gordon/bumper640x360.png" width="640px" height="360px">
|
|
||||||
</center>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body></html>
|
|
Binary file not shown.
@ -1,168 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/heap"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const nRequester = 100
|
|
||||||
const nWorker = 10
|
|
||||||
|
|
||||||
var roundRobin = flag.Bool("r", false, "use round-robin scheduling")
|
|
||||||
|
|
||||||
// Simulation of some work: just sleep for a while and report how long.
|
|
||||||
func op() int {
|
|
||||||
n := rand.Int63n(1e9)
|
|
||||||
time.Sleep(nWorker * n)
|
|
||||||
return int(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
fn func() int
|
|
||||||
c chan int
|
|
||||||
}
|
|
||||||
|
|
||||||
func requester(work chan Request) {
|
|
||||||
c := make(chan int)
|
|
||||||
for {
|
|
||||||
time.Sleep(rand.Int63n(nWorker * 2e9))
|
|
||||||
work <- Request{op, c}
|
|
||||||
<-c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Worker struct {
|
|
||||||
i int
|
|
||||||
requests chan Request
|
|
||||||
pending int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Worker) work(done chan *Worker) {
|
|
||||||
for {
|
|
||||||
req := <-w.requests
|
|
||||||
req.c <- req.fn()
|
|
||||||
done <- w
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pool []*Worker
|
|
||||||
|
|
||||||
func (p Pool) Len() int { return len(p) }
|
|
||||||
|
|
||||||
func (p Pool) Less(i, j int) bool {
|
|
||||||
return p[i].pending < p[j].pending
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) Swap(i, j int) {
|
|
||||||
a := *p
|
|
||||||
a[i], a[j] = a[j], a[i]
|
|
||||||
a[i].i = i
|
|
||||||
a[j].i = j
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) Push(x interface{}) {
|
|
||||||
a := *p
|
|
||||||
n := len(a)
|
|
||||||
a = a[0 : n+1]
|
|
||||||
w := x.(*Worker)
|
|
||||||
a[n] = w
|
|
||||||
w.i = n
|
|
||||||
*p = a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) Pop() interface{} {
|
|
||||||
a := *p
|
|
||||||
*p = a[0 : len(a)-1]
|
|
||||||
w := a[len(a)-1]
|
|
||||||
w.i = -1 // for safety
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
type Balancer struct {
|
|
||||||
pool Pool
|
|
||||||
done chan *Worker
|
|
||||||
i int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBalancer() *Balancer {
|
|
||||||
done := make(chan *Worker, nWorker)
|
|
||||||
b := &Balancer{make(Pool, 0, nWorker), done, 0}
|
|
||||||
for i := 0; i < nWorker; i++ {
|
|
||||||
w := &Worker{requests: make(chan Request, nRequester)}
|
|
||||||
heap.Push(&b.pool, w)
|
|
||||||
go w.work(b.done)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Balancer) balance(work chan Request) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case req := <-work:
|
|
||||||
b.dispatch(req)
|
|
||||||
case w := <-b.done:
|
|
||||||
b.completed(w)
|
|
||||||
}
|
|
||||||
b.print()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Balancer) print() {
|
|
||||||
sum := 0
|
|
||||||
sumsq := 0
|
|
||||||
for _, w := range b.pool {
|
|
||||||
fmt.Printf("%d ", w.pending)
|
|
||||||
sum += w.pending
|
|
||||||
sumsq += w.pending * w.pending
|
|
||||||
}
|
|
||||||
avg := float64(sum) / float64(len(b.pool))
|
|
||||||
variance := float64(sumsq)/float64(len(b.pool)) - avg*avg
|
|
||||||
fmt.Printf(" %.2f %.2f\n", avg, variance)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Balancer) dispatch(req Request) {
|
|
||||||
if *roundRobin {
|
|
||||||
w := b.pool[b.i]
|
|
||||||
w.requests <- req
|
|
||||||
w.pending++
|
|
||||||
b.i++
|
|
||||||
if b.i >= len(b.pool) {
|
|
||||||
b.i = 0
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w := heap.Pop(&b.pool).(*Worker)
|
|
||||||
w.requests <- req
|
|
||||||
w.pending++
|
|
||||||
// fmt.Printf("started %p; now %d\n", w, w.pending)
|
|
||||||
heap.Push(&b.pool, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Balancer) completed(w *Worker) {
|
|
||||||
if *roundRobin {
|
|
||||||
w.pending--
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.pending--
|
|
||||||
// fmt.Printf("finished %p; now %d\n", w, w.pending)
|
|
||||||
heap.Remove(&b.pool, w.i)
|
|
||||||
heap.Push(&b.pool, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
work := make(chan Request)
|
|
||||||
for i := 0; i < nRequester; i++ {
|
|
||||||
go requester(work)
|
|
||||||
}
|
|
||||||
NewBalancer().balance(work)
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This code differs from the slides in that it handles errors.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"compress/gzip"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func EncryptAndGzip(dstfile, srcfile string, key, iv []byte) error {
|
|
||||||
r, err := os.Open(srcfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var w io.Writer
|
|
||||||
w, err = os.Create(dstfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w = cipher.StreamWriter{S: cipher.NewOFB(c, iv), W: w}
|
|
||||||
w2, err := gzip.NewWriter(w)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer w2.Close()
|
|
||||||
_, err = io.Copy(w2, r)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecryptAndGunzip(dstfile, srcfile string, key, iv []byte) error {
|
|
||||||
f, err := os.Open(srcfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
c, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r := cipher.StreamReader{S: cipher.NewOFB(c, iv), R: f}
|
|
||||||
r2, err := gzip.NewReader(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w, err := os.Create(dstfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
_, err = io.Copy(w, r2)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := EncryptAndGzip(
|
|
||||||
"/tmp/passwd.gz",
|
|
||||||
"/etc/passwd",
|
|
||||||
make([]byte, 16),
|
|
||||||
make([]byte, 16),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
err = DecryptAndGunzip(
|
|
||||||
"/dev/stdout",
|
|
||||||
"/tmp/passwd.gz",
|
|
||||||
make([]byte, 16),
|
|
||||||
make([]byte, 16),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This code differs from the slides in that it handles errors.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"compress/gzip"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func EncryptAndGzip(dstfile, srcfile string, key, iv []byte) error {
|
|
||||||
r, err := os.Open(srcfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var w io.WriteCloser
|
|
||||||
w, err = os.Create(dstfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
w, err = gzip.NewWriter(w)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
c, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(cipher.StreamWriter{S: cipher.NewOFB(c, iv), W: w}, r)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := EncryptAndGzip(
|
|
||||||
"/tmp/passwd.gz",
|
|
||||||
"/etc/passwd",
|
|
||||||
make([]byte, 16),
|
|
||||||
make([]byte, 16),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generic expression parser/evaluator
|
|
||||||
|
|
||||||
type Value interface {
|
|
||||||
String() string
|
|
||||||
BinaryOp(op string, y Value) Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type Parser struct {
|
|
||||||
precTab map[string]int
|
|
||||||
newVal func(string) Value
|
|
||||||
src string
|
|
||||||
pos int
|
|
||||||
tok string
|
|
||||||
}
|
|
||||||
|
|
||||||
const alphanum = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
||||||
|
|
||||||
func (p *Parser) stop(c uint8) bool {
|
|
||||||
switch {
|
|
||||||
case p.pos >= len(p.src):
|
|
||||||
return true
|
|
||||||
case c == '"':
|
|
||||||
if p.src[p.pos] == '"' {
|
|
||||||
p.pos++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case strings.IndexRune(alphanum, int(c)) >= 0:
|
|
||||||
return strings.IndexRune(alphanum, int(p.src[p.pos])) < 0
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) next() {
|
|
||||||
// skip blanks
|
|
||||||
for ; p.pos < len(p.src) && p.src[p.pos] <= ' '; p.pos++ {
|
|
||||||
}
|
|
||||||
if p.pos >= len(p.src) {
|
|
||||||
p.tok = ""
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start := p.pos
|
|
||||||
c := p.src[p.pos]
|
|
||||||
for p.pos < len(p.src) {
|
|
||||||
p.pos++
|
|
||||||
if p.stop(c) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.tok = p.src[start:p.pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) binaryExpr(prec1 int) Value {
|
|
||||||
x := p.newVal(p.tok)
|
|
||||||
p.next()
|
|
||||||
for prec := p.precTab[p.tok]; prec >= prec1; prec-- {
|
|
||||||
for p.precTab[p.tok] == prec {
|
|
||||||
op := p.tok
|
|
||||||
p.next()
|
|
||||||
y := p.binaryExpr(prec + 1)
|
|
||||||
x = x.BinaryOp(op, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func Eval(precTab map[string]int, newVal func(string) Value, src string) Value {
|
|
||||||
var p Parser
|
|
||||||
p.precTab = precTab
|
|
||||||
p.newVal = newVal
|
|
||||||
p.src = src
|
|
||||||
p.next()
|
|
||||||
return p.binaryExpr(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command-line expression evaluator
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := bufio.NewReader(os.Stdin)
|
|
||||||
for {
|
|
||||||
fmt.Printf("> ")
|
|
||||||
line, err := r.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fmt.Printf("%s\n", Eval(precTab, trace(newVal), line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom grammar and values
|
|
||||||
|
|
||||||
var precTab = map[string]int{
|
|
||||||
"&&": 1,
|
|
||||||
"||": 2,
|
|
||||||
"==": 3,
|
|
||||||
"!=": 3,
|
|
||||||
"<": 3,
|
|
||||||
"<=": 3,
|
|
||||||
">": 3,
|
|
||||||
">=": 3,
|
|
||||||
"+": 4,
|
|
||||||
"-": 4,
|
|
||||||
"*": 5,
|
|
||||||
"/": 5,
|
|
||||||
"%": 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
func newVal(lit string) Value {
|
|
||||||
x, err := strconv.Atoi(lit)
|
|
||||||
if err == nil {
|
|
||||||
return Int(x)
|
|
||||||
}
|
|
||||||
b, err := strconv.ParseBool(lit)
|
|
||||||
if err == nil {
|
|
||||||
return Bool(b)
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal literal '%s'", lit))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
func (e Error) String() string { return string(e) }
|
|
||||||
func (e Error) BinaryOp(op string, y Value) Value { return e }
|
|
||||||
|
|
||||||
type Int int
|
|
||||||
|
|
||||||
func (x Int) String() string { return strconv.Itoa(int(x)) }
|
|
||||||
func (x Int) BinaryOp(op string, y Value) Value {
|
|
||||||
switch y := y.(type) {
|
|
||||||
case Error:
|
|
||||||
return y
|
|
||||||
case Int:
|
|
||||||
switch op {
|
|
||||||
case "+":
|
|
||||||
return x + y
|
|
||||||
case "-":
|
|
||||||
return x - y
|
|
||||||
case "*":
|
|
||||||
return x * y
|
|
||||||
case "/":
|
|
||||||
return x / y
|
|
||||||
case "%":
|
|
||||||
return x % y
|
|
||||||
case "==":
|
|
||||||
return Bool(x == y)
|
|
||||||
case "!=":
|
|
||||||
return Bool(x != y)
|
|
||||||
case "<":
|
|
||||||
return Bool(x < y)
|
|
||||||
case "<=":
|
|
||||||
return Bool(x <= y)
|
|
||||||
case ">":
|
|
||||||
return Bool(x > y)
|
|
||||||
case ">=":
|
|
||||||
return Bool(x >= y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bool bool
|
|
||||||
|
|
||||||
func (x Bool) String() string { return strconv.FormatBool(bool(x)) }
|
|
||||||
func (x Bool) BinaryOp(op string, y Value) Value {
|
|
||||||
switch y := y.(type) {
|
|
||||||
case Error:
|
|
||||||
return y
|
|
||||||
case Bool:
|
|
||||||
switch op {
|
|
||||||
case "&&":
|
|
||||||
return Bool(x && y)
|
|
||||||
case "||":
|
|
||||||
return Bool(x || y)
|
|
||||||
case "==":
|
|
||||||
return Bool(x == y)
|
|
||||||
case "!=":
|
|
||||||
return Bool(x != y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func trace(newVal func(string) Value) func(string) Value {
|
|
||||||
return func(s string) Value {
|
|
||||||
v := newVal(s)
|
|
||||||
fmt.Printf("\tnewVal(%q) = %s\n", s, fmtv(v))
|
|
||||||
return &traceValue{v}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type traceValue struct {
|
|
||||||
Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *traceValue) BinaryOp(op string, y Value) Value {
|
|
||||||
z := x.Value.BinaryOp(op, y.(*traceValue).Value)
|
|
||||||
fmt.Printf("\t%s.BinaryOp(%q, %s) = %s\n", fmtv(x.Value), op, fmtv(y.(*traceValue).Value), fmtv(z))
|
|
||||||
return &traceValue{z}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *traceValue) String() string {
|
|
||||||
s := x.Value.String()
|
|
||||||
fmt.Printf("\t%s.String() = %#v\n", fmtv(x.Value), s)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func fmtv(v Value) string {
|
|
||||||
t := fmt.Sprintf("%T", v)
|
|
||||||
if i := strings.LastIndex(t, "."); i >= 0 { // strip package
|
|
||||||
t = t[i+1:]
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s(%#v)", t, v)
|
|
||||||
}
|
|
@ -1,259 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generic expression parser/evaluator
|
|
||||||
|
|
||||||
type Value interface {
|
|
||||||
String() string
|
|
||||||
BinaryOp(op string, y Value) Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type Parser struct {
|
|
||||||
precTab map[string]int
|
|
||||||
newVal func(string) Value
|
|
||||||
src string
|
|
||||||
pos int
|
|
||||||
tok string
|
|
||||||
}
|
|
||||||
|
|
||||||
const alphanum = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
||||||
|
|
||||||
func (p *Parser) stop(c uint8) bool {
|
|
||||||
switch {
|
|
||||||
case p.pos >= len(p.src):
|
|
||||||
return true
|
|
||||||
case c == '"':
|
|
||||||
if p.src[p.pos] == '"' {
|
|
||||||
p.pos++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case strings.IndexRune(alphanum, int(c)) >= 0:
|
|
||||||
return strings.IndexRune(alphanum, int(p.src[p.pos])) < 0
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) next() {
|
|
||||||
// skip blanks
|
|
||||||
for ; p.pos < len(p.src) && p.src[p.pos] <= ' '; p.pos++ {
|
|
||||||
}
|
|
||||||
if p.pos >= len(p.src) {
|
|
||||||
p.tok = ""
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start := p.pos
|
|
||||||
c := p.src[p.pos]
|
|
||||||
for p.pos < len(p.src) {
|
|
||||||
p.pos++
|
|
||||||
if p.stop(c) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.tok = p.src[start:p.pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) binaryExpr(prec1 int) Value {
|
|
||||||
x := p.newVal(p.tok)
|
|
||||||
p.next()
|
|
||||||
for prec := p.precTab[p.tok]; prec >= prec1; prec-- {
|
|
||||||
for p.precTab[p.tok] == prec {
|
|
||||||
op := p.tok
|
|
||||||
p.next()
|
|
||||||
y := p.binaryExpr(prec + 1)
|
|
||||||
x = x.BinaryOp(op, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func Eval(precTab map[string]int, newVal func(string) Value, src string) Value {
|
|
||||||
var p Parser
|
|
||||||
p.precTab = precTab
|
|
||||||
p.newVal = newVal
|
|
||||||
p.src = src
|
|
||||||
p.next()
|
|
||||||
return p.binaryExpr(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command-line expression evaluator
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := bufio.NewReader(os.Stdin)
|
|
||||||
for {
|
|
||||||
fmt.Printf("> ")
|
|
||||||
line, err := r.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fmt.Printf("%s\n", Eval(precTab, trace(newVal), line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom grammar and values
|
|
||||||
|
|
||||||
var precTab = map[string]int{
|
|
||||||
"&&": 1,
|
|
||||||
"||": 2,
|
|
||||||
"==": 3,
|
|
||||||
"!=": 3,
|
|
||||||
"<": 3,
|
|
||||||
"<=": 3,
|
|
||||||
">": 3,
|
|
||||||
">=": 3,
|
|
||||||
"+": 4,
|
|
||||||
"-": 4,
|
|
||||||
"*": 5,
|
|
||||||
"/": 5,
|
|
||||||
"%": 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
func newVal(lit string) Value {
|
|
||||||
x, err := strconv.Atoi(lit)
|
|
||||||
if err == nil {
|
|
||||||
return Int(x)
|
|
||||||
}
|
|
||||||
b, err := strconv.ParseBool(lit)
|
|
||||||
if err == nil {
|
|
||||||
return Bool(b)
|
|
||||||
}
|
|
||||||
s, err := strconv.Unquote(lit)
|
|
||||||
if err == nil {
|
|
||||||
return String(s)
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal literal '%s'", lit))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
func (e Error) String() string { return string(e) }
|
|
||||||
func (e Error) BinaryOp(op string, y Value) Value { return e }
|
|
||||||
|
|
||||||
type Int int
|
|
||||||
|
|
||||||
func (x Int) String() string { return strconv.Itoa(int(x)) }
|
|
||||||
func (x Int) BinaryOp(op string, y Value) Value {
|
|
||||||
switch y := y.(type) {
|
|
||||||
case Error:
|
|
||||||
return y
|
|
||||||
case String:
|
|
||||||
switch op {
|
|
||||||
case "*":
|
|
||||||
return String(strings.Repeat(string(y), int(x)))
|
|
||||||
}
|
|
||||||
case Int:
|
|
||||||
switch op {
|
|
||||||
case "+":
|
|
||||||
return x + y
|
|
||||||
case "-":
|
|
||||||
return x - y
|
|
||||||
case "*":
|
|
||||||
return x * y
|
|
||||||
case "/":
|
|
||||||
return x / y
|
|
||||||
case "%":
|
|
||||||
return x % y
|
|
||||||
case "==":
|
|
||||||
return Bool(x == y)
|
|
||||||
case "!=":
|
|
||||||
return Bool(x != y)
|
|
||||||
case "<":
|
|
||||||
return Bool(x < y)
|
|
||||||
case "<=":
|
|
||||||
return Bool(x <= y)
|
|
||||||
case ">":
|
|
||||||
return Bool(x > y)
|
|
||||||
case ">=":
|
|
||||||
return Bool(x >= y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bool bool
|
|
||||||
|
|
||||||
func (x Bool) String() string { return strconv.FormatBool(bool(x)) }
|
|
||||||
func (x Bool) BinaryOp(op string, y Value) Value {
|
|
||||||
switch y := y.(type) {
|
|
||||||
case Error:
|
|
||||||
return y
|
|
||||||
case Bool:
|
|
||||||
switch op {
|
|
||||||
case "&&":
|
|
||||||
return Bool(x && y)
|
|
||||||
case "||":
|
|
||||||
return Bool(x || y)
|
|
||||||
case "==":
|
|
||||||
return Bool(x == y)
|
|
||||||
case "!=":
|
|
||||||
return Bool(x != y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
type String string
|
|
||||||
|
|
||||||
func (x String) String() string { return strconv.Quote(string(x)) }
|
|
||||||
func (x String) BinaryOp(op string, y Value) Value {
|
|
||||||
switch y := y.(type) {
|
|
||||||
case Error:
|
|
||||||
return y
|
|
||||||
case Int:
|
|
||||||
switch op {
|
|
||||||
case "*":
|
|
||||||
return String(strings.Repeat(string(x), int(y)))
|
|
||||||
}
|
|
||||||
case String:
|
|
||||||
switch op {
|
|
||||||
case "+":
|
|
||||||
return x + y
|
|
||||||
case "<":
|
|
||||||
return Bool(x < y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func trace(newVal func(string) Value) func(string) Value {
|
|
||||||
return func(s string) Value {
|
|
||||||
v := newVal(s)
|
|
||||||
fmt.Printf("\tnewVal(%q) = %s\n", s, fmtv(v))
|
|
||||||
return &traceValue{v}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type traceValue struct {
|
|
||||||
Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *traceValue) BinaryOp(op string, y Value) Value {
|
|
||||||
z := x.Value.BinaryOp(op, y.(*traceValue).Value)
|
|
||||||
fmt.Printf("\t%s.BinaryOp(%q, %s) = %s\n", fmtv(x.Value), op, fmtv(y.(*traceValue).Value), fmtv(z))
|
|
||||||
return &traceValue{z}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *traceValue) String() string {
|
|
||||||
s := x.Value.String()
|
|
||||||
fmt.Printf("\t%s.String() = %#v\n", fmtv(x.Value), s)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func fmtv(v Value) string {
|
|
||||||
t := fmt.Sprintf("%T", v)
|
|
||||||
if i := strings.LastIndex(t, "."); i >= 0 { // strip package
|
|
||||||
t = t[i+1:]
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s(%#v)", t, v)
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
@ -1,277 +0,0 @@
|
|||||||
/* http://www.w3.org/Talks/Tools/Slidy/slidy.css
|
|
||||||
|
|
||||||
Copyright (c) 2005 W3C (MIT, ERCIM, Keio), All Rights Reserved.
|
|
||||||
W3C liability, trademark, document use and software licensing
|
|
||||||
rules apply, see:
|
|
||||||
|
|
||||||
http://www.w3.org/Consortium/Legal/copyright-documents
|
|
||||||
http://www.w3.org/Consortium/Legal/copyright-software
|
|
||||||
*/
|
|
||||||
body
|
|
||||||
{
|
|
||||||
margin: 0 0 0 0;
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: black;
|
|
||||||
background-color: white;
|
|
||||||
font-family: "Lucida Sans", "Lucida Grande", Lucida, sans-serif;
|
|
||||||
font-size: 14pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden { display: none; visibility: hidden }
|
|
||||||
|
|
||||||
div.toolbar {
|
|
||||||
position: fixed; z-index: 200;
|
|
||||||
top: auto; bottom: 0; left: 0; right: 0;
|
|
||||||
height: 1.2em; text-align: right;
|
|
||||||
padding-left: 1em;
|
|
||||||
padding-right: 1em;
|
|
||||||
font-size: 60%;
|
|
||||||
color: red; background: rgb(240,240,240);
|
|
||||||
}
|
|
||||||
|
|
||||||
div.background {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.handout {
|
|
||||||
margin-left: 20px;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.slide.titlepage {
|
|
||||||
color: white;
|
|
||||||
background: black;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.slide {
|
|
||||||
z-index: 20;
|
|
||||||
margin: 0 0 0 0;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 20px;
|
|
||||||
border-width: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
line-height: 120%;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this rule is hidden from IE 6 and below which don't support + selector */
|
|
||||||
div.slide + div[class].slide { page-break-before: always;}
|
|
||||||
|
|
||||||
div.slide h1 {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 20px;
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
border-bottom: 4px solid #36c;
|
|
||||||
display: block;
|
|
||||||
font-size: 160%;
|
|
||||||
line-height: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.slide h2 {
|
|
||||||
font-size:120%;
|
|
||||||
line-height: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc {
|
|
||||||
position: absolute;
|
|
||||||
top: auto;
|
|
||||||
bottom: 4em;
|
|
||||||
left: 4em;
|
|
||||||
right: auto;
|
|
||||||
width: 60%;
|
|
||||||
max-width: 30em;
|
|
||||||
height: 30em;
|
|
||||||
border: solid thin black;
|
|
||||||
padding: 1em;
|
|
||||||
background: rgb(240,240,240);
|
|
||||||
color: black;
|
|
||||||
z-index: 300;
|
|
||||||
overflow: auto;
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toc-heading {
|
|
||||||
width: 100%;
|
|
||||||
border-bottom: solid 1px rgb(180,180,180);
|
|
||||||
margin-bottom: 1em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
font-size: 120%;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 140%;
|
|
||||||
padding-top: 0.2em;
|
|
||||||
padding-bottom: 0.2em;
|
|
||||||
padding-left: 1em;
|
|
||||||
padding-right: 1em;
|
|
||||||
/*
|
|
||||||
border-style: solid;
|
|
||||||
border-left-width: 1em;
|
|
||||||
border-top-width: thin;
|
|
||||||
border-right-width: thin;
|
|
||||||
border-bottom-width: thin;
|
|
||||||
border-color: #95ABD0;
|
|
||||||
*/
|
|
||||||
color: #0F398D;
|
|
||||||
background-color: #fff8f8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
div.slide {
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
position: relative;
|
|
||||||
border-top-style: solid;
|
|
||||||
border-top-width: thin;
|
|
||||||
border-top-color: black;
|
|
||||||
}
|
|
||||||
div.slide pre { font-size: 60%; padding-left: 0.5em; }
|
|
||||||
div.handout { display: block; visibility: visible; }
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote { font-style: italic }
|
|
||||||
|
|
||||||
img { background-color: transparent }
|
|
||||||
|
|
||||||
p.copyright { font-size: smaller }
|
|
||||||
|
|
||||||
.center { text-align: center }
|
|
||||||
.footnote { font-size: smaller; margin-left: 2em; }
|
|
||||||
|
|
||||||
a img { border-width: 0; border-style: none }
|
|
||||||
|
|
||||||
a:visited { color: navy }
|
|
||||||
a:link { color: navy }
|
|
||||||
a:hover { color: red; text-decoration: underline }
|
|
||||||
a:active { color: red; text-decoration: underline }
|
|
||||||
|
|
||||||
a {text-decoration: none}
|
|
||||||
.navbar a:link {color: white}
|
|
||||||
.navbar a:visited {color: yellow}
|
|
||||||
.navbar a:active {color: red}
|
|
||||||
.navbar a:hover {color: red}
|
|
||||||
|
|
||||||
ul { list-style-type: square; }
|
|
||||||
ul ul { list-style-type: disc; }
|
|
||||||
ul ul ul { list-style-type: circle; }
|
|
||||||
ul ul ul ul { list-style-type: disc; }
|
|
||||||
li { margin-left: 2em; margin-top: 0.5em; }
|
|
||||||
li li { font-size: 85%; font-style: italic }
|
|
||||||
li li li { font-size: 85%; font-style: normal }
|
|
||||||
|
|
||||||
div dt
|
|
||||||
{
|
|
||||||
margin-left: 0;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
div dd
|
|
||||||
{
|
|
||||||
margin-left: 2em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
p,pre,ul,ol,blockquote,h2,h3,h4,h5,h6,dl,table {
|
|
||||||
margin-left: 1em;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.subhead { font-weight: bold; margin-top: 2em; }
|
|
||||||
|
|
||||||
p.smaller { font-size: smaller }
|
|
||||||
|
|
||||||
td,th { padding: 0.2em }
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0.5em 1.5em 0.5em 1.5em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
margin: 0.5em 1.5em 0.5em 1.5em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul { list-style-type: square; }
|
|
||||||
ul ul { list-style-type: disc; }
|
|
||||||
ul ul ul { list-style-type: circle; }
|
|
||||||
ul ul ul ul { list-style-type: disc; }
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
list-style: square;
|
|
||||||
//margin: 0.1em 0em 0.6em 0;
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
line-height: 140%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol li {
|
|
||||||
margin: 0.1em 0em 0.6em 1.5em;
|
|
||||||
padding: 0 0 0 0px;
|
|
||||||
line-height: 140%;
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
li ul li {
|
|
||||||
font-size: 85%;
|
|
||||||
font-style: italic;
|
|
||||||
list-style-type: disc;
|
|
||||||
background: transparent;
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
}
|
|
||||||
li li ul li {
|
|
||||||
font-size: 85%;
|
|
||||||
font-style: normal;
|
|
||||||
list-style-type: circle;
|
|
||||||
background: transparent;
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
}
|
|
||||||
li li li ul li {
|
|
||||||
list-style-type: disc;
|
|
||||||
background: transparent;
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li ol li {
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
li li ol li {
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
setting class="outline on ol or ul makes it behave as an
|
|
||||||
ouline list where blocklevel content in li elements is
|
|
||||||
hidden by default and can be expanded or collapsed with
|
|
||||||
mouse click. Set class="expand" on li to override default
|
|
||||||
*/
|
|
||||||
|
|
||||||
ol.outline li:hover { cursor: pointer }
|
|
||||||
ol.outline li.nofold:hover { cursor: default }
|
|
||||||
|
|
||||||
ul.outline li:hover { cursor: pointer }
|
|
||||||
ul.outline li.nofold:hover { cursor: default }
|
|
||||||
|
|
||||||
ol.outline { list-style:decimal; }
|
|
||||||
ol.outline ol { list-style-type:lower-alpha }
|
|
||||||
|
|
||||||
/* for slides with class "title" in table of contents */
|
|
||||||
a.titleslide { font-weight: bold; font-style: italic }
|
|
2772
doc/talks/slidy.js
2772
doc/talks/slidy.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user