+-----+ +-----+
input | | intermediate | | output
------>| F |-------------->| G |------->
data | | data | | data
+-----+ +-----+
+----------+
input | | output
------>| FG |------->
data | | data
+----------+
In case of emergency, you can:
F
a better producerG
a better consumerTerms
are used for meta-programming
and as data
.Processes
are used to describe behavior
.Types
enforce a safe
use of data by terms.Sessions
enforce a safe
behavior of processes.Dependent
types
and sessions
enable formal verification
.Duality
sessions
enables fusion
.\[(A·B)_{ij} = \sum_{k=0}^{m-1} A_{ik} × B_{kj}\]
Bₘₓₚ
Aₙₓₘ
(A·B)ₙₓₚ
\(zipP(u,v)_k\) | \(= u_k × v_k\) |
\(sumD(w)\) | \(= \sum_{k=0}^{m-1} w_{k}\) |
\(dotP(u,v)\) | \(= sumD(zipP(u,v))\) |
Notations
?
: What I am going to read?
!
: Here is what I write!
double =
proc{a: ?Int, b: !Int}
let x <- a.
b <- x + x
proc{b: !Int}
new (a: Int).
a <- 21.
@double{a, b}
Replacing @double
by its definition:
proc{b: !Int}
new (a: Int).
a <- 21.
let x <- a.
b <- x + x
Fusing away the allocation:
proc{b: !Int}
let x = 21.
b <- x + x
Expanding local definitions:
proc{b: !Int}
b <- 42
Think again...
A type is an approximation
which enables abstraction
+--------+
a | | d = a / b
--->| |----------->
| divmod |
--->| |----------->
b | | m = a % b
+--------+
proc{ a: ?Int, b: ?Int , d: !Int, m: !Int } -- Any order
proc[: a: ?Int, b: ?Int , d: !Int, m: !Int :] -- In order
proc[ a: ?Int, b: ?Int , d: !Int, m: !Int ] -- All in parallel
proc[: [a: ?Int, b: ?Int], [d: !Int, m: !Int] :] -- ∥ read; ∥ write
proc[: {a: ?Int, b: ?Int}, [d: !Int, m: !Int] :] -- Any read; ∥ write
let x <- a.
let y <- b.
d <- x / y.
m <- x % y
( let x <- a
| let y <- b).
( d <- x / y
| m <- x % y)
let y <- b.
let x <- a.
m <- x % y.
d <- x / y
let x <- a.
let y <- b.
( d <- x / y
| m <- x % y)
let y <- b.
let x <- a.
( d <- x / y
| m <- x % y)
( let x <- a
| let y <- b).
d <- x / y.
m <- x % y
S | ~S |
---|---|
!A |
?A |
{S₀, ..., Sₙ} |
[~S₀, ..., ~Sₙ] |
[:S₀, ..., Sₙ:] |
[:~S₀, ..., ~Sₙ:] |
Example
~
{ !Int, [: ?Double, ?Int:]}
= [~!Int,~[: ?Double, ?Int:]]
= [ ?Int,~[: ?Double, ?Int:]]
= [ ?Int, [:~?Double,~?Int:]]
= [ ?Int, [: !Double, !Int:]]
S
a session (!Int
, {!Int,?Double}
)F
a process following S
G
a process following the dual of S
new [ c : S, d : ~S ].
( @F{c}
| @G{d} )
new [: c : S, d : ~S :].
@F{c}.
@G{d}
F = proc(u: {!Int^100}) ...
G = proc(u: {?Int^100}) ...
FG = proc()
new/alloc [: u: {!Int^100}, v: {?Int^100} :]
@F{u}.
@G{v}
Or:
F = proc(u: [!Int^100]) ...
G = proc(u: [?Int^100]) ...
zipP = zipWith Double Double Double _*D_
zipWith =
\(A B C: Type)
(f: (a: A)(b: B)-> C)
(n: Int)->
proc{[: a: ?A^n :], [: b: ?B^n :], [: c: !C^n :]}
slice (a,b,c)^n (cc <- f (<- a) (<- b))
sumD = foldl Double Double _+D_ 0.0
foldl = \(A B: Type)
(f: (acc: B)(a: A)-> B)
(init: B)
(n: Int)->
proc[: [: ai: ?A^n :], cr: !B :]
new (tmp: B).
tmp <- init.
slice (ai)^n
(tmp <- f (<- tmp) (<- a)).
fwd(?B)(tmp,cr)
dotP = \(n : Int)->
proc{u: [: ?Double^n :],
v: [: ?Double^n :],
o: !Double}
new [: w: [: !Double^n :], w' :]
@(zipP n){u, v, w}.
@(sumD n)[:w', o:]
fused_dotP = \(n : Int)->
proc{[: ai: ?Double^n :],
[: bi: ?Double^n :],
o : !Double}
new (tmp : Double).
tmp <- 0.0.
(slice (ai, bi)^n
let a <- ai.
let b <- bi.
let c <- tmp.
tmp <- (c +D (a *D b))).
let x <- tmp.
o <- x
Precision | + Modularity |
= |
Cost Free Abstraction!
Fusion:
predictable + safe + automatic