From 1f85bf14257cb92a0abc23ed40c6bc0da74f7e2d Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Fri, 31 Mar 2023 16:03:12 +0200 Subject: [PATCH] Forse finalmente le slide sono finite --- examples/chans/main.go | 139 +++++++++++++----------------------- examples/ifaces/main.go | 1 + slides.md | 152 +++++++++++++++++++++++++--------------- 3 files changed, 146 insertions(+), 146 deletions(-) create mode 100644 examples/ifaces/main.go diff --git a/examples/chans/main.go b/examples/chans/main.go index d693612..dabeaa0 100644 --- a/examples/chans/main.go +++ b/examples/chans/main.go @@ -1,75 +1,60 @@ package main import ( - "context" "fmt" + "math/rand" "time" ) -func Aggregate[T any](cs ...<-chan T) <-chan T { - agg := make(chan T) +func trySend[T any](c chan<- T, v T) bool { + select { + case c <- v: + return true + default: + return false + } +} - go func() { - defer close(agg) - for { - closed := 0 - for _, c := range cs { - select { - case value, more := <-c: - if more { - agg <- value - } else { - closed++ - } - default: - } - } - - if closed == len(cs) { - break - } - } - }() +func raceSame[T any](cs ...<-chan T) T { + done := make(chan T) + defer close(done) - return agg -} + for _, c := range cs { + go func(c <-chan T) { + trySend(done, <-c) + }(c) + } -type TargetChan[T any] struct { - c <-chan T - target *T + return <-done } -type TryReceiver interface { - TryReceive() bool +type Awaiter interface { + Await() } -func (tc TargetChan[T]) TryReceive() bool { - select { - case value := <-tc.c: - *tc.target = value - return true - default: - return false - } +type awaiterChan[T any] <-chan T + +func (rc awaiterChan[T]) Await() { <-rc } + +type targetChan[T any] struct { + c <-chan T + target *T } -func AwaitFirst(cancel context.CancelFunc, ws ...TryReceiver) { +func (rc targetChan[T]) Await() { *rc.target = <-rc.c } + +func raceAny(rs ...Awaiter) { done := make(chan struct{}) - go func() { - defer close(done) - for { - for _, w := range ws { - if w.TryReceive() { - done <- struct{}{} - return - } - } - } - }() + defer close(done) + + for _, r := range rs { + go func(r Awaiter) { + r.Await() + trySend(done, struct{}{}) + }(r) + } <-done - cancel() - return } func main() { @@ -77,56 +62,30 @@ func main() { c2 := make(chan int) c3 := make(chan float64) - c := context.Background() - c, cancel := context.WithCancel(c) - go func() { defer close(c1) - for { - select { - case <-c.Done(): - return - case <-time.After(300 * time.Millisecond): - } - - c1 <- "1" - } + time.Sleep(300 * time.Millisecond) + c1 <- "1" }() go func() { defer close(c2) - for { - select { - case <-c.Done(): - return - case <-time.After(400 * time.Millisecond): - } - - c2 <- 2 - } + time.Sleep(400 * time.Millisecond) + c2 <- 2 }() go func() { defer close(c3) - for { - select { - case <-c.Done(): - return - case <-time.After(200 * time.Millisecond): - } - - c3 <- 3.0 - } + time.Sleep(200 * time.Millisecond) + c3 <- 3.0 }() - var result1 string var result2 int var result3 float64 - AwaitFirst( - cancel, - TargetChan[string]{c1, &result1}, - TargetChan[int]{c2, &result2}, - TargetChan[float64]{c3, &result3}, + raceAny( + awaiterChan[string](c1), + targetChan[int]{c2, &result2}, + targetChan[float64]{c3, &result3}, ) - fmt.Println(result1, result2, result3) + fmt.Println(result2, result3) } diff --git a/examples/ifaces/main.go b/examples/ifaces/main.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/examples/ifaces/main.go @@ -0,0 +1 @@ +package main diff --git a/slides.md b/slides.md index 9861548..bd1c1a0 100644 --- a/slides.md +++ b/slides.md @@ -798,78 +798,109 @@ user1, _ := database.Read(db, tables.Users, userRef1) -# Altro esempio caotico -Vediamo come implementare le _promise_ in Go con le generics +# Pattern: _Channels_ +Alcune utility per lavorare meglio con i _channel_ --- ```go -type Promise[T any] struct { - value T - err error - done <-chan struct{} -} - -func (p Promise[T]) Await() (T, error) { - <-p.done - return p.value, p.err +func trySend[T any](c chan<- T, v T) bool { + select { + case c <- v: + return true + default: + return false + } } ``` --- ```go -type PromiseFunc[T any] func(resolve func(T), reject func(error)) +func raceSame[T any](cs ...<-chan T) T { + done := make(chan T) + defer close(done) + + for _, c := range cs { + go func(c <-chan T) { + trySend(done, <-c) + }(c) + } -func Run[T any](f PromiseFunc[T]) *Promise[T] { - done := make(chan struct{}) - p := Promise{ done: done } + return <-done +} +``` - go f( - func(value T) { p.value = value; done <- struct{} }, - func(err error) { p.err = err; done <- struct{} } - ) +--- - return &p +```go +type Awaiter interface { + Await() } + +type awaiterChan[T any] <-chan T + +func (rc awaiterChan[T]) Await() { <-rc } ``` --- ```go -func AwaitAll[T any](ps ...*Promise[T]) error { - ... +type targetChan[T any] struct { + c <-chan T + target *T } + +func (rc targetChan[T]) Await() { *rc.target = <-rc.c } ``` +--- + ```go -type Waiter interface { Wait() error } +func race(rs ...Awaiter) { + done := make(chan struct{}) + defer close(done) -func (p Promise[T]) Wait() error { - <-p.done - return p.err -} + for _, r := range rs { + go func(r Awaiter) { + r.Await() + trySend(done, struct{}{}) + }(r) + } -func AwaitAll(ws ...Waiter) error { - ... + <-done } ``` --- ```go -func ResolveInto[T any](p *Promise[T], target *T) *Promise[T] { - ... -} +var result2 int +var result3 float64 + +raceAny( + awaiterChan[string](c1), + targetChan[int]{c2, &result2}, + targetChan[float64]{c3, &result3}, +) + +fmt.Println(result2, result3) ``` +--- + ```go -AwaitAll( - ResolveInto(httpRequest1, &result1), // :: *Promise[int] - ResolveInto(httpRequest2, &result2), // :: *Promise[struct{ ... }] - ResolveInto(httpRequest3, &result3), // :: *Promise[any] - timer1, // :: *Promise[struct{}] +var result2 int +var result3 float64 + +// Variante più pulita di questa utility +channels.Race( + channels.Awaiter(c1), + channels.Awaiter(c2, channels.WithTarget(&result2)), + channels.Awaiter(c3, channels.WithTarget(&result3)), ) + +fmt.Println(result2, result3) ``` --- @@ -883,15 +914,24 @@ _Proof checking_ in Go ## Premesse +Definiamo i possibili "tipi" delle nostre espressioni + ```go type Bool interface{ isBool() } -type Term interface{ isTerm() } +type Nat interface{ isNat() } -type Term2Term interface{ isTerm2Term() } +type Nat2Nat interface{ isNat2Nat() } +``` + +--- + +## Premesse + +Trick per codificare higher-kinded types in Go -// Trick per codificare higher-kinded types -type V[H Term2Term, T Term] Term +```go +type V[ H Nat2Nat, T Nat ] Nat ``` --- @@ -899,8 +939,8 @@ type V[H Term2Term, T Term] Term ## Assiomi dei Naturali ```go -type Zero Term -type Succ Term2Term +type Zero Nat +type Succ Nat2Nat // Alcuni alias utili type One = V[Succ, Zero] @@ -918,19 +958,19 @@ type Eq[A, B any] Bool // Eq_Refl ovvero l'assioma // forall x : x = x func Eq_Reflexive[T any]() Eq[T, T] { - panic("axiom") + panic("axiom: comptime only") } // Eq_Symmetric ovvero l'assioma // forall a, b: a = b => b = a func Eq_Symmetric[A, B any](_ Eq[A, B]) Eq[B, A] { - panic("axiom") + panic("axiom: comptime only") } // Eq_Transitive ovvero l'assioma // forall a, b, c: a = b e b = c => a = c func Eq_Transitive[A, B, C any](_ Eq[A, B], _ Eq[B, C]) Eq[A, C] { - panic("axiom") + panic("axiom: comptime only") } ``` @@ -938,7 +978,7 @@ func Eq_Transitive[A, B, C any](_ Eq[A, B], _ Eq[B, C]) Eq[A, C] { ## Uguaglianza e Sostituzione -Per ogni funzione `F`, ovvero tipo vincolato all'interfaccia `Term2Term` vorremmo dire che +Per ogni funzione `F`, ovvero tipo vincolato all'interfaccia `Nat2Nat` vorremmo dire che ``` F @@ -954,9 +994,9 @@ Data una funzione ed una dimostrazione che due cose sono uguali allora possiamo ```go // Function_Eq ovvero l'assioma -// forall f function, forall a, b term: a = b => f(a) = f(b) -func Function_Eq[F Term2Term, A, B Term](_ Eq[A, B]) Eq[V[F, A], V[F, B]] { - panic("axiom") +// forall f function, forall a, b nat: a = b => f(a) = f(b) +func Function_Eq[F Nat2Nat, A, B Nat](_ Eq[A, B]) Eq[V[F, A], V[F, B]] { + panic("axiom: comptime only") } ``` @@ -965,24 +1005,24 @@ func Function_Eq[F Term2Term, A, B Term](_ Eq[A, B]) Eq[V[F, A], V[F, B]] { ## Assiomi dell'addizione ```go -type Plus[L, R Term] Term +type Plus[L, R Nat] Nat // "n + 0 = n" // Plus_Zero ovvero l'assioma // forall n, m: n + succ(m) = succ(n + m) -func Plus_Zero[N Term]() Eq[Plus[N, Zero], N] { - panic("axiom") +func Plus_Zero[N Nat]() Eq[Plus[N, Zero], N] { + panic("axiom: comptime only") } // "n + (m + 1) = (n + m) + 1" // Plus_Sum ovvero l'assioma -// forall a, m: n + succ(m) = succ(n + m) -func Plus_Sum[N, M Term]() Eq[ +// forall n, m: n + succ(m) = succ(n + m) +func Plus_Sum[N, M Nat]() Eq[ Plus[N, V[Succ, M]], V[Succ, Plus[N, M]], -] { panic("axiom") } +] { panic("axiom: comptime only") } ``` ---