Go doesn't have direct sum types, but we can use interfaces and type switches:

package main

import "fmt"

// Define types for our "sum type"
type Success struct {
Value string
}

type Error struct {
Message string
}

// Interface for our sum type
type Result interface {
isResult()
}

// Implement the interface
func (s Success) isResult() {}
func (e Error) isResult() {}

// Pattern matching using type switch
func handleResult(r Result) string {
switch v := r.(type) {
case Success:
return fmt.Sprintf("Success: %s", v.Value)
case Error:
return fmt.Sprintf("Error: %s", v.Message)
default:
// Go requires a default case, which can help catch new types
panic("Unhandled result type")
}
}

func main() {
result := Success{Value: "Operation completed"}
fmt.Println(handleResult(result))
}

Here is a practical example of it:

package main

import (
"fmt"
"time"
)

// Common interface for our "sum type"
type Notification interface {
Send() string
isNotification() // marker method
}

// Email notification
type EmailNotification struct {
To string
Subject string
Body string
}

func (n EmailNotification) Send() string {
return fmt.Sprintf("Email sent to %s with subject '%s'", n.To, n.Subject)
}

func (EmailNotification) isNotification() {}

// SMS notification
type SMSNotification struct {
PhoneNumber string
Message string
}

func (n SMSNotification) Send() string {
return fmt.Sprintf("SMS sent to %s", n.PhoneNumber)
}

func (SMSNotification) isNotification() {}

// Push notification
type PushNotification struct {
DeviceToken string
Title string
Message string
ExpiresAt time.Time
}

func (n PushNotification) Send() string {
return fmt.Sprintf("Push notification sent to device %s", n.DeviceToken)
}

func (PushNotification) isNotification() {}

// Function that handles different notification types
func ProcessNotification(notification Notification) {
// Type switch for pattern matching
switch n := notification.(type) {
case EmailNotification:
fmt.Printf("Processing Email: %s\n", n.Send())
fmt.Printf("Email details - To: %s, Subject: %s\n", n.To, n.Subject)

case SMSNotification:
fmt.Printf("Processing SMS: %s\n", n.Send())
fmt.Printf("SMS length: %d characters\n", len(n.Message))

case PushNotification:
fmt.Printf("Processing Push: %s\n", n.Send())
timeToExpiry := time.Until(n.ExpiresAt)
fmt.Printf("Push expires in: %v\n", timeToExpiry)

default:
// This catches any future notification types that we haven't handled
fmt.Println("Unknown notification type")
}
}

// Function to record notifications in different ways based on type
func LogNotification(notification Notification) string {
timestamp := time.Now().Format(time.RFC3339)

switch n := notification.(type) {
case EmailNotification:
return fmt.Sprintf("[%s] EMAIL: To=%s Subject=%s",
timestamp, n.To, n.Subject)

case SMSNotification:
return fmt.Sprintf("[%s] SMS: To=%s",
timestamp, n.PhoneNumber)

case PushNotification:
return fmt.Sprintf("[%s] PUSH: Device=%s Title=%s ExpiresAt=%s",
timestamp, n.DeviceToken, n.Title, n.ExpiresAt.Format(time.RFC3339))

default:
return fmt.Sprintf("[%s] UNKNOWN notification type", timestamp)
}
}

func main() {
// Create different notification types
email := EmailNotification{
To: "user@example.com",
Subject: "Important Update",
Body: "Hello, this is an important update about your account.",
}

sms := SMSNotification{
PhoneNumber: "+1234567890",
Message: "Your verification code is 123456",
}

push := PushNotification{
DeviceToken: "device-token-abc123",
Title: "New Message",
Message: "You have a new message from a friend",
ExpiresAt: time.Now().Add(24 * time.Hour),
}

// Process notifications
fmt.Println("=== Processing Notifications ===")
ProcessNotification(email)
fmt.Println()
ProcessNotification(sms)
fmt.Println()
ProcessNotification(push)

// Log notifications
fmt.Println("\n=== Logging Notifications ===")
fmt.Println(LogNotification(email))
fmt.Println(LogNotification(sms))
fmt.Println(LogNotification(push))

// We can also store different notification types in a slice
notifications := []Notification{email, sms, push}

fmt.Println("\n=== Processing Notification Queue ===")
for i, notification := range notifications {
fmt.Printf("Item %d: %s\n", i+1, LogNotification(notification))
}
}

The marker method pattern isNotification() prevents other types that happen to have a Send() method from being considered notifications. T