foewhfuoewhow

main
Antonio De Lucreziis 2 years ago
parent 079cc4bc3c
commit 994212b3f3

Binary file not shown.

@ -0,0 +1,32 @@
subprojects = $(patsubst examples/%, %, $(wildcard examples/*))
.PHONY: usage
usage:
@printf "Found the following subprojects:\n"
@for project in $(subprojects); do printf " - %s\n" $$project; done
@printf "\n"
@printf "Available rules\n"
@printf " - run-<name>: Will run the program\n"
@printf " - compile-<name>: Will compile the program\n"
@printf " - compile-noinline-<name>: Will compile the program without inlining\n"
@printf " - decomp-<name>: Will run lensm on the project\n"
@printf " - decomp-noinline-<name>: Will run lensm on the project without inlining\n"
@printf "\n"
run-%: FORCE
go run -v ./examples/$*
compile-%: FORCE
go build -v -o "./bin/$*" ./examples/$*
compile-noinline-%: FORCE
go build -v -o "./bin/$*@no-inline" -gcflags=-l ./examples/$*
decomp-%: compile-% FORCE
lensm -text-size 16 -filter main "./bin/$*"
decomp-noinline-%: compile-noinline-% FORCE
lensm -text-size 16 -filter main "./bin/$*@no-inline"
FORCE: ;

Binary file not shown.

Binary file not shown.

@ -1,31 +0,0 @@
package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
func Min[T constraints.Ordered](x, y T) T {
if x < y {
return x
}
return y
}
type Liter int
func main() {
{
var a, b int = 1, 2
fmt.Println(Min(a, b))
}
{
var a, b float64 = 3.14, 2.71
fmt.Println(Min(a, b))
}
{
var a, b Liter = 1, 2
fmt.Println(Min(a, b))
}
}

@ -0,0 +1,83 @@
package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
// before go generics
func MinInt8(x, y int8) int8 {
if x < y {
return x
}
return y
}
func MinInt16(x, y int16) int16 {
if x < y {
return x
}
return y
}
func MinInt32(x, y int32) int32 {
if x < y {
return x
}
return y
}
func MinInt64(x, y int64) int64 {
if x < y {
return x
}
return y
}
func MinFloat32(x, y float32) float32 {
if x < y {
return x
}
return y
}
func MinFloat64(x, y float64) float64 {
if x < y {
return x
}
return y
}
// with go generics
func Min[T constraints.Ordered](x, y T) T {
if x < y {
return x
}
return y
}
type Liter int
func main() {
{
var a, b int = 1, 2
fmt.Println(Min(a, b))
}
{
var a, b float64 = 3.14, 2.71
fmt.Println(Min(a, b))
}
{
var a, b Liter = 1, 2
fmt.Println(Min(a, b))
}
}

@ -0,0 +1,87 @@
package main
//
// Encoding the semantics
//
type Bool interface{ isBool() }
type Term interface{ isTerm() }
type Term2Term interface{ isTerm2Term() }
// trick to encode higher-kinded types
type V[H Term2Term, T Term] Term
//
// Naturals
//
type Zero Term
type Succ Term2Term
// Some aliases
type One = V[Succ, Zero]
type Two = V[Succ, V[Succ, Zero]]
type Three = V[Succ, V[Succ, V[Succ, Zero]]]
//
// Equality
//
type Eq[A, B any] Bool
// Eq_Refl ~> forall x : x = x
func Eq_Reflexive[T any]() Eq[T, T] { panic("axiom") }
// Eq_Symmetric ~> forall a, b: a = b => b = a
func Eq_Symmetric[A, B any](_ Eq[A, B]) Eq[B, A] { panic("axiom") }
// Eq_Transitive ~> 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") }
// Function_Eq ~> forall f function, forall a, b terms: 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")
}
//
// Plus Axioms
//
type Plus[L, R Term] Term
// "a + 0 = a"
// Plus_Zero ~> forall a, b: a + succ(b) = succ(a + b)
func Plus_Zero[N Term]() Eq[Plus[N, Zero], N] { panic("axiom") }
// "a + (b + 1) = (a + b) + 1"
// Plus_Sum ~> forall a, b: a + succ(b) = succ(a + b)
func Plus_Sum[N, M Term]() Eq[Plus[N, V[Succ, M]], V[Succ, Plus[N, M]]] { panic("axiom") }
//
// 1 + 1 = 2
//
// Theorem_OnePlusOneEqTwo
func Theorem_OnePlusOneEqTwo() Eq[Plus[One, One], Two] {
var en1 Eq[
Plus[One, Zero],
One,
] = Plus_Zero[One]()
var en2 Eq[
V[Succ, Plus[One, Zero]],
Two,
] = Function_Eq[Succ](en1)
var en3 Eq[
Plus[One, One],
V[Succ, Plus[One, Zero]],
] = Plus_Sum[One, Zero]()
return Eq_Transitive(en3, en2)
}

@ -90,7 +90,7 @@ func MinInt8(x, y int8) int8 {
return y
}
func MinInt16(x, y int16) int8 {
func MinInt16(x, y int16) int16 {
if x < y {
return x
}
@ -510,6 +510,66 @@ d := &bytes.Buffer{} /* (*bytes.Buffer) */
(*bytes.Buffer).Write(d /* (*bytes.Buffer) */, []byte{ 42 })
```
---
<!-- _class: chapter -->
# Confronto con altri linguaggi
Vediamo come funzionano le generics in Go confrontandole con altri linguaggi
---
## C
```c
// versione semplificata da linux/minmax.h
#define min(x, y) ({ \ // block expr (GCC extension)
typeof(x) _min1 = (x); \ // eval x once
typeof(y) _min2 = (y); \ // eval y once
(void) (&_min1 == &_min2); \ // check same type
_min1 < _min2 ? _min1 : _min2; \ // do comparison
})
```
---
## C++
```cpp
template<typename T>
T min(T const& a, T const& b)
{
return (a < b) ? a : b;
}
```
---
## Rust
```rust
pub fn min<T: PartialOrd>(a: T, b: T) -> T {
if a < b {
a
} else {
b
}
}
```
---
## Go _Gcshape Stenciling_
- _A **gcshape** (or gcshape grouping) is a collection of types that can all **share the same instantiation of a generic function/method** in our implementation when specified as one of the type arguments_.
- _Two concrete types are in the same gcshape grouping if and only if they have the **same underlying type** or they are **both pointer types**._
- _In order to avoid creating a different function instantiation for each invocation of a generic function/method with distinct type arguments (which would be pure stenciling), we **pass a dictionary along with every call** to a generic function/method_.
<!-- :link: [Go 1.18 implementation of generics via dictionaries and gcshape stenciling](https://github.com/golang/proposal/blob/master/design/generics-implementation-dictionaries-go1.18.md) -->
---
<!-- _class: chapter -->
@ -613,50 +673,6 @@ var foo Validator = FooRequest{}
<!-- _class: chapter -->
# Confronto
Vediamo un attimo come fungono internamente le generics del Go
---
```c
// C?
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
```
```cpp
// C++
template<typename T>
T min(T const& a, T const& b)
{
return (a < b) ? a : b;
}
```
---
```go
// Go
func Min[T constraints.Ordered](x, y T) T {
if x < y {
return x
}
return y
}
```
```rust
// Rust
???
```
---
<!-- _class: chapter -->
# Pattern (2)
Vediamo un analogo di `PhantomData<T>` dal Rust per rendere _type-safe_ l'interfaccia di una libreria
@ -795,13 +811,6 @@ func (p Promise[T]) Await() (T, error) {
<-p.done
return p.value, p.err
}
type Waiter { Wait() error }
func (p Promise[T]) Wait() error {
<-p.done
return p.err
}
```
---
@ -809,11 +818,11 @@ func (p Promise[T]) Wait() error {
```go
type PromiseFunc[T any] func(resolve func(T), reject func(error))
func Start[T any](f PromiseFunc[T]) *Promise[T] {
func Run[T any](f PromiseFunc[T]) *Promise[T] {
done := make(chan struct{})
p := Promise{ done: done }
f(
go f(
func(value T) { p.value = value; done <- struct{} },
func(err error) { p.err = err; done <- struct{} }
)
@ -825,6 +834,13 @@ func Start[T any](f PromiseFunc[T]) *Promise[T] {
---
```go
type Waiter { Wait() error }
func (p Promise[T]) Wait() error {
<-p.done
return p.err
}
func AwaitAll(ws ...Waiter) error {
var wg sync.WaitGroup
wg.Add(len(ws))
@ -863,6 +879,157 @@ func AwaitAll(ws ...Waiter) error {
---
```go
Validate()
```
---
<!-- _class: chapter -->
# 1 + 1 = 2
_Proof checking_ in Go
---
## Premesse
```go
type Bool interface{ isBool() }
type Term interface{ isTerm() }
type Term2Term interface{ isTerm2Term() }
// trick to encode higher-kinded types
type V[H Term2Term, T Term] Term
```
---
## Assiomi dei Naturali
```go
type Zero Term
type Succ Term2Term
// Alcuni alias utili
type One = V[Succ, Zero]
type Two = V[Succ, V[Succ, Zero]]
type Three = V[Succ, V[Succ, V[Succ, Zero]]]
```
---
## Uguaglianza
```go
type Eq[A, B any] Bool
// Eq_Refl ~> forall x : x = x
func Eq_Reflexive[T any]() Eq[T, T] {
panic("axiom")
}
// Eq_Symmetric ~> forall a, b: a = b => b = a
func Eq_Symmetric[A, B any](_ Eq[A, B]) Eq[B, A] {
panic("axiom")
}
// Eq_Transitive ~> 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")
}
```
---
## "Funzionalità dell'uguale"
Per ogni funzione `F`, ovvero tipo vincolato all'interfaccia `Term2Term` vorremmo dire che
```
F
Eq[ A , B ] ------> Eq[ F[A] , F[B] ]
```
---
## "Funzionalità dell'uguale"
Data una funzione ed una dimostrazione che due cose sono uguali allora possiamo applicare la funzione ed ottenere altre cose uguali
```go
// Function_Eq ~> 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")
}
```
---
## Assiomi dell'addizione
```go
type Plus[L, R Term] Term
// "n + 0 = n"
// Plus_Zero ~> forall n, m: n + succ(m) = succ(n + m)
func Plus_Zero[N Term]() Eq[Plus[N, Zero], N] {
panic("axiom")
}
// "n + (m + 1) = (n + m) + 1"
// Plus_Sum ~> forall a, m: n + succ(m) = succ(n + m)
func Plus_Sum[N, M Term]() Eq[
Plus[N, V[Succ, M]],
V[Succ, Plus[N, M]],
] { panic("axiom") }
```
---
## 1 + 1 = 2
```go
func Theorem_OnePlusOneEqTwo() Eq[Plus[One, One], Two] {
var en1 Eq[ Plus[One, Zero], One ] = Plus_Zero[One]()
var en2 Eq[
V[Succ, Plus[One, Zero]],
Two
] = Function_Eq[Succ](en1)
var en3 Eq[
Plus[One, One],
V[Succ, Plus[One, Zero]],
] = Plus_Sum[One, Zero]()
return Eq_Transitive(en3, en2)
}
```
---
## 1 + 1 = 2
```go
func Theorem_OnePlusOneEqTwo() Eq[Plus[One, One], Two] {
return Eq_Transitive(
Plus_Sum[One, Zero](),
Function_Eq[Succ](
Plus_Zero[One](),
),
)
}
```
---
<!-- _class: chapter -->
# Conclusione
@ -890,3 +1057,19 @@ Per scrivere _codice generico_ in Go
# Fine :C
_Domande_
---
<style scoped>
li {
font-size: 80%;
}
</style>
## Bibliografia
- <https://go.dev/blog/intro-generics>
- <https://go.dev/blog/when-generics>
- <https://github.com/golang/proposal/blob/master/design/generics-implementation-dictionaries-go1.18.md>

Loading…
Cancel
Save