PY

python-scala-idioms

Helps developers write idiomatic, functional Scala code when migrating from Python.

Install

mkdir -p .claude/skills/python-scala-idioms && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5767" && unzip -o skill.zip -d .claude/skills/python-scala-idioms && rm skill.zip

Installs to .claude/skills/python-scala-idioms

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Guide for writing idiomatic Scala when translating from Python. Use when the goal is not just syntactic translation but producing clean, idiomatic Scala code. Covers immutability, expression-based style, sealed hierarchies, and common Scala conventions.
253 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Convert mutable Python classes to immutable Scala case classes
  • Replace null checks with Option, Either, or Try types
  • Translate procedural if-else chains into expression-based pattern matching
  • Refactor Python loops into functional collection methods like map and filter
  • Apply Scala naming conventions for variables and constants

How it works

The skill provides a set of transformation patterns that map common Python procedural constructs to Scala's functional and type-safe paradigms.

Inputs & outputs

You give it
Python code snippet
You get back
Idiomatic Scala code equivalent

When to use python-scala-idioms

  • Convert Python list comprehensions to Scala maps
  • Rewrite Python classes as Scala case classes
  • Refactor procedural code to functional Scala

About this skill

Python to Idiomatic Scala Translation

Core Principles

When translating Python to Scala, aim for idiomatic Scala, not literal translation:

  1. Prefer immutability - Use val over var, immutable collections
  2. Expression-based - Everything returns a value, minimize statements
  3. Type safety - Leverage Scala's type system, avoid Any
  4. Pattern matching - Use instead of if-else chains
  5. Avoid null - Use Option, Either, Try

Immutability First

# Python - mutable by default
class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count += 1
        return self.count
// Scala - immutable approach
case class Counter(count: Int = 0) {
  def increment: Counter = copy(count = count + 1)
}

// Usage
val c1 = Counter()
val c2 = c1.increment  // Counter(1)
val c3 = c2.increment  // Counter(2)
// c1 is still Counter(0)

Expression-Based Style

# Python - statement-based
def get_status(code):
    if code == 200:
        status = "OK"
    elif code == 404:
        status = "Not Found"
    else:
        status = "Unknown"
    return status
// Scala - expression-based
def getStatus(code: Int): String = code match {
  case 200 => "OK"
  case 404 => "Not Found"
  case _ => "Unknown"
}

// No intermediate variable, match is an expression

Sealed Hierarchies for Domain Modeling

# Python - loose typing
def process_payment(method: str, amount: float):
    if method == "credit":
        # process credit
        pass
    elif method == "debit":
        # process debit
        pass
    elif method == "crypto":
        # process crypto
        pass
// Scala - sealed trait for exhaustive matching
sealed trait PaymentMethod
case class CreditCard(number: String, expiry: String) extends PaymentMethod
case class DebitCard(number: String) extends PaymentMethod
case class Crypto(walletAddress: String) extends PaymentMethod

def processPayment(method: PaymentMethod, amount: Double): Unit = method match {
  case CreditCard(num, exp) => // process credit
  case DebitCard(num) => // process debit
  case Crypto(addr) => // process crypto
}
// Compiler warns if you miss a case!

Replace Null Checks with Option

# Python
def find_user(id):
    user = db.get(id)
    if user is None:
        return None
    profile = user.get("profile")
    if profile is None:
        return None
    return profile.get("email")
// Scala - Option chaining
def findUser(id: Int): Option[String] = for {
  user <- db.get(id)
  profile <- user.profile
  email <- profile.email
} yield email

// Or with flatMap
def findUser(id: Int): Option[String] =
  db.get(id)
    .flatMap(_.profile)
    .flatMap(_.email)

Prefer Methods on Collections

# Python
result = []
for item in items:
    if item.active:
        result.append(item.value * 2)
// Scala - use collection methods
val result = items
  .filter(_.active)
  .map(_.value * 2)

Avoid Side Effects in Expressions

# Python
items = []
for x in range(10):
    items.append(x * 2)
    print(f"Added {x * 2}")
// Scala - separate side effects
val items = (0 until 10).map(_ * 2).toList
items.foreach(x => println(s"Value: $x"))

// Or use tap for debugging
val items = (0 until 10)
  .map(_ * 2)
  .tapEach(x => println(s"Value: $x"))
  .toList

Use Named Parameters for Clarity

# Python
def create_user(name, email, admin=False, active=True):
    pass

user = create_user("Alice", "[email protected]", admin=True)
// Scala - named parameters work the same
def createUser(
  name: String,
  email: String,
  admin: Boolean = false,
  active: Boolean = true
): User = ???

val user = createUser("Alice", "[email protected]", admin = true)

// Case class with defaults is often better
case class User(
  name: String,
  email: String,
  admin: Boolean = false,
  active: Boolean = true
)

val user = User("Alice", "[email protected]", admin = true)

