Skip to content

Instantly share code, notes, and snippets.

@porfirion
Last active January 19, 2023 01:07
Show Gist options
  • Save porfirion/f310b9d970cd4dbae4c79a90facecc49 to your computer and use it in GitHub Desktop.
Save porfirion/f310b9d970cd4dbae4c79a90facecc49 to your computer and use it in GitHub Desktop.
Conditions with parametric types
package main
import (
"errors"
"fmt"
"math/rand"
"strconv"
"time"
"unsafe"
)
type EXPR[Inp, Out any] func(Inp) (Out, error)
type COND[Inp any] interface {
Check(inp Inp) bool
}
func CONST[In, Out any](res Out) EXPR[In, Out] {
return func(in In) (Out, error) {
return res, nil
}
}
func RAND[In, Out any](res ...Out) EXPR[In, Out] {
rnd := rand.New(rand.NewSource(time.Now().Unix()))
return func(in In) (Out, error) {
return res[rnd.Intn(len(res))], nil
}
}
type FallbackCondition[Inp any] interface {
COND[Inp]
IsFallback()
}
func Fallback[Inp any]() FallbackCondition[Inp] {
return FALLBACK[Inp]{}
}
// always false, but it's a special case for defining switch
type FALLBACK[Inp any] struct{}
func (F FALLBACK[Inp]) Check(inp Inp) bool {
return false
}
func (F FALLBACK[Inp]) IsFallback() {}
func IsFallback[Inp any](c COND[Inp]) bool {
_, ok := c.(FallbackCondition[Inp])
return ok
}
func SWITCH[In, Out any](S map[COND[In]]EXPR[In, Out]) EXPR[In, Out] {
return func(inp In) (res Out, err error) {
var fallbackExp EXPR[In, Out]
for cond, val := range S {
if cond.Check(inp) {
return val(inp)
} else if IsFallback(cond) {
fallbackExp = val
}
}
if fallbackExp != nil {
return fallbackExp(inp)
}
return res, errors.New("default case is not defined")
}
}
type BOOL[Inp any] bool
func (b BOOL[Inp]) Check(Inp) bool {
return bool(b)
}
// doesn't work - slice is not hashable
type IN_SLICE[Inp comparable] []Inp
func (in IN_SLICE[Inp]) Check(inp Inp) bool {
for i := range in {
if in[i] == inp {
return true
}
}
return false
}
func IN[Inp comparable](value ...Inp) COND[Inp] {
return (*IN_SLICE[Inp])(&value)
}
// doesn't work - func is not hashable
type ConditionFunc[Inp any] func(Inp) bool
func (cf ConditionFunc[Inp]) Check(in Inp) bool {
return cf(in)
}
type ConditionFuncArr[Inp any] [1]func(Inp) bool
func (cfa *ConditionFuncArr[Inp]) Check(in Inp) bool {
return cfa[0](in)
}
func COND_FUNC[Inp any](c func(Inp) bool) COND[Inp] {
return &ConditionFuncArr[Inp]{c}
}
// pointer receiver not possible
//type FP[Inp any] *func(Inp)bool
//func (fp FP[Inp])Check(in Inp) {
// return (*fp)(in)
//}
type IN_STRUCT[Inp comparable] struct {
values []Inp
}
func (ins IN_STRUCT[Inp]) Check(in Inp) bool {
for i := range ins.values {
if ins.values[i] == in {
return true
}
}
return false
}
type IN_STRUCT_P[Inp comparable] struct {
values *[]Inp
}
func (ins IN_STRUCT_P[Inp]) Check(in Inp) bool {
for i := range *ins.values {
if (*ins.values)[i] == in {
return true
}
}
return false
}
type AND_SLICE[Inp any] []COND[Inp]
func (a AND_SLICE[Inp]) Check(in Inp) bool {
for i := range a {
if res := (a[i]).Check(in); !res {
return false
}
}
return true
}
func AND[Inp any](conditions ...COND[Inp]) COND[Inp] {
return (*AND_SLICE[Inp])(&conditions)
}
type OR_SLICE[Inp any] []COND[Inp]
func (o OR_SLICE[Inp]) Check(in Inp) bool {
for i := range o {
if o[i].Check(in) {
return true
}
}
return false
}
// can't be just COND[Inp], since we can't declare interface receiver
type NOT_ARR[Inp any] [1]COND[Inp]
func (n NOT_ARR[Inp]) Check(in Inp) bool {
return !(n[0]).Check(in)
}
func NOT[Inp any](c COND[Inp]) COND[Inp] {
return NOT_ARR[Inp]{c}
}
func main() {
var f func(int) bool
var funcsSlice []func(int) bool
var funcsArr [1]func(int) bool
fmt.Println("func", unsafe.Sizeof(f))
fmt.Println("func slice", unsafe.Sizeof(funcsSlice))
fmt.Println("func arr", unsafe.Sizeof(funcsArr))
fmt.Println("parametrized func arr", unsafe.Sizeof(ConditionFuncArr[int]{}))
var sw = SWITCH[int, string](map[COND[int]]EXPR[int, string]{
// works only with pointer
&IN_SLICE[int]{1}: CONST[int, string]("IN_SLICE 1 case"),
&IN_SLICE[int]{2}: CONST[int, string]("IN_SLICE 2 case"),
// doesn't work since function is not hashable
//IN_FUNC[int](3, 4): CONST[int, string]{"IN_FUNC case"},
// works only with pointer
&IN_STRUCT[int]{values: []int{5, 6}}: CONST[int, string]("IN_STRUCT case"),
IN_STRUCT_P[int]{values: ptr([]int{7})}: CONST[int, string]("IN_STRUCT_P case"),
IN[int](8): func(i int) (string, error) {
return strconv.Itoa(i), nil
},
BOOL[int](false): CONST[int, string]("FALSE case"),
&OR_SLICE[int]{
IN[int](10),
IN[int](11),
}: CONST[int, string]("10 or 11"),
&AND_SLICE[int]{
IN[int](12, 13),
NOT_ARR[int]{IN[int](13)},
}: CONST[int, string]("12 only"),
// the same
AND(
IN[int](12, 13),
// doesn't work without casting: type NOT_ARR[int] of (NOT_ARR[int]{…}) does not match inferred type Condition[int] for Condition[Inp]
(COND[int])(NOT_ARR[int]{IN[int](13)}),
): CONST[int, string]("12 again"),
// ha-ha! it works!
&ConditionFuncArr[int]{func(i int) bool {
return i == 13
}}: CONST[int, string]("13!"),
NOT(IN[int](15, 16, 17, 18, 19, 20)): CONST[int, string]("not more than 14"),
COND_FUNC(func(i int) bool {
return i < 16
}): CONST[int, string]("it is 15"),
IN[int](16): func(inp int) (string, error) {
return "", errors.New("just an error")
},
IN[int](17): SWITCH[int, string](map[COND[int]]EXPR[int, string]{
IN[int](18): CONST[int, string]("never true"),
}),
IN[int](18): RAND[int, string]("18", "18!", "18!!"),
FALLBACK[int]{}: CONST[int, string]("fallback case"),
Fallback[int](): CONST[int, string]("fallback func case"),
})
for i := 0; i < 20; i++ {
fmt.Printf("%d: ", i)
fmt.Println(sw(i))
}
ex()
}
func ptr[T any](v T) *T {
return &v
}
// =============================================
type (
CONDi = COND[int]
EXPRis = EXPR[int, string]
)
var (
SWITCHis = SWITCH[int, string]
INi = IN[int]
RANDis = RAND[int, string]
)
func ex() {
sw := SWITCHis(map[COND[int]]EXPR[int, string]{
INi(1, 2): func(i int) (string, error) {
return "1 or 2", nil
},
INi(3, 4): RANDis("5", "6"),
})
for i := 1; i <= 5; i++ {
fmt.Println(sw(i))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment