or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

backported-collections.mdcollection-extensions.mdindex.mdjava-conversion.mdresource-management.mdstring-parsing.mdutility-features.md
tile.json

utility-features.mddocs/

Utility Features

Additional utilities including chaining operations, annotations for cross-compilation, enhanced regex functionality, and other helper features that improve code quality and cross-version compatibility.

Capabilities

Chaining Operations

Fluent interface methods that enable method chaining and functional programming patterns on any type.

/**
 * Trait providing chaining syntax for all types
 */
trait ChainingSyntax {
  /**
   * Add chaining methods to any type
   * @param a Value to add chaining methods to
   * @return ChainingOps instance with chaining methods
   */
  implicit final def scalaUtilChainingOps[A](a: A): ChainingOps[A]
}

/**
 * Import target for chaining operations
 */
object chaining extends ChainingSyntax

/**
 * Value class providing chaining methods
 * @param self The wrapped value
 */
final class ChainingOps[A](self: A) extends AnyVal {
  /**
   * Apply side effect function and return original value
   * Useful for debugging, logging, or other side effects in method chains
   * @param f Side effect function
   * @return Original value unchanged
   */
  def tap[U](f: A => U): A
  
  /**
   * Transform value by applying function (alias for function application)
   * Useful for applying transformations in a fluent style
   * @param f Transformation function
   * @return Transformed value
   */
  def pipe[B](f: A => B): B
}

Usage Examples:

import scala.util.chaining._

// Basic tap usage for side effects
val result = List(1, 2, 3, 4, 5)
  .filter(_ % 2 == 0)
  .tap(filtered => println(s"After filter: $filtered"))
  .map(_ * 2)
  .tap(mapped => println(s"After map: $mapped"))

// Pipe for transformations
val processed = "hello world"
  .pipe(_.split(" "))
  .pipe(_.map(_.capitalize))
  .pipe(_.mkString("-"))
// Result: "Hello-World"

// Debugging chains with tap
val calculation = 42
  .tap(x => println(s"Starting with: $x"))
  .pipe(_ * 2)
  .tap(x => println(s"After doubling: $x"))
  .pipe(_ + 10)
  .tap(x => println(s"After adding 10: $x"))
// Result: 94

// Complex data processing pipeline
case class User(name: String, age: Int, email: String)

val users = List(
  User("Alice", 25, "alice@example.com"),
  User("Bob", 17, "bob@example.com"),
  User("Charlie", 30, "charlie@example.com")
)

val processedUsers = users
  .filter(_.age >= 18)
  .tap(adults => println(s"Found ${adults.length} adult users"))
  .sortBy(_.age)
  .pipe(_.take(10))  // Top 10 by age
  .map(user => user.copy(email = user.email.toLowerCase))
  .tap(final => println(s"Final result: ${final.length} users"))

Cross-Compilation Annotations

Annotations that facilitate cross-compilation between different Scala versions by suppressing warnings and marking unused code.

/**
 * Local warning suppression annotation
 * On Scala 2.13+: Suppresses compiler warnings locally
 * On Scala 2.11: No functionality, but enables cross-compilation
 * @param value Warning category to suppress (optional)
 */
class nowarn(value: String = "") extends scala.annotation.ClassfileAnnotation

/**
 * Mark parameters or definitions as unused to suppress warnings
 * Extends deprecated to suppress unused warnings across Scala versions
 */
final class unused extends deprecated("unused", "unused")

Usage Examples:

import scala.annotation.unused

// Suppress specific warnings
@nowarn("cat=deprecation")
def useDeprecatedApi(): Unit = {
  // Use deprecated API without warnings
  legacyLibrary.deprecatedMethod()
}

// Suppress all warnings in a scope
@nowarn
def experimentalCode(): Unit = {
  // Experimental code with potential warnings
  val x = unsafeOperation()
  riskyTransformation(x)
}

// Mark unused parameters
def processData(data: List[String], @unused debugMode: Boolean): List[String] = {
  // debugMode parameter is unused but kept for API compatibility
  data.filter(_.nonEmpty).map(_.trim)
}

// Mark unused variables in pattern matching
def handleResponse(response: Either[Error, Success]): String = response match {
  case Left(@unused error) => "Error occurred"  // error details unused
  case Right(success) => s"Success: ${success.message}"
}

// Mark unused imports (useful for cross-compilation)
import scala.collection.compat._  // @unused - needed for 2.11/2.12 but not 2.13

// Cross-version compatibility pattern
class CrossVersionService {
  @nowarn("cat=unused")
  private val compatImport = {
    // Import needed for older Scala versions but unused in newer ones
    import scala.collection.compat._
    ()
  }
  
  def processCollection[A](items: List[A]): Option[A] = {
    items.maxOption  // Available through compat import on older versions
  }
}

Enhanced Regex Operations

Additional regex functionality providing more intuitive matching operations.

