Scala ADTs, Sealed Traits, and Enums for Python Developers
SkillDev toolsTranslating Python Enums and Protocols to Scala sealed traits and algebraic data types
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Scala ADTs, Sealed Traits, and Enums for Python Developers skill
What this skill tells your AI
The instructions your AI receives, as published by cxcscmu/skilllearnbench in skills/b1-one-shot-claude-haiku-4-5/python-scala-translation/scala-adt-enums/SKILL.md and read by ahel’s review.
Enums
Python Enum
from enum import Enum
class TokenType(Enum):
STRING = "string"
NUMERIC = "numeric"
TEMPORAL = "temporal"
STRUCTURED = "structured"
BINARY = "binary"
NULL = "null"
# Usage
t = TokenType.STRING
t.value # "string"
t.name # "STRING"
Scala Enum (Scala 2.13 - using sealed trait + case objects)
sealed trait TokenType {
def value: String
}
object TokenType {
case object STRING extends TokenType { val value = "string" }
case object NUMERIC extends TokenType { val value = "numeric" }
case object TEMPORAL extends TokenType { val value = "temporal" }
case object STRUCTURED extends TokenType { val value = "structured" }
case object BINARY extends TokenType { val value = "binary" }
case object NULL extends TokenType { val value = "null" }
// Helper for pattern matching
def all: List[TokenType] = List(STRING, NUMERIC, TEMPORAL, STRUCTURED, BINARY, NULL)
}
// Usage
val t: TokenType = TokenType.STRING
t.value // "string"
Algebraic Data Types (ADTs)
Python Union Types
from typing import Union
JsonValue = Union[str, int, float, bool, None, list["JsonValue"], dict[str, "JsonValue"]]
# Function handling multiple types
def process(value: Union[str, int]) -> str:
if isinstance(value, str):
return value
else:
return str(value)
Scala Sealed Traits (Idiomatic ADT)
sealed trait JsonValue
case class JString(value: String) extends JsonValue
case class JNumber(value: Double) extends JsonValue
case class JBoolean(value: Boolean) extends JsonValue
case object JNull extends JsonValue
case class JArray(value: Vector[JsonValue]) extends JsonValue
case class JObject(value: Map[String, JsonValue]) extends JsonValue
// Pattern matching (exhaustive)
def process(json: JsonValue): String = json match {
case JString(s) => s
case JNumber(n) => n.toString
case JBoolean(b) => b.toString
case JNull => "null"
case JArray(arr) => s"Array(${arr.size})"
case JObject(obj) => s"Object(${obj.size})"
}
Protocols → Scala Traits
Python Protocol
from typing import Protocol, runtime_checkable
@runtime_checkable
class Tokenizable(Protocol):
def to_token(self) -> str: ...
def process(obj: Tokenizable) -> str:
return obj.to_token()
Scala Trait (Type Class Pattern)
trait Tokenizable {
def toToken: String
}
def process(obj: Tokenizable): String = obj.toToken
// Or as type class (more flexible, less invasive)
trait Tokenizable[T] {
def toToken(value: T): String
}
object Tokenizable {
implicit val stringTokenizable: Tokenizable[String] = new Tokenizable[String] {
def toToken(value: String) = value
}
}
def process[T](obj: T)(implicit ev: Tokenizable[T]): String = ev.toToken(obj)
Abstract Classes and Inheritance
Python ABC
from abc import ABC, abstractmethod
from typing import Generic, TypeVar
T = TypeVar("T")
class BaseTokenizer(ABC, Generic[T]):
@abstractmethod
def tokenize(self, value: T) -> Token:
pass
def tokenize_batch(self, values: Iterable[T]) -> Iterator[Token]:
for v in values:
yield self.tokenize(v)
class StringTokenizer(BaseTokenizer[str]):
def tokenize(self, value: str) -> Token:
return Token(value, TokenType.STRING)
Scala Abstract Class
abstract class BaseTokenizer[T] {
def tokenize(value: T): Token
def tokenizeBatch(values: Iterable[T]): Iterator[Token] =
values.toIterator.map(tokenize)
}
class StringTokenizer extends BaseTokenizer[String] {
def tokenize(value: String): Token =
Token(value, TokenType.STRING)
}
Dataclasses → Case Classes
Python Dataclass
from dataclasses import dataclass, field
from typing import Any
@dataclass(frozen=True)
class Token:
value: str
token_type: TokenType
metadata: dict[str, Any] = field(default_factory=dict)
def with_metadata(self, **kwargs: Any) -> "Token":
new_meta = {**self.metadata, **kwargs}
return Token(self.value, self.token_type, new_meta)
Scala Case Class
case class Token(
value: String,
tokenType: TokenType,
metadata: Map[String, Any] = Map()
) {
def withMetadata(pairs: (String, Any)*): Token = {
val newMeta = metadata ++ pairs.toMap
copy(metadata = newMeta)
}
}
Mutable Dataclasses → Regular Classes
Python Mutable Dataclass
@dataclass
class MutableTokenBatch:
tokens: list[Token] = field(default_factory=list)
_processed: bool = False
def add(self, token: Token) -> None:
if self._processed:
raise RuntimeError("Batch already processed")
self.tokens.append(token)
def mark_processed(self) -> None:
self._processed = True
Scala Class
class MutableTokenBatch {
private val tokens = scala.collection.mutable.ListBuffer[Token]()
private var processed = false
def add(token: Token): Unit = {
if (processed) throw new RuntimeException("Batch already processed")
tokens += token
}
def markProcessed(): Unit = {
processed = true
}
def getTokens: List[Token] = tokens.toList
}
Pattern Matching for Type Narrowing
Scala pattern matching is more powerful than Python's isinstance checks:
// Python equivalent:
if isinstance(value, str):
return value
elif isinstance(value, int):
return value.toString()
// Scala pattern matching:
value match {
case s: String => s
case i: Int => i.toString
case _ => "unknown"
}
// Even better with case classes:
value match {
case Token(v, TokenType.STRING, _) => v
case Token(v, TokenType.NUMERIC, _) => s"[NUM]$v"
case _ => "other"
}
Best Practices
- Use sealed traits for sum types - ensures exhaustive pattern matching
- Prefer case classes over regular classes - automatic equals, hashCode, toString, copy
- Use case objects for singleton ADT members
- Leverage pattern matching - more concise than isinstance chains
- Consider type classes for ad-hoc polymorphism - instead of duck typing
- Use Option[T] instead of None/null - more type-safe
Signals
- GitHub stars
- 83
- Forks
- 5
- Last commit
- Jul 2026
Advanced
- Catalog kind
- skill
- Gateway key
scala-adt-enums- Source
- github.com/cxcscmu/skilllearnbench