Run2 scala type classes
Translating Python Protocol, ABC, and isinstance dispatch to Scala traits, overloaded methods, and functor/monad abstractions.From its SKILL.md
npx -y skills add cxcscmu/SkillLearnBench --skill run2_scala-type-classesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
4.6 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it
Scala Traits, Overloading, and Functors (vs Python Protocol/ABC/isinstance)
Python Protocol → Scala Trait
Python structural typing (duck typing):
@runtime_checkable
class Tokenizable(Protocol):
def to_token(self) -> str: ...
Scala uses nominal typing — classes must explicitly declare extends Tokenizable:
trait Tokenizable {
def toToken: String
}
- No
@runtime_checkable— Scala trait membership is checked at compile time - Method name convention:
toToken(camelCase) notto_token
Python ABC → Scala Abstract Class
class BaseTokenizer(ABC, Generic[T]):
@abstractmethod
def tokenize(self, value: T) -> Token: ...
def tokenize_batch(self, values: Iterable[T]) -> Iterator[Token]:
for v in values: yield self.tokenize(v)
abstract class BaseTokenizer[T] {
def tokenize(value: T): Token // abstract (no body)
def tokenizeBatch(values: Iterable[T]): Iterator[Token] =
values.iterator.map(tokenize) // concrete default
}
- Methods without body are abstract — no
@abstractmethodannotation needed values.iterator.map(tokenize)is lazy — same semantics as Python generator
Python isinstance Dispatch → Scala Method Overloading
Python runtime dispatch with type checks:
def tokenize(self, value: Any) -> Token:
if value is None: return Token("NULL", TokenType.NULL)
if isinstance(value, (str, bytes)): return self._string_tokenizer.tokenize(value)
if isinstance(value, (int, float, Decimal)): return self._numeric_tokenizer.tokenize(value)
if isinstance(value, (datetime, date)): return self._temporal_tokenizer.tokenize(value)
Scala compile-time overloading:
final class UniversalTokenizer {
private val stringTokenizer = new StringTokenizer()
private val numericTokenizer = new NumericTokenizer()
private val temporalTokenizer = new TemporalTokenizer()
def tokenize(value: String): Token = stringTokenizer.tokenize(value)
def tokenize(value: Array[Byte]): Token = stringTokenizer.tokenizeBytes(value)
def tokenize(value: Int): Token = numericTokenizer.tokenizeInt(value)
def tokenize(value: Double): Token = numericTokenizer.tokenizeDouble(value)
def tokenize(value: BigDecimal): Token = numericTokenizer.tokenize(value)
def tokenize(value: LocalDateTime): Token = temporalTokenizer.tokenize(value)
def tokenize(value: LocalDate): Token = temporalTokenizer.tokenizeDate(value)
def tokenize(value: Tokenizable): Token = Token(value.toToken, TokenType.STRUCTURED)
def tokenizeNull: Token = Token("NULL", TokenType.NULL) // null has no value in Scala
}
- Each overload is resolved at compile time — no runtime overhead
None→tokenizeNull(separate method, since Scalanullis not a type)
TokenFunctor and TokenMonad
class TokenFunctor[T](private val _value: T) {
def get: T = _value
def map[B](f: T => B): TokenFunctor[B] = new TokenFunctor(f(_value))
def flatMap[B](f: T => TokenFunctor[B]): TokenFunctor[B] = f(_value)
def getOrElse[B >: T](default: => B): B = Option(_value).getOrElse(default)
}
Key improvements over Python:
mapandflatMapare properly typed withB— notAnygetis the primary value accessorgetOrElseuses call-by-namedefault— only evaluated if needed[B >: T]lower bound ingetOrElsepreserves type safety
class TokenMonad[T](value: T) extends TokenFunctor[T](value) {
def ap[B](funcWrapped: TokenMonad[T => B]): TokenMonad[B] =
new TokenMonad(funcWrapped.get(get))
}
object TokenMonad {
def pure[T](value: T): TokenMonad[T] = new TokenMonad(value)
}
purelives in companion object — replaces Python@classmethodapproperly typed: acceptsTokenMonad[T => B], returnsTokenMonad[B]
TokenRegistry: Handler Pipeline
Python for/else semantics (first successful handler wins):
for handler in self._handlers:
result = handler(item)
if result is not None:
results.append(result); break
else:
results.append(None)
Scala with lazy iterator short-circuit:
handlers.iterator.flatMap(h => h(item)).nextOption()
h(item)returnsOption[Token]flatMaponIterator[Option[Token]]flattens toIterator[Token], skippingNone.nextOption()takes only the first result — lazy, so remaining handlers NOT evaluated- Returns
Option[Token]—Noneif all handlers returnedNone
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.