Scala Naming Conventions

PythonScala
snake_case (variables, functions)camelCase
SCREAMING_SNAKE (constants)CamelCase or PascalCase
PascalCase (classes)PascalCase
_privateprivate keyword
__very_privateprivate[this]
# Python
MAX_RETRY_COUNT = 3
def calculate_total_price(items):
    pass

class ShoppingCart:
    def __init__(self):
        self._items = []
// Scala
val MaxRetryCount = 3  // or final val MAX_RETRY_COUNT
def calculateTotalPrice(items: List[Item]): Double = ???

class ShoppingCart {
  private var items: List[Item] = Nil
}

Avoid Returning Unit

# Python - None return is common
def save_user(user):
    db.save(user)
    # implicit None return
// Scala - consider returning useful information
def saveUser(user: User): Either[Error, UserId] = {
  db.save(user) match {
    case Right(id) => Right(id)
    case Left(err) => Left(err)
  }
}

// Or at minimum, use Try
def saveUser(user: User): Try[Unit] = Try {
  db.save(user)
}

Use Apply for Factory Methods

# Python
class Parser:
    def __init__(self, config):
        self.config = config

    @classmethod
    def default(cls):
        return cls(Config())
// Scala - companion object with apply
class Parser(config: Config)

object Parser {
  def apply(config: Config): Parser = new Parser(config)
  def apply(): Parser = new Parser(Config())
}

// Usage
val parser = Parser()  // Calls apply()
val parser = Parser(customConfig)

Cheat Sheet: Common Transformations

Python PatternIdiomatic Scala
if x is Nonex.isEmpty or pattern match
if x is not Nonex.isDefined or x.nonEmpty
x if x else defaultx.getOrElse(default)
[x for x in xs if p(x)]xs.filter(p)
[f(x) for x in xs]xs.map(f)
any(p(x) for x in xs)xs.exists(p)
all(p(x) for x in xs)xs.forall(p)
next(x for x in xs if p(x), None)xs.find(p)
dict(zip(keys, values))keys.zip(values).toMap
isinstance(x, Type)x.isInstanceOf[Type] or pattern match
try: ... except: ...Try { ... } or pattern match
Mutable accumulator loopfoldLeft / foldRight
for i, x in enumerate(xs)xs.zipWithIndex

Anti-Patterns to Avoid

// DON'T: Use null
val name: String = null  // Bad!

// DO: Use Option
val name: Option[String] = None

// DON'T: Use Any or type casts
val data: Any = getData()
val name = data.asInstanceOf[String]

// DO: Use proper types and pattern matching
sealed trait Data
case class UserData(name: String) extends Data
val data: Data = getData()
data match {
  case UserData(name) => // use name
}

// DON'T: Nested if-else chains
if (x == 1) ... else if (x == 2) ... else if (x == 3) ...

// DO: Pattern matching
x match {
  case 1 => ...
  case 2 => ...
  case 3 => ...
}

// DON'T: var with mutation
var total = 0
for (x <- items) total += x

// DO: fold
val total = items.sum
// or
val total = items.foldLeft(0)(_ + _)

When not to use it

  • When the project requires literal syntactic translation rather than idiomatic Scala
  • When performance constraints strictly forbid the overhead of functional abstractions

Limitations

  • Does not automate full codebase migration
  • Requires manual verification of type safety and logic

How it compares

Unlike a direct syntax-to-syntax translator, this skill enforces Scala-specific idioms like immutability and exhaustive pattern matching.

Compared to similar skills

python-scala-idioms side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
python-scala-idioms (this skill)16moNo flagsIntermediate
software-architecture3336moNo flagsIntermediate
effective-go3239moNo flagsBeginner
solid-principles579moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

software-architecture

davila7

Guide for quality focused software architecture. This skill should be used when users want to write code, design architecture, analyze code, in any case that relates to software development.

333868

effective-go

openshift

Apply Go best practices, idioms, and conventions from golang.org/doc/effective_go. Use when writing, reviewing, or refactoring Go code to ensure idiomatic, clean, and efficient implementations.

323536

solid-principles

SmidigStorm

Enforce SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) in object-oriented design. Use when writing or reviewing classes and modules.

57236

typescript-review

metabase

Review TypeScript and JavaScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing TypeScript/JavaScript code.

39178

react-modernization

wshobson

Upgrade React applications to latest versions, migrate from class components to hooks, and adopt concurrent features. Use when modernizing React codebases, migrating to React Hooks, or upgrading to latest React versions.

21134

ast-grep

ast-grep

Guide for writing ast-grep rules to perform structural code search and analysis. Use when users need to search codebases using Abstract Syntax Tree (AST) patterns, find specific code structures, or perform complex code queries that go beyond simple text search. This skill should be used when users ask to search for code patterns, find specific language constructs, or locate code with particular structural characteristics.

17135

Search skills

Search the agent skills registry