package fn

// T2 is the simplest 2-tuple type. It is useful for capturing ad hoc
// type conjunctions in a single value that can be easily dot-chained.
type T2[A, B any] struct {
	first  A
	second B
}

// NewT2 is the canonical constructor for a T2. We include it because the fields
// themselves are unexported.
func NewT2[A, B any](a A, b B) T2[A, B] {
	return T2[A, B]{
		first:  a,
		second: b,
	}
}

// First returns the first value in the T2.
func (t2 T2[A, B]) First() A {
	return t2.first
}

// Second returns the second value in the T2.
func (t2 T2[A, B]) Second() B {
	return t2.second
}

// Unpack ejects the 2-tuple's members into the multiple return values that
// are customary in go idiom.
func (t2 T2[A, B]) Unpack() (A, B) {
	return t2.first, t2.second
}

// Pair takes two functions that share the same argument type and runs them
// both and produces a 2-tuple of the results.
func Pair[A, B, C any](f func(A) B, g func(A) C) func(A) T2[B, C] {
	return func(a A) T2[B, C] {
		return NewT2[B, C](f(a), g(a))
	}
}

// MapFirst lifts the argument function into one that applies to the first
// element of a 2-tuple.
func MapFirst[A, B, C any](f func(A) B) func(T2[A, C]) T2[B, C] {
	return func(t2 T2[A, C]) T2[B, C] {
		return NewT2[B, C](f(t2.First()), t2.Second())
	}
}

// MapSecond lifts the argument function into one that applies to the second
// element of a 2-tuple.
func MapSecond[A, B, C any](f func(A) B) func(T2[C, A]) T2[C, B] {
	return func(t2 T2[C, A]) T2[C, B] {
		return NewT2[C, B](t2.First(), f(t2.Second()))
	}
}