package main

import "fmt"

// section: stack
type Stack[T any] struct {
	items []T
}

func NewStack[T any]() *Stack[T] {
	return &Stack[T]{
		items: make([]T, 0),
	}
}

func (s *Stack[T]) Push(item T) {
	s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
	if len(s.items) == 0 {
		var zero T
		return zero, false
	}
	index := len(s.items) - 1
	item := s.items[index]
	s.items = s.items[:index]
	return item, true
}

// section: stack

// section: simple-alias
// Simple type alias - creates a convenient name for a specific instantiation
type IntStack = Stack[int]
type StringStack = Stack[string]

// section: simple-alias

// section: parameterized-alias
// Parameterized type alias - the alias itself takes type parameters
type StringMap[V any] = map[string]V
type Pair[T, U any] = struct {
	First  T
	Second U
}
type Result[T any] = struct {
	Value T
	Err   error
}

// section: parameterized-alias

// section: invalid-alias
// Invalid: alias cannot be a type parameter itself
// type Identity[T any] = T  // compile error: cannot use a type parameter as RHS in type declaration

// section: invalid-alias

// section: usage
func main() {
	// Using simple type aliases
	intStack := new(IntStack)
	intStack.Push(1)
	intStack.Push(2)
	intStack.Push(3)

	if val, ok := intStack.Pop(); ok {
		fmt.Println("Popped from IntStack:", val)
	}

	// Using parameterized type aliases
	ages := StringMap[int]{
		"Alice": 30,
		"Bob":   25,
	}
	fmt.Println("Ages:", ages)

	pair := Pair[string, int]{
		First:  "count",
		Second: 42,
	}
	fmt.Println("Pair:", pair)

	result := Result[string]{
		Value: "success",
		Err:   nil,
	}
	fmt.Println("Result:", result.Value)
}

// section: usage