/**
 * Extension methods for Regex
 * @param regex The regex to extend
 */
implicit class RegexOps(regex: Regex) {
  /**
   * Test if regex matches entire character sequence with anchoring
   * More intuitive than findFirstIn for full string matching
   * @param source Character sequence to test
   * @return true if regex matches the entire sequence
   */
  def matches(source: CharSequence): Boolean
}

Usage Examples:

import scala.util.matching.Regex
import scala.util.matching.compat._

val emailPattern = """[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}""".r
val phonePattern = """\d{3}-\d{3}-\d{4}""".r

// Full string matching
emailPattern.matches("user@example.com")     // true
emailPattern.matches("not-an-email")         // false
emailPattern.matches("user@example.com!")    // false (extra character)

phonePattern.matches("555-123-4567")         // true
phonePattern.matches("555-123-456")          // false (too short)

// Validation functions
def isValidEmail(email: String): Boolean = {
  emailPattern.matches(email)
}

def isValidPhoneNumber(phone: String): Boolean = {
  phonePattern.matches(phone)
}

// Form validation
case class ContactForm(email: String, phone: String) {
  def validate(): List[String] = {
    val errors = List.newBuilder[String]
    
    if (!emailPattern.matches(email)) {
      errors += "Invalid email format"
    }
    
    if (!phonePattern.matches(phone)) {
      errors += "Invalid phone number format"
    }
    
    errors.result()
  }
}

// Pattern matching with validation
def processUserInput(input: String): String = input match {
  case emailPattern.matches(_) if emailPattern.matches(input) => "Valid email"
  case phonePattern.matches(_) if phonePattern.matches(input) => "Valid phone"
  case _ => "Invalid format"
}

Random Number Extensions

Enhanced random number generation with support for long ranges.

/**
 * Extension methods for scala.util.Random
 * @param self Random instance to extend
 */
final class RandomExtensions(self: Random) {
  /**
   * Generate random long in range [0, n)
   * Extends Random to support long ranges beyond Int.MaxValue
   * @param n Upper bound (exclusive)
   * @return Random long between 0 (inclusive) and n (exclusive)
   */
  def nextLong(n: Long): Long
}

Usage Examples:

import scala.util.Random
import scala.collection.compat._

val random = new Random()

// Generate random longs in large ranges
val bigRandomId = random.nextLong(1000000000000L)  // 0 to 1 trillion
val timestampOffset = random.nextLong(86400000L)   // 0 to 24 hours in milliseconds

// Generate random data for testing
def generateTestData(count: Int): List[TestRecord] = {
  (1 to count).map { _ =>
    TestRecord(
      id = random.nextLong(Long.MaxValue),
      timestamp = System.currentTimeMillis() + random.nextLong(86400000L),
      value = random.nextDouble()
    )
  }.toList
}

// Random sampling from large datasets
def sampleLargeDataset[A](data: Vector[A], sampleSize: Int): List[A] = {
  val indices = (1 to sampleSize).map(_ => random.nextLong(data.length.toLong).toInt).toSet
  indices.map(data(_)).toList
}

Factory and Collection Extensions

Additional factory methods and collection utilities for enhanced collection creation patterns.

/**
 * Extension methods for Array companion object
 * @param fact Array companion object
 */
class ArrayExtensions(fact: Array.type) {
  /**
   * Create array from collection
   * @param source Source collection
   * @param elemTag ClassTag for element type
   * @return New array containing elements from source
   */
  def from[A: ClassTag](source: TraversableOnce[A]): Array[A]
}

/**
 * Extension methods for SortedMap companion object
 * @param fact SortedMap companion object
 */
class ImmutableSortedMapExtensions(fact: immutable.SortedMap.type) {
  /**
   * Create SortedMap from collection of pairs
   * @param source Source collection of key-value pairs
   * @param ordering Ordering for keys
   * @return New SortedMap containing pairs from source
   */
  def from[K: Ordering, V](source: TraversableOnce[(K, V)]): immutable.SortedMap[K, V]
}

/**
 * Extension methods for Option companion object
 * @param fact Option companion object
 */
class OptionCompanionExtensionMethods(fact: Option.type) {
  /**
   * Create Option based on condition
   * @param cond Condition to test
   * @param a Value to wrap if condition is true (call-by-name)
   * @return Some(a) if cond is true, None otherwise
   */
  def when[A](cond: Boolean)(a: => A): Option[A]
  
  /**
   * Create Option based on negated condition
   * @param cond Condition to test
   * @param a Value to wrap if condition is false (call-by-name)
   * @return Some(a) if cond is false, None otherwise
   */
  def unless[A](cond: Boolean)(a: => A): Option[A]
}

Usage Examples:

import scala.collection.compat._

// Array creation from collections
val list = List(1, 2, 3, 4, 5)
val array = Array.from(list)

val stringArray = Array.from("hello".toCharArray.map(_.toString))

