Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Convert Python Scala

ASecurity

Convert Python code to idiomatic Scala. Use when migrating Python projects to Scala, translating Python patterns to Scala's hybrid OOP+FP paradigm, or refactoring Python codebases for type safety, JVM integration, and functional programming. Extends meta-convert-dev with Python-to-Scala specific patterns.

8 stars
0 votes
0 copies
0 views
Added 2/8/2026
developmentpythonjavasqlexpressfastapitestingrefactoringapidatabaseperformance

Works with

cliapi

Security Analysis

A100/100

Scanned 2/10/2026

$npx -y skills add aRustyDev/ai --skill convert-python-scala --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Convert Python Scala?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Convert Python Scala
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-convert-python-scala/badge)](https://www.skillsdirectory.com/skills/arustydev-convert-python-scala)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: convert-python-scala
description: Convert Python code to idiomatic Scala. Use when migrating Python projects to Scala, translating Python patterns to Scala's hybrid OOP+FP paradigm, or refactoring Python codebases for type safety, JVM integration, and functional programming. Extends meta-convert-dev with Python-to-Scala specific patterns.
---

# Convert Python to Scala

Convert Python code to idiomatic Scala. This skill extends `meta-convert-dev` with Python-to-Scala specific type mappings, idiom translations, and tooling for transforming dynamic Python code into Scala's powerful hybrid OOP+FP paradigm on the JVM.

## This Skill Extends

- `meta-convert-dev` - Foundational conversion patterns (APTV workflow, testing strategies)

For general concepts like the Analyze → Plan → Transform → Validate workflow, testing strategies, and common pitfalls, see the meta-skill first.

## This Skill Adds

- **Type mappings**: Python types → Scala types (dynamic → static with inference)
- **Paradigm shift**: Imperative/OOP → Hybrid OOP+FP
- **Error handling**: Exceptions → Option/Either/Try pattern matching
- **Concurrency**: threading/asyncio → Futures/Akka actors
- **Metaprogramming**: Decorators → Scala 3 macros, implicit conversions
- **Serialization**: Pydantic → circe, upickle, case class derivation
- **Module system**: Python packages → Scala packages, objects, imports
- **Build/Deps**: pip/poetry → sbt, mill, build.sbt
- **Testing**: pytest → ScalaTest, MUnit, ScalaCheck
- **Dev Workflow**: Python REPL → Scala REPL, Ammonite, sbt console
- **FFI**: C extensions → JNI, JNA, Scala Native C interop

## This Skill Does NOT Cover

- General conversion methodology - see `meta-convert-dev`
- Python language fundamentals - see `lang-python-dev`
- Scala language fundamentals - see `lang-scala-dev`
- Reverse conversion (Scala → Python) - see `convert-scala-python`
- Advanced Scala libraries (Akka, Cats, ZIO) - see specialized skills

---

## Quick Reference

| Python | Scala | Notes |
|--------|-------|-------|
| `int` | `Int`, `Long`, `BigInt` | Fixed size or arbitrary precision |
| `float` | `Double` | Default floating point |
| `bool` | `Boolean` | Direct mapping |
| `str` | `String` | Java String |
| `list[T]` | `List[T]`, `Vector[T]` | Immutable by default |
| `tuple` | `(T, U, ...)` | Tuple types |
| `dict[K, V]` | `Map[K, V]` | Immutable by default |
| `set[T]` | `Set[T]` | Immutable by default |
| `None` | `Option[T]` / `None` | Explicit optional |
| `Union[T, U]` | `Either[T, U]` | Type-safe union |
| `@dataclass` | `case class` | Data classes with pattern matching |
| `def func()` | `def func(): Unit` | Functions |
| `class` | `class` / `trait` / `case class` | OOP + traits |
| Exception | `Try[T]`, `Either[L, R]` | Functional error handling |
| `async def` | `Future[T]`, `IO[T]` | Async/effect handling |

## When Converting Code

1. **Analyze Python semantics** - understand duck typing, mutable state, exceptions
2. **Embrace immutability** - prefer `val` over `var`, immutable collections
3. **Use case classes** for data - automatic equals, hashCode, copy, pattern matching
4. **Pattern match** instead of type checks and isinstance
5. **Option, Try, Either** for null/error handling, not exceptions
6. **For-comprehensions** for sequential operations (flatMap chains)
7. **Traits and mixins** for composition, not deep inheritance
8. **Type inference** - let compiler infer types where clear
9. **Test equivalence** - same inputs → same outputs
10. **Leverage JVM ecosystem** - vast library support

---

## Type System Mapping

### Primitive Types

| Python | Scala | Notes |
|--------|-------|-------|
| `int` | `Int` | 32-bit signed integer |
| `int` | `Long` | 64-bit signed integer |
| `int` | `BigInt` | Arbitrary precision (like Python) |
| `float` | `Double` | 64-bit IEEE 754 |
| `bool` | `Boolean` | true/false |
| `str` | `String` | Immutable UTF-16 (Java String) |
| `bytes` | `Array[Byte]` | Mutable byte array |
| `bytearray` | `Array[Byte]` | Mutable byte array |
| `None` | `None` (Option) | Type-safe null |

**Integer Precision Note**: Python's `int` has arbitrary precision and never overflows. Scala's `Int` and `Long` are fixed-size. Use `BigInt` for Python-like behavior:

```scala
// Python arbitrary precision
val pythonLike: BigInt = BigInt(10).pow(100)

// Scala fixed-size (can overflow)
val scalaInt: Int = 1000000000  // OK
val overflow: Int = Int.MaxValue + 1  // Wraps around!
```

### Collection Types

| Python | Scala | Mutability | Notes |
|--------|-------|-----------|-------|
| `list[T]` | `List[T]` | Immutable | Linked list, O(1) prepend |
| `list[T]` | `Vector[T]` | Immutable | Better random access |
| `list[T]` | `ArrayBuffer[T]` | Mutable | Like Python list |
| `tuple` | `(T, U, V)` | Immutable | Fixed-size tuples |
| `dict[K, V]` | `Map[K, V]` | Immutable | Hash map |
| `dict[K, V]` | `mutable.Map[K, V]` | Mutable | Like Python dict |
| `set[T]` | `Set[T]` | Immutable | Hash set |
| `set[T]` | `mutable.Set[T]` | Mutable | Like Python set |
| `frozenset[T]` | `Set[T]` | Immutable | Default is immutable |
| `deque` | `Queue[T]` | Mutable | Double-ended queue |

**Collection Philosophy Difference**:

```python
# Python: Mutable by default
my_list = [1, 2, 3]
my_list.append(4)  # In-place mutation
```

```scala
// Scala: Immutable by default
val myList = List(1, 2, 3)
val newList = myList :+ 4  // Creates new list

// Mutable collections require explicit import
import scala.collection.mutable
val buffer = mutable.ArrayBuffer(1, 2, 3)
buffer += 4  // In-place mutation
```

### Composite Types

| Python | Scala | Notes |
|--------|-------|-------|
| `@dataclass` | `case class` | Immutable data with pattern matching |
| `class` | `class` | Mutable objects |
| `class` | `trait` | Interface with implementation |
| `typing.Protocol` | `trait` | Structural types |
| `typing.TypedDict` | `case class` | Named fields with types |
| `typing.NamedTuple` | `case class` | Named tuples |
| `enum.Enum` | `sealed trait` + case objects | ADTs |
| `typing.Literal` | `sealed trait` | Literal types |
| `typing.Union[T, U]` | `Either[T, U]` | Type-safe union |
| `typing.Optional[T]` | `Option[T]` | Nullable handling |
| `typing.Callable` | `Function1[A, B]` | Function types |
| `typing.Generic[T]` | Type parameters `[T]` | Generic types |

### Type Annotations → Type Parameters + Bounds

| Python | Scala | Notes |
|--------|-------|-------|
| `def f(x: T) -> T` | `def f[T](x: T): T` | Generic function |
| `x: Iterable[T]` | `x: Iterable[T]` | Scala has Iterable |
| `x: Sequence[T]` | `x: Seq[T]` | Sequence trait |
| `x: Any` | `x: Any` | Top type (avoid) |
| `x: object` | `x: AnyRef` | Reference types |
| `T extends Base` | `T <: Base` | Upper type bound |
| `T` (unconstrained) | `T` | Type parameter |

---

## APTV Workflow for Python → Scala

### 1. Analyze Phase

Before converting, understand Python-specific features:

```python
# Analyze this Python code
class UserService:
    def __init__(self, db):
        self.db = db
        self.cache = {}  # Mutable state

    def get_user(self, user_id):
        if user_id in self.cache:
            return self.cache[user_id]

        user = self.db.query(f"SELECT * FROM users WHERE id={user_id}")
        if user is None:
            raise UserNotFoundError(f"User {user_id} not found")

        self.cache[user_id] = user
        return user
```

**Key observations**:
- Mutable instance state (`self.cache`)
- None checking
- Exception-based error handling
- String interpolation
- Duck typing (db assumed to have .query())

### 2. Plan Phase

Design Scala equivalent:

```scala
// Type mappings
// Python dict -> Map[Int, User]
// Python None -> Option[User]
// Exception -> Either[UserError, User] or Try[User]

// Architecture decisions:
// - Immutable service with explicit cache parameter
// - Or use functional cache (State monad, cats.effect.Ref)
// - Database interface as trait
// - Error as ADT (sealed trait)
```

### 3. Transform Phase

Idiomatic Scala implementation:

```scala
// Define error ADT
sealed trait UserError
case class UserNotFound(userId: Int) extends UserError
case class DatabaseError(message: String) extends UserError

// Database trait (replaces duck typing)
trait Database {
  def query(sql: String): Option[User]
}

// Immutable service
class UserService(db: Database) {
  // Cache as parameter for immutability
  def getUser(userId: Int, cache: Map[Int, User]): Either[UserError, (User, Map[Int, User])] = {
    cache.get(userId) match {
      case Some(user) =>
        Right((user, cache))

      case None =>
        db.query(s"SELECT * FROM users WHERE id=$userId") match {
          case Some(user) =>
            Right((user, cache + (userId -> user)))

          case None =>
            Left(UserNotFound(userId))
        }
    }
  }
}

// Or with functional effects (cats-effect)
import cats.effect.IO
import cats.effect.Ref

class UserServiceF(db: Database, cache: Ref[IO, Map[Int, User]]) {
  def getUser(userId: Int): IO[Either[UserError, User]] = {
    for {
      cachedUser <- cache.get.map(_.get(userId))
      result <- cachedUser match {
        case Some(user) =>
          IO.pure(Right(user))

        case None =>
          db.query(s"SELECT * FROM users WHERE id=$userId") match {
            case Some(user) =>
              cache.update(_ + (userId -> user)).as(Right(user))
            case None =>
              IO.pure(Left(UserNotFound(userId)))
          }
      }
    } yield result
  }
}
```

### 4. Validate Phase

Test equivalence:

```scala
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class UserServiceSpec extends AnyFlatSpec with Matchers {
  "UserService" should "return cached user" in {
    val mockDb = new Database {
      def query(sql: String): Option[User] = None
    }

    val service = new UserService(mockDb)
    val user = User(1, "Alice")
    val cache = Map(1 -> user)

    val Right((result, _)) = service.getUser(1, cache)
    result should be(user)
  }

  it should "query database when not cached" in {
    val user = User(2, "Bob")
    val mockDb = new Database {
      def query(sql: String): Option[User] = Some(user)
    }

    val service = new UserService(mockDb)
    val Right((result, newCache)) = service.getUser(2, Map.empty)

    result should be(user)
    newCache should contain key 2
  }

  it should "return error when user not found" in {
    val mockDb = new Database {
      def query(sql: String): Option[User] = None
    }

    val service = new UserService(mockDb)
    val Left(error) = service.getUser(999, Map.empty)

    error should be(UserNotFound(999))
  }
}
```

---

## Idiom Translation Patterns

### Pattern 1: None/Null Handling → Option

**Python:**
```python
# Optional chaining
user = get_user(user_id)
name = user.name if user else "Anonymous"

# Or with walrus operator
if user := get_user(user_id):
    process(user)
else:
    log_error("User not found")

# get with default
config_value = config.get("timeout", 30)
```

**Scala:**
```scala
// Option combinators
val name = getUser(userId)
  .map(_.name)
  .getOrElse("Anonymous")

// Pattern matching
getUser(userId) match {
  case Some(user) => process(user)
  case None => logError("User not found")
}

// For-comprehension (flatMap chain)
val fullName = for {
  user <- getUser(userId)
  profile <- user.profile
} yield s"${profile.firstName} ${profile.lastName}"

// getOrElse with default
val configValue = config.get("timeout").getOrElse(30)
```

**Why this translation:**
- Python uses truthiness and None; Scala uses explicit Option[T]
- Scala's Option provides safe combinators (map, flatMap, getOrElse, fold)
- Pattern matching makes intent explicit
- For-comprehensions chain multiple Option operations elegantly

### Pattern 2: List Comprehensions → For-Comprehensions

**Python:**
```python
# List comprehension
squared_evens = [x * x for x in numbers if x % 2 == 0]

# Nested comprehension
pairs = [(x, y) for x in xs for y in ys if x + y > 10]

# Generator expression
total = sum(x * x for x in numbers if x % 2 == 0)
```

**Scala:**
```scala
// For-comprehension with yield
val squaredEvens = for {
  x <- numbers
  if x % 2 == 0
} yield x * x

// Nested for-comprehension
val pairs = for {
  x <- xs
  y <- ys
  if x + y > 10
} yield (x, y)

// Method chaining (more idiomatic for simple cases)
val total = numbers.filter(_ % 2 == 0).map(x => x * x).sum

// Lazy evaluation with View
val lazyResult = numbers.view
  .filter(_ % 2 == 0)
  .map(x => x * x)
  .force  // Materialize
```

**Why this translation:**
- Python comprehensions are eager; Scala for-comprehensions desugar to map/flatMap
- Scala provides both for-comprehension and method chaining styles
- View provides lazy evaluation similar to Python generators
- For-comprehensions work with any type supporting map/flatMap (monads)

### Pattern 3: Dictionary Operations → Map Operations

**Python:**
```python
# Dictionary creation
user_ages = {user.name: user.age for user in users}

# Get with default
timeout = config.get("timeout", 30)

# Update (mutation)
cache[key] = value

# Merge dictionaries
combined = {**dict1, **dict2}
```

**Scala:**
```scala
// Map creation
val userAges = users.map(user => user.name -> user.age).toMap
// Or with for-comprehension
val userAges = (for {
  user <- users
} yield user.name -> user.age).toMap

// Get with default
val timeout = config.getOrElse("timeout", 30)

// Immutable update (creates new Map)
val newCache = cache + (key -> value)

// Mutable update
import scala.collection.mutable
val mutableCache = mutable.Map[String, Int]()
mutableCache(key) = value

// Merge maps
val combined = dict1 ++ dict2  // Right side wins on conflicts
```

**Why this translation:**
- Scala Map is immutable by default (Python dict is mutable)
- `+` operator for adding entries creates new Map
- `++` operator merges Maps
- Mutable Map available but requires explicit import
- getOrElse instead of .get() with default

### Pattern 4: Exception Handling → Try/Either/Option

**Python:**
```python
# Try-except
try:
    result = risky_operation()
except ValueError as e:
    logger.error(f"Invalid value: {e}")
    result = default_value

# Exception chaining
try:
    data = fetch_data(url)
except NetworkError as e:
    raise ProcessingError(f"Failed to fetch {url}") from e
```

**Scala:**
```scala
// Try monad
import scala.util.{Try, Success, Failure}

val result: Try[Result] = Try {
  riskyOperation()
}.recover {
  case e: IllegalArgumentException =>
    logger.error(s"Invalid value: $e")
    defaultValue
}

// Pattern matching on Try
Try(riskyOperation()) match {
  case Success(value) => processSuccess(value)
  case Failure(exception) => handleError(exception)
}

// Either for domain errors
def fetchData(url: String): Either[ProcessingError, Data] = {
  // Returns Left for error, Right for success
  fetchNetwork(url).left.map { networkError =>
    ProcessingError(s"Failed to fetch $url", networkError)
  }
}

// For-comprehension with Either
val result: Either[Error, Result] = for {
  data <- fetchData(url)
  parsed <- parseData(data)
  validated <- validateData(parsed)
} yield validated
// Short-circuits on first Left (error)
```

**Why this translation:**
- Python uses exceptions for control flow; Scala prefers functional error handling
- Try wraps code that might throw exceptions
- Either provides left/right semantics (Left = error, Right = success)
- For-comprehensions with Try/Either short-circuit on failure
- Scala still supports exceptions but discourages them for expected errors

### Pattern 5: Duck Typing → Traits and Structural Types

**Python:**
```python
# Duck typing - if it quacks like a duck...
def process_file_like(file_like):
    data = file_like.read()  # Any object with .read()
    return parse(data)

# Works with files, StringIO, BytesIO, custom objects
```

**Scala:**
```scala
// Trait-based (nominal typing)
trait Readable {
  def read(): String
}

def processFilelike(readable: Readable): ParsedData = {
  val data = readable.read()
  parse(data)
}

// Structural typing (duck typing in Scala)
import scala.language.reflectiveCalls

type FilelikeType = {
  def read(): String
}

def processFilelikeStructural(fileLike: FilelikeType): ParsedData = {
  val data = fileLike.read()  // Calls via reflection
  parse(data)
}

// Type class pattern (more idiomatic)
trait Readable[T] {
  def read(t: T): String
}

def processFilelikeTC[T](t: T)(implicit readable: Readable[T]): ParsedData = {
  val data = readable.read(t)
  parse(data)
}

// Implicit instances
implicit val fileReadable: Readable[File] = new Readable[File] {
  def read(f: File): String = scala.io.Source.fromFile(f).mkString
}
```

**Why this translation:**
- Python uses runtime duck typing; Scala uses compile-time traits
- Structural types exist but use reflection (slower)
- Type classes provide extensibility without modifying original types
- Traits are the idiomatic Scala approach

### Pattern 6: Decorators → Macros and Implicit Conversions

**Python:**
```python
# Function decorator
@cache
@log_calls
def expensive_function(x):
    return compute(x)

# Class decorator
@dataclass
class Point:
    x: int
    y: int

# Property decorator
class Circle:
    @property
    def area(self):
        return 3.14 * self.radius ** 2
```

**Scala:**
```scala
// Macros for compile-time code generation (Scala 3)
import scala.annotation.experimental
import scala.quoted.*

// Annotation-based (similar to decorators)
@cached
@logged
def expensiveFunction(x: Int): Int = compute(x)

// Case class (similar to @dataclass)
case class Point(x: Int, y: Int)
// Automatically provides: equals, hashCode, toString, copy, pattern matching

// Property-like access (no decorator needed)
class Circle(val radius: Double) {
  def area: Double = Math.PI * radius * radius
  // Called as circle.area (no parens)
}

// Transparent inline for inlining (Scala 3)
transparent inline def logged[T](inline expr: T): T = {
  println(s"Calling function")
  expr
}

// Or use higher-order functions
def withLogging[T](name: String)(f: => T): T = {
  println(s"Calling $name")
  val result = f
  println(s"Finished $name")
  result
}

// Usage
val result = withLogging("expensive") {
  expensiveFunction(42)
}
```

**Why this translation:**
- Python decorators are runtime; Scala 3 inline/macros are compile-time
- Case classes provide dataclass-like functionality built-in
- Properties in Scala are methods without side effects (no parens)
- Higher-order functions can replace many decorator patterns
- Scala 2 macros are complex; Scala 3 improves this significantly

### Pattern 7: Async/Await → Futures and Effect Systems

**Python:**
```python
import asyncio

# Async function
async def fetch_user(user_id):
    async with httpx.AsyncClient() as client:
        response = await client.get(f"/users/{user_id}")
        return User(**response.json())

# Gather multiple async operations
async def fetch_all():
    users, orders = await asyncio.gather(
        fetch_users(),
        fetch_orders()
    )
    return users, orders
```

**Scala:**
```scala
import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global

// Future-based (standard library)
def fetchUser(userId: Int)(implicit ec: ExecutionContext): Future[User] = {
  val client = HttpClient()
  client.get(s"/users/$userId").map { response =>
    parseUser(response.json())
  }
}

// Combine multiple Futures
def fetchAll()(implicit ec: ExecutionContext): Future[(List[User], List[Order])] = {
  for {
    users <- fetchUsers()
    orders <- fetchOrders()
  } yield (users, orders)
}

// Or with Future.sequence
val futures = List(fetchUser(1), fetchUser(2), fetchUser(3))
val combined: Future[List[User]] = Future.sequence(futures)

// With Cats Effect IO (more powerful)
import cats.effect.IO
import cats.implicits._

def fetchUserIO(userId: Int): IO[User] = {
  for {
    response <- httpClient.get(s"/users/$userId")
    user <- IO.fromEither(parseUser(response.json()))
  } yield user
}

// Parallel execution
import cats.syntax.parallel._

val usersIO: IO[List[User]] = List(1, 2, 3).parTraverse(fetchUserIO)
```

**Why this translation:**
- Python's asyncio is cooperative; Scala Futures run on thread pools
- Futures are eager (start immediately); cats-effect IO is lazy
- For-comprehensions sequence async operations
- Cats-effect provides more control over parallelism and resource safety

### Pattern 8: Multiple Inheritance → Traits and Mixins

**Python:**
```python
class Loggable:
    def log(self, message):
        print(f"[LOG] {message}")

class Cacheable:
    def __init__(self):
        self.cache = {}

    def get_cached(self, key):
        return self.cache.get(key)

class UserService(Loggable, Cacheable):
    def __init__(self):
        super().__init__()

    def process(self):
        self.log("Processing")
        return self.get_cached("data")
```

**Scala:**
```scala
// Traits with implementation
trait Loggable {
  def log(message: String): Unit = {
    println(s"[LOG] $message")
  }
}

trait Cacheable {
  private var cache: Map[String, Any] = Map.empty

  def getCached(key: String): Option[Any] = cache.get(key)

  def putCached(key: String, value: Any): Unit = {
    cache = cache + (key -> value)
  }
}

// Mix in traits
class UserService extends Loggable with Cacheable {
  def process(): Option[Any] = {
    log("Processing")
    getCached("data")
  }
}

// Trait linearization
trait A {
  def msg = "A"
}

trait B extends A {
  override def msg = "B" + super.msg
}

trait C extends A {
  override def msg = "C" + super.msg
}

// Linearization: D -> C -> B -> A
class D extends B with C
val d = new D
println(d.msg)  // "CBA"
```

**Why this translation:**
- Python's multiple inheritance can be complex (MRO)
- Scala traits provide cleaner composition with linearization
- Traits can have state (unlike Java interfaces)
- Mixin composition is more explicit and predictable
- Self-types enforce dependencies between traits

### Pattern 9: Dynamic Attributes → Sealed Traits or Maps

**Python:**
```python
# Dynamic attribute access
class DynamicObject:
    def __init__(self):
        self.attrs = {}

    def __getattr__(self, name):
        return self.attrs.get(name)

    def __setattr__(self, name, value):
        if name == 'attrs':
            super().__setattr__(name, value)
        else:
            self.attrs[name] = value

obj = DynamicObject()
obj.custom_field = "value"  # Dynamic!
```

**Scala:**
```scala
// Static approach with sealed trait
sealed trait Field
case class StringField(value: String) extends Field
case class IntField(value: Int) extends Field
case class BoolField(value: Boolean) extends Field

class StaticObject {
  private var fields: Map[String, Field] = Map.empty

  def get(name: String): Option[Field] = fields.get(name)

  def set(name: String, field: Field): Unit = {
    fields = fields + (name -> field)
  }
}

// Dynamic approach with Map
class DynamicObject {
  private var attrs: Map[String, Any] = Map.empty

  def apply(name: String): Option[Any] = attrs.get(name)

  def update(name: String, value: Any): Unit = {
    attrs = attrs + (name -> value)
  }
}

val obj = new DynamicObject
obj("customField") = "value"
println(obj("customField"))  // Some(value)

// Or use Dynamic trait (compile-time rewriting)
import scala.language.dynamics

class FlexibleObject extends Dynamic {
  private var fields: Map[String, Any] = Map.empty

  def selectDynamic(name: String): Any = fields(name)

  def updateDynamic(name: String)(value: Any): Unit = {
    fields = fields + (name -> value)
  }
}

// Usage looks dynamic but is compile-time checked
val flex = new FlexibleObject
flex.customField = "value"  // Translates to updateDynamic
println(flex.customField)   // Translates to selectDynamic
```

**Why this translation:**
- Python allows true runtime attribute creation
- Scala prefers static typing with sealed traits for known variants
- Map-based approach for truly dynamic data
- Dynamic trait provides syntax similar to Python but with compile-time checking
- Trade flexibility for type safety and performance

### Pattern 10: Context Managers → Try-with-resources / Loan Pattern

**Python:**
```python
# Context manager protocol
with open("data.txt") as f:
    data = f.read()
# File automatically closed

# Custom context manager
from contextlib import contextmanager

@contextmanager
def database_transaction(conn):
    conn.begin()
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise

with database_transaction(conn) as tx:
    tx.execute("INSERT ...")
```

**Scala:**
```scala
// Try-with-resources (Scala 2.13+)
import scala.util.Using

val result: Try[String] = Using(Source.fromFile("data.txt")) { source =>
  source.mkString
}
// Source automatically closed

// Loan pattern (functional resource management)
def withResource[R <: AutoCloseable, T](resource: => R)(f: R => T): T = {
  var res: R = null.asInstanceOf[R]
  try {
    res = resource
    f(res)
  } finally {
    if (res != null) res.close()
  }
}

// Usage
val data = withResource(Source.fromFile("data.txt")) { source =>
  source.mkString
}

// Custom resource management
def withTransaction[T](conn: Connection)(f: Connection => T): Try[T] = Try {
  conn.setAutoCommit(false)
  try {
    val result = f(conn)
    conn.commit()
    result
  } catch {
    case e: Exception =>
      conn.rollback()
      throw e
  }
}

withTransaction(conn) { implicit tx =>
  executeQuery("INSERT ...")
}

// With cats-effect Resource
import cats.effect.{IO, Resource}

def acquireConnection: IO[Connection] = IO(/* ... */)
def releaseConnection(conn: Connection): IO[Unit] = IO(conn.close())

val connectionResource: Resource[IO, Connection] = Resource.make(
  acquire = acquireConnection
)(release = releaseConnection)

val program: IO[Result] = connectionResource.use { conn =>
  IO {
    conn.executeQuery("SELECT ...")
  }
}
```

**Why this translation:**
- Python's `with` statement uses `__enter__`/`__exit__` protocol
- Scala uses try-finally, Using, or functional patterns
- Loan pattern captures the context manager pattern functionally
- cats-effect Resource provides composable resource management
- Scala's type system ensures resources are properly released

---

## Module System Translation

### Python Package Structure

```python
# Python package layout
myproject/
├── mypackage/
│   ├── __init__.py
│   ├── module1.py
│   ├── module2.py
│   └── subpackage/
│       ├── __init__.py
│       └── module3.py
└── setup.py

# Imports
from mypackage import module1
from mypackage.subpackage import module3
from mypackage.module1 import SomeClass
```

### Scala Package Structure

```scala
// Scala package layout
myproject/
├── src/main/scala/
│   └── com/mycompany/myproject/
│       ├── Module1.scala
│       ├── Module2.scala
│       └── subpackage/
│           └── Module3.scala
└── build.sbt

// Package declarations (Module1.scala)
package com.mycompany.myproject

class SomeClass {
  // ...
}

// Imports
import com.mycompany.myproject.Module1
import com.mycompany.myproject.subpackage.Module3
import com.mycompany.myproject.Module1.SomeClass

// Wildcard import
import com.mycompany.myproject._  // Like Python's *

// Selective imports
import com.mycompany.myproject.{Module1, Module2}

// Rename on import
import com.mycompany.myproject.{Module1 => M1}
```

**Key Differences**:

| Python | Scala | Notes |
|--------|-------|-------|
| `__init__.py` required | No init file needed | Package defined by directory |
| Relative imports common | Absolute imports preferred | Explicit package paths |
| `from X import *` | `import X._` | Wildcard imports |
| `import X as Y` | `import X as Y` (2.x) or `import X as Y` (3.x) | Import aliasing |

### Scala Objects as Module Singletons

```python
# Python module-level functions and constants
# mymodule.py
DEFAULT_TIMEOUT = 30

def connect(host, port):
    return Connection(host, port)

# Usage
import mymodule
conn = mymodule.connect("localhost", 8080)
```

```scala
// Scala object for singleton module
package com.mycompany.myproject

object MyModule {
  val DefaultTimeout: Int = 30

  def connect(host: String, port: Int): Connection = {
    new Connection(host, port)
  }
}

// Usage
import com.mycompany.myproject.MyModule
val conn = MyModule.connect("localhost", 8080)

// Or with wildcard import
import com.mycompany.myproject.MyModule._
val conn = connect("localhost", 8080)
```

---

## Error Handling Translation

### Exception Hierarchy → ADTs

**Python:**
```python
class AppError(Exception):
    pass

class NotFoundError(AppError):
    def __init__(self, resource, resource_id):
        self.resource = resource
        self.resource_id = resource_id
        super().__init__(f"{resource} {resource_id} not found")

class ValidationError(AppError):
    def __init__(self, field, message):
        self.field = field
        super().__init__(f"{field}: {message}")

# Usage
def get_user(user_id):
    user = db.find(user_id)
    if not user:
        raise NotFoundError("User", user_id)
    return user

try:
    user = get_user(123)
except NotFoundError as e:
    print(f"Error: {e}")
```

**Scala:**
```scala
// Sealed trait for error ADT
sealed trait AppError
case class NotFoundError(resource: String, resourceId: Int) extends AppError
case class ValidationError(field: String, message: String) extends AppError
case class DatabaseError(cause: Throwable) extends AppError

// Either-based error handling
def getUser(userId: Int): Either[AppError, User] = {
  db.find(userId) match {
    case Some(user) => Right(user)
    case None => Left(NotFoundError("User", userId))
  }
}

// Pattern matching on errors
getUser(123) match {
  case Right(user) => println(s"Found: $user")
  case Left(NotFoundError(resource, id)) =>
    println(s"Error: $resource $id not found")
  case Left(ValidationError(field, msg)) =>
    println(s"Error: $field: $msg")
  case Left(err) =>
    println(s"Error: $err")
}

// For-comprehension error chaining
val result: Either[AppError, Result] = for {
  user <- getUser(userId)
  profile <- getProfile(user.profileId)
  settings <- getSettings(user.settingsId)
} yield Result(user, profile, settings)
// Short-circuits on first Left
```

**Why this translation:**
- Python exception hierarchies → Scala sealed trait ADTs
- Exceptions for control flow → Either/Try for expected errors
- Pattern matching provides exhaustive error handling
- Compiler ensures all error cases are handled
- For-comprehensions elegantly chain error-producing operations

---

## Serialization Patterns

### Pydantic → Case Classes with Circe

**Python (Pydantic):**
```python
from pydantic import BaseModel, Field, field_validator
from typing import Optional

class User(BaseModel):
    id: int
    name: str = Field(min_length=1, max_length=100)
    email: Optional[str] = None
    age: int = Field(ge=0, le=150)

    @field_validator('email')
    @classmethod
    def validate_email(cls, v):
        if v and '@' not in v:
            raise ValueError('Invalid email')
        return v

# Serialization
user = User(id=1, name="Alice", age=30)
json_str = user.model_dump_json()

# Deserialization with validation
user = User.model_validate_json(json_str)
```

**Scala (Circe):**
```scala
import io.circe._
import io.circe.generic.semiauto._
import io.circe.syntax._

// Case class definition
case class User(
  id: Int,
  name: String,
  email: Option[String],
  age: Int
)

// Validation separate from data structure
object User {
  def validate(user: User): Either[List[String], User] = {
    val errors = List.newBuilder[String]

    if (user.name.length < 1 || user.name.length > 100)
      errors += "name must be between 1 and 100 characters"

    if (user.age < 0 || user.age > 150)
      errors += "age must be between 0 and 150"

    user.email.foreach { email =>
      if (!email.contains('@'))
        errors += "invalid email"
    }

    val errorList = errors.result()
    if (errorList.isEmpty) Right(user)
    else Left(errorList)
  }
}

// Automatic codec derivation
implicit val userDecoder: Decoder[User] = deriveDecoder
implicit val userEncoder: Encoder[User] = deriveEncoder

// Serialization
val user = User(1, "Alice", None, 30)
val json: Json = user.asJson
val jsonString: String = json.noSpaces

// Deserialization
val decoded: Either[Error, User] = decode[User](jsonString)

// With validation
val validated: Either[List[String], User] = decoded.flatMap(User.validate)

// Or use refined types for compile-time validation
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.numeric._
import eu.timepit.refined.string._
import eu.timepit.refined.collection._

type NameType = String Refined (MinSize[1] And MaxSize[100])
type AgeType = Int Refined Interval.Closed[0, 150]
type EmailType = String Refined MatchesRegex["^.+@.+$"]

case class ValidatedUser(
  id: Int,
  name: NameType,
  email: Option[EmailType],
  age: AgeType
)
```

**Why this translation:**
- Pydantic combines data structure and validation; Scala separates concerns
- Circe provides automatic codec derivation for case classes
- Refined types provide compile-time validation
- Scala validation is more verbose but more flexible
- Libraries like cats-validation provide more sophisticated validation

---

## Concurrency Patterns

### Threading/Asyncio → Futures/Akka

**Python (asyncio):**
```python
import asyncio

async def fetch_data(url):
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

async def process_all(urls):
    tasks = [fetch_data(url) for url in urls]
    results = await asyncio.gather(*tasks)
    return results

# Run
results = asyncio.run(process_all(urls))
```

**Scala (Futures):**
```scala
import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global

def fetchData(url: String)(implicit ec: ExecutionContext): Future[Json] = {
  httpClient.get(url).map(_.json)
}

def processAll(urls: List[String])(implicit ec: ExecutionContext): Future[List[Json]] = {
  val futures = urls.map(fetchData)
  Future.sequence(futures)
}

// Run
import scala.concurrent.Await
import scala.concurrent.duration._

val results = Await.result(processAll(urls), 30.seconds)
```

**Scala (Akka Actors):**
```scala
import akka.actor.{Actor, ActorSystem, Props}

// Actor for handling requests
class FetchActor extends Actor {
  def receive: Receive = {
    case FetchData(url) =>
      val result = fetchDataSync(url)
      sender() ! DataResult(result)
  }
}

case class FetchData(url: String)
case class DataResult(data: Json)

// Create actor system
val system = ActorSystem("FetchSystem")
val fetchActor = system.actorOf(Props[FetchActor], "fetchActor")

// Send messages
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._

implicit val timeout: Timeout = Timeout(30.seconds)

val results: Future[List[DataResult]] = Future.sequence(
  urls.map { url =>
    (fetchActor ? FetchData(url)).mapTo[DataResult]
  }
)
```

**Why this translation:**
- Python's asyncio is single-threaded; Scala Futures use thread pools
- Futures are eager and composable via map/flatMap
- Akka actors provide message-based concurrency with fault tolerance
- Actors isolate state and communicate via messages (like Erlang)

---

## Build System & Dependencies

### pip/poetry → sbt/mill

**Python (pyproject.toml):**
```toml
[tool.poetry]
name = "myproject"
version = "0.1.0"
description = "My Python project"

[tool.poetry.dependencies]
python = "^3.11"
pydantic = "^2.0"
httpx = "^0.25"

[tool.poetry.dev-dependencies]
pytest = "^7.0"
mypy = "^1.0"
```

**Scala (build.sbt):**
```scala
name := "myproject"
version := "0.1.0"
scalaVersion := "3.3.1"

// Dependencies
libraryDependencies ++= Seq(
  "io.circe" %% "circe-core" % "0.14.6",
  "io.circe" %% "circe-generic" % "0.14.6",
  "io.circe" %% "circe-parser" % "0.14.6",
  "com.softwaremill.sttp.client3" %% "core" % "3.9.1",

  // Test dependencies
  "org.scalatest" %% "scalatest" % "3.2.17" % Test,
  "org.scalatestplus" %% "scalacheck-1-17" % "3.2.17.0" % Test
)

// Compiler options
scalacOptions ++= Seq(
  "-deprecation",
  "-feature",
  "-unchecked",
  "-Xfatal-warnings"
)
```

**Dependency Translation Table:**

| Python Package | Scala Equivalent | Purpose |
|----------------|------------------|---------|
| `requests`, `httpx` | `sttp`, `akka-http` | HTTP clients |
| `pydantic` | `circe`, `upickle` | JSON serialization |
| `fastapi` | `http4s`, `akka-http` | Web frameworks |
| `pytest` | `scalatest`, `munit` | Testing |
| `mypy` | `scalac` (built-in) | Type checking |
| `black` | `scalafmt` | Code formatting |
| `pylint` | `scalafix`, `wartremover` | Linting |
| `click` | `decline`, `scopt` | CLI parsing |

---

## Testing Strategy

### pytest → ScalaTest/ScalaCheck

**Python (pytest):**
```python
import pytest
from myapp import UserService

@pytest.fixture
def user_service():
    return UserService(db=MockDatabase())

def test_get_existing_user(user_service):
    user = user_service.get_user(1)
    assert user is not None
    assert user.name == "Alice"

def test_get_missing_user(user_service):
    with pytest.raises(NotFoundError):
        user_service.get_user(999)

@pytest.mark.parametrize("user_id,expected_name", [
    (1, "Alice"),
    (2, "Bob"),
    (3, "Charlie"),
])
def test_multiple_users(user_service, user_id, expected_name):
    user = user_service.get_user(user_id)
    assert user.name == expected_name

# Property-based testing
from hypothesis import given
import hypothesis.strategies as st

@given(st.integers(min_value=1, max_value=1000))
def test_user_id_valid_range(user_id):
    # Property: all valid IDs should not raise exception
    try:
        get_user(user_id)
    except NotFoundError:
        pass  # OK if not found
    except Exception as e:
        pytest.fail(f"Unexpected exception: {e}")
```

**Scala (ScalaTest + ScalaCheck):**
```scala
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks

class UserServiceSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks {

  // Fixture
  def userService: UserService = new UserService(new MockDatabase())

  "UserService" should "return existing user" in {
    val service = userService
    val user = service.getUser(1)

    user should not be empty
    user.get.name shouldBe "Alice"
  }

  it should "return None for missing user" in {
    val service = userService
    val user = service.getUser(999)

    user shouldBe None
  }

  // Or with Either/Try
  it should "return Left for missing user" in {
    val service = userService
    val result = service.getUserEither(999)

    result shouldBe a[Left[_, _]]
    result.left.get shouldBe NotFoundError("User", 999)
  }

  // Table-driven tests
  it should "return correct names for multiple users" in {
    val service = userService
    val testCases = Table(
      ("userId", "expectedName"),
      (1, "Alice"),
      (2, "Bob"),
      (3, "Charlie")
    )

    forAll(testCases) { (userId, expectedName) =>
      val user = service.getUser(userId)
      user.get.name shouldBe expectedName
    }
  }

  // Property-based testing with ScalaCheck
  it should "handle all valid user IDs without exception" in {
    forAll(Gen.choose(1, 1000)) { userId =>
      val service = userService
      noException should be thrownBy {
        service.getUser(userId)
      }
    }
  }

  // Property: round-trip serialization
  it should "round-trip serialize users" in {
    forAll(genUser) { user =>
      val json = user.asJson
      val decoded = decode[User](json.noSpaces)

      decoded shouldBe Right(user)
    }
  }
}

// Custom generators
object UserGenerators {
  import org.scalacheck.Gen

  val genUser: Gen[User] = for {
    id <- Gen.posNum[Int]
    name <- Gen.alphaNumStr.suchThat(_.nonEmpty)
    age <- Gen.choose(0, 150)
    email <- Gen.option(genEmail)
  } yield User(id, name, email, age)

  val genEmail: Gen[String] = for {
    user <- Gen.alphaNumStr.suchThat(_.nonEmpty)
    domain <- Gen.alphaNumStr.suchThat(_.nonEmpty)
  } yield s"$user@$domain.com"
}
```

**Why this translation:**
- pytest fixtures → ScalaTest traits or def methods
- pytest.raises → should/shouldBe matchers
- pytest.mark.parametrize → Table-driven tests
- Hypothesis → ScalaCheck for property-based testing
- Scala's type system catches more errors at compile time

---

## Dev Workflow & REPL

### Python REPL → Scala REPL/Ammonite

**Python REPL:**
```python
$ python
>>> from myapp import User
>>> user = User(id=1, name="Alice", age=30)
>>> user.greet()
'Hello, Alice!'
>>> user.age += 1
>>> user.age
31
```

**Scala REPL:**
```scala
$ scala
scala> import com.mycompany.myapp.User
scala> val user = User(id=1, name="Alice", age=30)
val user: User = User(1,Alice,None,30)

scala> user.greet()
val res0: String = Hello, Alice!

scala> val olderUser = user.copy(age = user.age + 1)
val olderUser: User = User(1,Alice,None,31)

scala> olderUser.age
val res1: Int = 31
```

**sbt console:**
```scala
$ sbt console
[info] Starting scala interpreter...

scala> import com.mycompany.myapp._
scala> // Full project classpath available
```

**Ammonite (enhanced REPL):**
```scala
$ amm
@ import $ivy.`io.circe::circe-core:0.14.6`
@ import io.circe._, io.circe.syntax._
@ case class User(id: Int, name: String)
@ val user = User(1, "Alice")
@ user.asJson
res: Json = {"id": 1, "name": "Alice"}

// Script files with dependencies
// user-service.sc
import $ivy.`com.softwaremill.sttp.client3::core:3.9.1`

case class User(id: Int, name: String)

def fetchUser(id: Int): User = {
  // Implementation
}
```

**Scala Notebooks:**
```scala
// Almond (Jupyter kernel for Scala)
// Can use in Jupyter notebooks for data exploration

// Scala CLI for quick scripts
$ scala-cli run script.scala
```

**Key Differences**:

| Python | Scala | Notes |
|--------|-------|-------|
| `python` REPL | `scala` REPL | Basic REPL |
| IPython | Ammonite | Enhanced REPL |
| Jupyter | Almond (Jupyter) | Notebooks |
| `python -m` | sbt console | Project context |
| Scripts run directly | Need compilation or scala-cli | Compilation step |

---

## FFI & Interoperability

### Python C Extensions → JNI/JNA/Scala Native

**Python (ctypes):**
```python
from ctypes import CDLL, c_int, c_char_p

# Load C library
lib = CDLL('./mylib.so')

# Define function signature
lib.process_data.argtypes = [c_char_p, c_int]
lib.process_data.restype = c_int

# Call C function
result = lib.process_data(b"data", 100)
```

**Scala (JNA):**
```scala
import com.sun.jna.{Library, Native}

// Define interface matching C library
trait MyLib extends Library {
  def process_data(data: String, size: Int): Int
}

// Load library
val lib = Native.load("mylib", classOf[MyLib])

// Call function
val result = lib.process_data("data", 100)
```

**Scala Native (direct C interop):**
```scala
import scala.scalanative.unsafe._
import scala.scalanative.unsigned._

@extern
object MyLib {
  def process_data(data: CString, size: CInt): CInt = extern
}

// Usage
Zone { implicit z =>
  val result = MyLib.process_data(c"data", 100)
}
```

**Python → Scala Gradual Migration:**

1. **Phase 1**: Identify Python modules to convert
2. **Phase 2**: Create Scala equivalents
3. **Phase 3**: Use Py4J or Jython for interop during migration
4. **Phase 4**: Complete migration, remove Python dependencies

**Py4J (Python calling Scala/JVM):**

```python
# Python side
from py4j.java_gateway import JavaGateway

gateway = JavaGateway()
user_service = gateway.entry_point.getUserService()
user = user_service.getUser(1)
print(user.getName())
```

```scala
// Scala side
import py4j.GatewayServer

object Main extends App {
  val gateway = new GatewayServer(new MyApp())
  gateway.start()
}

class MyApp {
  def getUserService(): UserService = new UserService()
}
```

---

## Common Pitfalls

### 1. Mutability Assumptions

```python
# Python: Default mutable
my_list = [1, 2, 3]
my_list.append(4)  # Mutates in place
```

```scala
// ❌ Assuming mutability
val myList = List(1, 2, 3)
myList.append(4)  // COMPILE ERROR - List is immutable

// ✓ Immutable update
val myList = List(1, 2, 3)
val newList = myList :+ 4
```

### 2. None Handling

```python
# Python: None is falsy
user = get_user(id)
if user:  # Implicit None check
    process(user)
```

```scala
// ❌ Treating Option as boolean
val user: Option[User] = getUser(id)
if (user)  // COMPILE ERROR - Option is not Boolean
  process(user)

// ✓ Pattern match or isDefined
user match {
  case Some(u) => process(u)
  case None => // handle missing
}

// Or
if (user.isDefined) {
  process(user.get)
}

// Better: use map/foreach
user.foreach(process)
```

### 3. Trying to Translate Exceptions Directly

```python
# Python: Exceptions for control flow
def get_user(id):
    if not exists(id):
        raise UserNotFoundError(id)
    return fetch(id)
```

```scala
// ❌ Using exceptions like Python
def getUser(id: Int): User = {
  if (!exists(id))
    throw new UserNotFoundException(id)
  fetch(id)
}

// ✓ Use Option/Either/Try
def getUser(id: Int): Option[User] = {
  if (exists(id)) Some(fetch(id))
  else None
}

// Or Either for error details
def getUser(id: Int): Either[UserError, User] = {
  if (exists(id)) Right(fetch(id))
  else Left(UserNotFound(id))
}
```

### 4. Dynamic Typing Assumptions

```python
# Python: Dynamic typing
def process(value):
    if isinstance(value, str):
        return value.upper()
    elif isinstance(value, int):
        return value * 2
    else:
        return None
```

```scala
// ❌ Trying to replicate with Any
def process(value: Any): Any = value match {
  case s: String => s.toUpperCase
  case i: Int => i * 2
  case _ => null  // Avoid null!
}

// ✓ Use sealed trait for known types
sealed trait Value
case class StringValue(s: String) extends Value
case class IntValue(i: Int) extends Value

def process(value: Value): String = value match {
  case StringValue(s) => s.toUpperCase
  case IntValue(i) => (i * 2).toString
}
```

### 5. List Comprehension Translation

```python
# Python: Nested comprehensions
result = [[x * y for y in ys] for x in xs]
```

```scala
// ❌ Trying to nest for-comprehensions incorrectly
val result = for {
  x <- xs
  y <- ys  // This flattens!
} yield x * y  // Wrong shape

// ✓ Nested maps
val result = xs.map { x =>
  ys.map { y =>
    x * y
  }
}

// Or explicit nesting in for-comp
val result = for {
  x <- xs
} yield for {
  y <- ys
} yield x * y
```

---

## Best Practices

1. **Embrace immutability** - use `val` and immutable collections by default
2. **Pattern match extensively** - case classes and sealed traits enable exhaustive matching
3. **Use Option/Either/Try** instead of null and exceptions for expected errors
4. **Leverage type inference** - but use explicit types for public APIs
5. **Composition over inheritance** - use traits for mixins, avoid deep hierarchies
6. **For-comprehensions** for sequential monadic operations
7. **Prefer expression-oriented** code - everything returns a value
8. **Use case classes** for data - they're cheap and provide lots of functionality
9. **Separate pure and impure** code - keep side effects at the edges
10. **Test with property-based testing** - ScalaCheck for generative tests

---

## Tooling Recommendations

### Code Quality

| Purpose | Python | Scala |
|---------|--------|-------|
| Formatter | black, ruff | scalafmt |
| Linter | pylint, ruff | scalafix, wartremover |
| Type checker | mypy | scalac (built-in) |
| Dependency check | safety | dependency-check |
| Build tool | pip, poetry | sbt, mill, gradle |

### IDE Support

| IDE | Python | Scala |
|-----|--------|-------|
| IntelliJ IDEA | Python plugin | Scala plugin (excellent) |
| VS Code | Pylance | Metals |
| Vim/Neovim | python-lsp | metals-vim |
| Emacs | elpy | metals-emacs |

### Testing Tools

```scala
// ScalaTest - flexible, multiple styles
class MySpec extends AnyFlatSpec with Matchers {
  "A Stack" should "pop values in LIFO order" in {
    val stack = Stack[Int]()
    stack.push(1)
    stack.push(2)
    stack.pop() shouldBe 2
  }
}

// MUnit - lightweight, fast
class MyTests extends munit.FunSuite {
  test("basic assertion") {
    assertEquals(add(2, 3), 5)
  }
}

// ScalaCheck - property-based testing
import org.scalacheck.Properties
import org.scalacheck.Prop.forAll

object StringSpecification extends Properties("String") {
  property("concatenation") = forAll { (a: String, b: String) =>
    (a + b).length == a.length + b.length
  }
}
```

---

## Library Mapping Reference

| Python Library | Scala Equivalent | Use Case |
|----------------|------------------|----------|
| requests | sttp, akka-http | HTTP client |
| fastapi | http4s, akka-http, play | Web framework |
| pydantic | circe, upickle, jsoniter-scala | JSON (de)serialization |
| sqlalchemy | doobie, slick, quill | Database ORM/query |
| celery | akka, zio | Task queue |
| pandas | spark, frameless | Dataframes |
| numpy | breeze, nd4s | Numerical computing |
| pytest | scalatest, munit | Testing |
| hypothesis | scalacheck | Property testing |
| click | decline, scopt | CLI parsing |
| asyncio | Future, cats-effect IO, zio | Async/effects |
| typing | Built-in type system | Static types |

---

## Examples: End-to-End Conversion

### Example 1: REST API Service

**Python (FastAPI):**
```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    email: Optional[str] = None

users_db = {}

@app.post("/users", response_model=User)
async def create_user(user: User):
    if user.id in users_db:
        raise HTTPException(status_code=400, detail="User exists")
    users_db[user.id] = user
    return user

@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
    user = users_db.get(user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user
```

**Scala (http4s):**
```scala
import cats.effect.IO
import io.circe.generic.auto._
import org.http4s._
import org.http4s.dsl.io._
import org.http4s.circe.CirceEntityCodec._
import scala.collection.concurrent.TrieMap

case class User(id: Int, name: String, email: Option[String])

class UserService {
  private val usersDb = TrieMap[Int, User]()

  val routes: HttpRoutes[IO] = HttpRoutes.of[IO] {
    case req @ POST -> Root / "users" =>
      for {
        user <- req.as[User]
        response <- if (usersDb.contains(user.id)) {
          BadRequest("User exists")
        } else {
          usersDb.put(user.id, user)
          Ok(user)
        }
      } yield response

    case GET -> Root / "users" / IntVar(userId) =>
      usersDb.get(userId) match {
        case Some(user) => Ok(user)
        case None => NotFound("User not found")
      }
  }
}
```

### Example 2: Data Processing Pipeline

**Python:**
```python
from typing import List
import pandas as pd

def process_sales_data(data: List[dict]) -> dict:
    df = pd.DataFrame(data)

    # Filter
    recent = df[df['year'] >= 2020]

    # Group and aggregate
    by_region = recent.groupby('region')['sales'].sum()

    # Transform
    result = {
        'total': recent['sales'].sum(),
        'by_region': by_region.to_dict(),
        'avg_sale': recent['sales'].mean()
    }

    return result
```

**Scala:**
```scala
case class SaleRecord(year: Int, region: String, sales: Double)
case class SalesReport(total: Double, byRegion: Map[String, Double], avgSale: Double)

def processSalesData(data: List[SaleRecord]): SalesReport = {
  // Filter
  val recent = data.filter(_.year >= 2020)

  // Group and aggregate
  val byRegion = recent
    .groupBy(_.region)
    .view
    .mapValues(_.map(_.sales).sum)
    .toMap

  // Transform
  val total = recent.map(_.sales).sum
  val avgSale = total / recent.size

  SalesReport(total, byRegion, avgSale)
}

// Or with Spark for large datasets
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._

def processSalesDataSpark(spark: SparkSession, data: DataFrame): SalesReport = {
  val recent = data.filter($"year" >= 2020)

  val byRegion = recent
    .groupBy("region")
    .agg(sum("sales").as("total"))
    .collect()
    .map(row => row.getString(0) -> row.getDouble(1))
    .toMap

  val total = recent.agg(sum("sales")).as[Double].first()
  val avgSale = recent.agg(avg("sales")).as[Double].first()

  SalesReport(total, byRegion, avgSale)
}
```

---

## Related Skills

- `meta-convert-dev` - General conversion methodology
- `lang-python-dev` - Python fundamentals
- `lang-scala-dev` - Scala fundamentals
- `lang-scala-akka-dev` - Akka patterns for concurrency
- `lang-scala-cats-dev` - Cats FP library patterns
- `patterns-serialization-dev` - Cross-language serialization
- `patterns-concurrency-dev` - Cross-language concurrency
- `patterns-metaprogramming-dev` - Cross-language metaprogramming

---

## Feedback for Skill Gaps

Based on this conversion skill development:

**Gaps in lang-python-dev**:
1. More coverage of Python's memory model and GC behavior
2. Detailed asyncio patterns beyond basics
3. Python metaprogramming (descriptors, metaclasses) for deeper conversions
4. More on Python's data model (__dunder__ methods)

**Gaps in lang-scala-dev**:
1. More detailed coverage of variance in type parameters
2. Path-dependent types explanation
3. Scala 3 specific features (union types, opaque types, etc.)
4. More on implicit resolution and type class patterns

**Patterns that were challenging to document**:
1. Python's dynamic attribute access → Scala's static typing (requires architectural changes)
2. Python decorators → Scala macros (compile-time vs runtime complexity)
3. Python's mutable-by-default → Scala's immutable-by-default (paradigm shift)
4. Python's duck typing → Scala's structural types (performance tradeoffs)
5. Exception-based control flow → monadic error handling (mental model shift)

**What worked well**:
1. APTV workflow from meta-convert-dev provided clear structure
2. Pattern-by-pattern translation with examples
3. Emphasis on hybrid OOP+FP paradigm unique to Scala
4. Clear mapping of Python idioms to Scala equivalents
5. JVM ecosystem integration guidance

Attribution

aRustyDevaRustyDev
View sourceSee grades on GitHubMore from aRustyDev →
SSkills Directory ProSkills Directory

Get any skill into Claude in one click.

Download any skill as a ZIP for Claude.ai, Claude Desktop, or .claude/skills. $9/mo.

See Pro

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills Directory ProSkills Directory

Get any skill into Claude in one click.

Download any skill as a ZIP for Claude.ai, Claude Desktop, or .claude/skills. $9/mo.

See Pro

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

285172 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

10341 votes
View all in development →