agentsclimarketplace

Python scala builder pattern

Skill cxcscmu/SkillLearnBench/skills/b4-skill-creator-claude-sonnet-4-6/python-scala-translation/python-scala-builder-pattern

Guide for translating Python fluent builder patterns and mutable accumulator objects to idiomatic immutable Scala builders with companion objects. Use this skill when converting Python classes with method chaining (returning self), mutable state accumulation, or factory/build() methods to Scala.From its SKILL.md

Install
npx -y skills add cxcscmu/SkillLearnBench --skill python-scala-builder-pattern

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

4.0 KB, 892 tokens by cl100k_base, as published. Nobody here has run it

Python Fluent Builder → Scala Immutable Builder

Python builders often use mutable self and return self for chaining. Scala's idiomatic approach uses immutable value objects — each with* method returns a new builder instance.

Pattern

class TokenizerBuilder(Generic[T]):
    def __init__(self) -> None:
        self._normalizers: list[Callable[[str], str]] = []
        self._validators: list[Callable[[T], bool]] = []
        self._metadata: dict[str, Any] = {}

    def with_normalizer(self, norm) -> "TokenizerBuilder[T]":
        self._normalizers.append(norm)
        return self

    def with_metadata(self, **kwargs) -> "TokenizerBuilder[T]":
        self._metadata.update(kwargs)
        return self

    def build(self) -> Callable[[T], Token]:
        ...
// Scala: immutable builder; each method returns a NEW instance
class TokenizerBuilder[T] private (
  normalizers: List[String => String],
  validators:  List[T => Boolean],
  metadata:    Map[String, Any]
) {
  def withNormalizer(f: String => String): TokenizerBuilder[T] =
    new TokenizerBuilder(normalizers :+ f, validators, metadata)

  def withValidator(f: T => Boolean): TokenizerBuilder[T] =
    new TokenizerBuilder(normalizers, validators :+ f, metadata)

  def withMetadata(pairs: (String, Any)*): TokenizerBuilder[T] =
    new TokenizerBuilder(normalizers, validators, metadata ++ pairs.toMap)

  def build(): T => Token = { value =>
    validators.foreach { v =>
      if (!v(value)) throw new IllegalArgumentException(s"Validation failed for $value")
    }
    val str = normalizers.foldLeft(value.toString)((s, f) => f(s))
    Token(str, TokenType.STRING, metadata)
  }
}

// Companion object provides the public constructor
object TokenizerBuilder {
  def apply[T](): TokenizerBuilder[T] =
    new TokenizerBuilder[T](Nil, Nil, Map.empty)
}

Why immutable?

  • Thread-safe by default — no shared mutable state
  • Enables easy rollback (keep a reference to an old builder)
  • Composable: builders can be passed around without defensive copying

Vararg Metadata

Python uses **kwargs for keyword metadata. In Scala, use (String, Any)* tuple varargs:

builder.withMetadata("type" -> "custom", "version" -> 1)
// Inside: metadata ++ pairs.toMap

Mutable Internal State → ListBuffer / mutable.Map

For cases where mutation is genuinely needed (e.g., MutableTokenBatch), use scala.collection.mutable:

class MutableTokenBatch:
    def __init__(self):
        self.tokens = []
        self._processed = False

    def add(self, token):
        if self._processed:
            raise RuntimeError("Batch already processed")
        self.tokens.append(token)
class MutableTokenBatch {
  private val _tokens     = scala.collection.mutable.ListBuffer.empty[Token]
  private var _processed  = false

  def tokens: List[Token] = _tokens.toList

  def add(token: Token): Unit = {
    if (_processed) throw new RuntimeException("Batch already processed")
    _tokens += token
  }

  def markProcessed(): Unit = { _processed = true }
}

Python dict default argument anti-pattern → Scala safe defaults

# DANGER: mutable default in Python
def __init__(self, format_options: dict = {}): ...

In Scala, default parameter expressions are evaluated fresh each call:

class NumericTokenizer(
  precision:     Int                 = 6,
  formatOptions: Map[String, Any]    = Map.empty   // safe — Map.empty is immutable
)

Build Output as a Function

Python build() returns a Callable. In Scala, the result is a plain function value:

val tokenize: T => Token = builder.build()
val result = tokenize("Hello World")
// or equivalently: builder.build()("Hello World")

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.