blob: 97cf4d254951a29988d715bec1af8787923943b0 [file] [log] [blame]
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]()
}