// SortedMap creation
val pairs = List("c" -> 3, "a" -> 1, "b" -> 2)
val sortedMap = immutable.SortedMap.from(pairs)
// Result: SortedMap("a" -> 1, "b" -> 2, "c" -> 3)

// Conditional Option creation
val userAge = 25
val drinkingAge = Option.when(userAge >= 21)(userAge)  // Some(25)
val invalidAge = Option.when(userAge < 0)(userAge)     // None

val message = Option.unless(userAge < 18)("Welcome, adult user!")

// Complex conditional logic
def processUser(user: User): Option[ProcessedUser] = {
  Option.when(user.isActive && user.email.nonEmpty) {
    ProcessedUser(
      id = user.id,
      name = user.name.trim,
      email = user.email.toLowerCase,
      lastLogin = user.lastLogin.getOrElse(Instant.now())
    )
  }
}

// Configuration-based instantiation
def createService(config: Config): Option[DatabaseService] = {
  Option.when(config.databaseEnabled) {
    new DatabaseService(
      url = config.databaseUrl,
      username = config.databaseUser,
      password = config.databasePassword
    )
  }
}

Size and Sorting Extensions

Enhanced operations for size checking and sorting on various collection types.

/**
 * Extension methods for Sorted collections
 * @param fact Sorted collection instance
 */
class SortedExtensionMethods[K, T <: Sorted[K, T]](fact: Sorted[K, T]) {
  /**
   * Elements from key onwards
   * @param from Starting key (inclusive)
   * @return Collection containing elements from key onwards
   */
  def rangeFrom(from: K): T
  
  /**
   * Elements up to key
   * @param to Ending key (inclusive)
   * @return Collection containing elements up to key
   */
  def rangeTo(to: K): T
  
  /**
   * Elements up to but not including key
   * @param until Ending key (exclusive)
   * @return Collection containing elements before key
   */
  def rangeUntil(until: K): T
}

/**
 * Extension methods for SortedMap instances
 * @param self SortedMap to extend
 */
class SortedMapExtensionMethods[K, V](self: collection.SortedMap[K, V]) {
  /**
   * First entry after given key
   * @param key Key to search after
   * @return Some((key, value)) of first entry after key, None if not found
   */
  def minAfter(key: K): Option[(K, V)]
  
  /**
   * Last entry before given key
   * @param key Key to search before
   * @return Some((key, value)) of last entry before key, None if not found
   */
  def maxBefore(key: K): Option[(K, V)]
}

/**
 * Extension methods for SortedSet instances
 * @param self SortedSet to extend
 */
class SortedSetExtensionMethods[A](self: collection.SortedSet[A]) {
  /**
   * First element after given key
   * @param key Key to search after
   * @return Some(element) of first element after key, None if not found
   */
  def minAfter(key: A): Option[A]
  
  /**
   * Last element before given key
   * @param key Key to search before
   * @return Some(element) of last element before key, None if not found
   */
  def maxBefore(key: A): Option[A]
}

Usage Examples:

import scala.collection.compat._
import scala.collection.{SortedMap, SortedSet}

// Sorted collection range operations
val sortedNumbers = SortedSet(1, 3, 5, 7, 9, 11, 13, 15)

val fromFive = sortedNumbers.rangeFrom(5)      // SortedSet(5, 7, 9, 11, 13, 15)
val upToTen = sortedNumbers.rangeTo(10)        // SortedSet(1, 3, 5, 7, 9)
val beforeTen = sortedNumbers.rangeUntil(10)   // SortedSet(1, 3, 5, 7, 9)

// SortedMap neighbor searches
val scores = SortedMap("Alice" -> 95, "Bob" -> 87, "Charlie" -> 92, "Diana" -> 98)

val nextAfterBob = scores.minAfter("Bob")       // Some(("Charlie", 92))
val prevBeforeCharlie = scores.maxBefore("Charlie") // Some(("Bob", 87))

// SortedSet neighbor searches
val timestamps = SortedSet(100L, 200L, 300L, 400L, 500L)

val nextAfter250 = timestamps.minAfter(250L)    // Some(300L)
val prevBefore350 = timestamps.maxBefore(350L) // Some(300L)

// Range-based querying
def findScoresInRange(scores: SortedMap[String, Int], 
                     minScore: Int, 
                     maxScore: Int): Map[String, Int] = {
  scores.filter { case (_, score) => score >= minScore && score <= maxScore }
}

// Time-based operations
case class Event(timestamp: Long, data: String)

def findEventsAfter(events: SortedSet[Event], afterTime: Long): Option[Event] = {
  implicit val eventOrdering: Ordering[Event] = Ordering.by(_.timestamp)
  val dummyEvent = Event(afterTime, "")
  events.minAfter(dummyEvent)
}

These utility features provide essential cross-compilation support, enhanced debugging capabilities, and improved collection operations that make Scala code more maintainable and functional across different versions.