package a import "fmt" type ImplicitOrd interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string } func LessGiven[T ImplicitOrd]() Ord[T] { return LessFunc[T](func(a, b T) bool { return a < b }) } type Eq[T any] interface { Eqv(a T, b T) bool } type Ord[T any] interface { Eq[T] Less(a T, b T) bool } type LessFunc[T any] func(a, b T) bool func (r LessFunc[T]) Eqv(a, b T) bool { return r(a, b) == false && r(b, a) == false } func (r LessFunc[T]) Less(a, b T) bool { return r(a, b) } type Option[T any] struct { v *T } func (r Option[T]) IsDefined() bool { return r.v != nil } func (r Option[T]) IsEmpty() bool { return !r.IsDefined() } func (r Option[T]) Get() T { return *r.v } func (r Option[T]) String() string { if r.IsDefined() { return fmt.Sprintf("Some(%v)", r.v) } else { return "None" } } func (r Option[T]) OrElse(t T) T { if r.IsDefined() { return *r.v } return t } func (r Option[T]) Recover(f func() T) Option[T] { if r.IsDefined() { return r } t := f() return Option[T]{&t} } type Func1[A1, R any] func(a1 A1) R type Func2[A1, A2, R any] func(a1 A1, a2 A2) R func (r Func2[A1, A2, R]) Curried() Func1[A1, Func1[A2, R]] { return func(a1 A1) Func1[A2, R] { return Func1[A2, R](func(a2 A2) R { return r(a1, a2) }) } } type HList interface { sealed() } // Header is constrains interface type, enforce Head type of Cons is HT type Header[HT any] interface { HList Head() HT } // Cons means H :: T // zero value of Cons[H,T] is not allowed. // so Cons defined as interface type type Cons[H any, T HList] interface { HList Head() H Tail() T } type Nil struct { } func (r Nil) Head() Nil { return r } func (r Nil) Tail() Nil { return r } func (r Nil) String() string { return "Nil" } func (r Nil) sealed() { } type hlistImpl[H any, T HList] struct { head H tail T } func (r hlistImpl[H, T]) Head() H { return r.head } func (r hlistImpl[H, T]) Tail() T { return r.tail } func (r hlistImpl[H, T]) String() string { return fmt.Sprintf("%v :: %v", r.head, r.tail) } func (r hlistImpl[H, T]) sealed() { } func hlist[H any, T HList](h H, t T) Cons[H, T] { return hlistImpl[H, T]{h, t} } func Concat[H any, T HList](h H, t T) Cons[H, T] { return hlist(h, t) } func Empty() Nil { return Nil{} } func Some[T any](v T) Option[T] { return Option[T]{}.Recover(func() T { return v }) } func None[T any]() Option[T] { return Option[T]{} } func Ap[T, U any](t Option[Func1[T, U]], a Option[T]) Option[U] { return FlatMap(t, func(f Func1[T, U]) Option[U] { return Map(a, f) }) } func Map[T, U any](opt Option[T], f func(v T) U) Option[U] { return FlatMap(opt, func(v T) Option[U] { return Some(f(v)) }) } func FlatMap[T, U any](opt Option[T], fn func(v T) Option[U]) Option[U] { if opt.IsDefined() { return fn(opt.Get()) } return None[U]() } type ApplicativeFunctor1[H Header[HT], HT, A, R any] struct { h Option[H] fn Option[Func1[A, R]] } func (r ApplicativeFunctor1[H, HT, A, R]) ApOption(a Option[A]) Option[R] { return Ap(r.fn, a) } func (r ApplicativeFunctor1[H, HT, A, R]) Ap(a A) Option[R] { return r.ApOption(Some(a)) } func Applicative1[A, R any](fn Func1[A, R]) ApplicativeFunctor1[Nil, Nil, A, R] { return ApplicativeFunctor1[Nil, Nil, A, R]{Some(Empty()), Some(fn)} } type ApplicativeFunctor2[H Header[HT], HT, A1, A2, R any] struct { h Option[H] fn Option[Func1[A1, Func1[A2, R]]] } func (r ApplicativeFunctor2[H, HT, A1, A2, R]) ApOption(a Option[A1]) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { nh := FlatMap(r.h, func(hv H) Option[Cons[A1, H]] { return Map(a, func(av A1) Cons[A1, H] { return Concat(av, hv) }) }) return ApplicativeFunctor1[Cons[A1, H], A1, A2, R]{nh, Ap(r.fn, a)} } func (r ApplicativeFunctor2[H, HT, A1, A2, R]) Ap(a A1) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { return r.ApOption(Some(a)) } func Applicative2[A1, A2, R any](fn Func2[A1, A2, R]) ApplicativeFunctor2[Nil, Nil, A1, A2, R] { return ApplicativeFunctor2[Nil, Nil, A1, A2, R]{Some(Empty()), Some(fn.Curried())} } func OrdOption[T any](m Ord[T]) Ord[Option[T]] { return LessFunc[Option[T]](func(t1 Option[T], t2 Option[T]) bool { if !t1.IsDefined() && !t2.IsDefined() { return false } return Applicative2(m.Less).ApOption(t1).ApOption(t2).OrElse(!t1.IsDefined()) }) } func Given[T ImplicitOrd]() Ord[T] { return LessGiven[T]() }