Last active
January 19, 2023 01:07
-
-
Save porfirion/f310b9d970cd4dbae4c79a90facecc49 to your computer and use it in GitHub Desktop.
Conditions with parametric types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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