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