or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

string-parsing.mddocs/

0

# String Parsing

1

2

Safe string parsing methods that return Option types instead of throwing exceptions. These methods provide consistent parsing across all numeric types and handle edge cases gracefully.

3

4

## Capabilities

5

6

### Safe Numeric Parsing

7

8

Extension methods for String that safely parse to numeric types, returning None for invalid input instead of throwing exceptions.

9

10

```scala { .api }

11

implicit class StringOps(s: String) {

12

/**

13

* Parse string to Boolean safely

14

* Accepts "true"/"false" (case-insensitive)

15

* @return Some(boolean) if valid, None otherwise

16

*/

17

def toBooleanOption: Option[Boolean]

18

19

/**

20

* Parse string to Byte safely

21

* @return Some(byte) if valid and within Byte range, None otherwise

22

*/

23

def toByteOption: Option[Byte]

24

25

/**

26

* Parse string to Short safely

27

* @return Some(short) if valid and within Short range, None otherwise

28

*/

29

def toShortOption: Option[Short]

30

31

/**

32

* Parse string to Int safely

33

* @return Some(int) if valid and within Int range, None otherwise

34

*/

35

def toIntOption: Option[Int]

36

37

/**

38

* Parse string to Long safely

39

* @return Some(long) if valid and within Long range, None otherwise

40

*/

41

def toLongOption: Option[Long]

42

43

/**

44

* Parse string to Float safely

45

* Handles special values like "NaN", "Infinity", "-Infinity"

46

* @return Some(float) if valid, None otherwise

47

*/

48

def toFloatOption: Option[Float]

49

50

/**

51

* Parse string to Double safely

52

* Handles special values like "NaN", "Infinity", "-Infinity"

53

* @return Some(double) if valid, None otherwise

54

*/

55

def toDoubleOption: Option[Double]

56

}

57

```

58

59

**Usage Examples:**

60

61

```scala

62

import scala.collection.compat._

63

64

// Integer parsing

65

"42".toIntOption // Some(42)

66

"abc".toIntOption // None

67

"2147483648".toIntOption // None (exceeds Int.MaxValue)

68

"".toIntOption // None

69

" 123 ".toIntOption // Some(123) - whitespace trimmed

70

71

// Boolean parsing

72

"true".toBooleanOption // Some(true)

73

"TRUE".toBooleanOption // Some(true)

74

"false".toBooleanOption // Some(false)

75

"FALSE".toBooleanOption // Some(false)

76

"yes".toBooleanOption // None

77

"1".toBooleanOption // None

78

79

// Floating point parsing

80

"3.14".toDoubleOption // Some(3.14)

81

"NaN".toFloatOption // Some(Float.NaN)

82

"Infinity".toDoubleOption // Some(Double.PositiveInfinity)

83

"-Infinity".toFloatOption // Some(Float.NegativeInfinity)

84

"invalid".toDoubleOption // None

85

86

// Byte range validation

87

"127".toByteOption // Some(127)

88

"128".toByteOption // None (exceeds Byte.MaxValue)

89

"-128".toByteOption // Some(-128)

90

"-129".toByteOption // None (below Byte.MinValue)

91

92

// Long parsing with large numbers

93

"9223372036854775807".toLongOption // Some(Long.MaxValue)

94

"9223372036854775808".toLongOption // None (exceeds Long.MaxValue)

95

```

96

97

### Safe Parsing Patterns

98

99

Common patterns for using safe parsing methods in real-world applications.

100

101

**Configuration Reading:**

102

103

```scala

104

import scala.collection.compat._

105

106

def readConfig(properties: Map[String, String]): Config = {

107

Config(

108

port = properties.get("server.port").flatMap(_.toIntOption).getOrElse(8080),

109

timeout = properties.get("timeout.seconds").flatMap(_.toLongOption).getOrElse(30L),

110

debugEnabled = properties.get("debug.enabled").flatMap(_.toBooleanOption).getOrElse(false),

111

maxMemory = properties.get("max.memory.gb").flatMap(_.toDoubleOption).getOrElse(2.0)

112

)

113

}

114

```

115

116

**CSV Parsing:**

117

118

```scala

119

import scala.collection.compat._

120

121

def parseCsvRow(row: String): Option[Person] = {

122

val fields = row.split(",")

123

if (fields.length >= 3) {

124

for {

125

name <- Option(fields(0)).filter(_.nonEmpty)

126

age <- fields(1).toIntOption

127

salary <- fields(2).toDoubleOption

128

} yield Person(name, age, salary)

129

} else None

130

}

131

132

// Usage

133

val csvData = List(

134

"Alice,25,50000.0",

135

"Bob,30,60000.0",

136

"Charlie,invalid,70000.0", // Will be None due to invalid age

137

"Diana,28,80000.0"

138

)

139

140

val people = csvData.flatMap(parseCsvRow)

141

```

142

143

**JSON-like String Processing:**

144

145

```scala

146

import scala.collection.compat._

147

148

def parseJsonValue(value: String): Option[JsonValue] = {

149

value.trim match {

150

case s if s.startsWith("\"") && s.endsWith("\"") =>

151

Some(JsonString(s.slice(1, s.length - 1)))

152

case s =>

153

s.toIntOption.map(JsonInt(_))

154

.orElse(s.toDoubleOption.map(JsonDouble(_)))

155

.orElse(s.toBooleanOption.map(JsonBoolean(_)))

156

}

157

}

158

```

159

160

**Input Validation:**

161

162

```scala

163

import scala.collection.compat._

164

165

case class ValidationError(field: String, message: String)

166

167

def validateUserInput(input: Map[String, String]): Either[List[ValidationError], User] = {

168

val errors = List.newBuilder[ValidationError]

169

170

val name = input.get("name") match {

171

case Some(n) if n.nonEmpty => Some(n)

172

case _ =>

173

errors += ValidationError("name", "Name is required")

174

None

175

}

176

177

val age = input.get("age").flatMap(_.toIntOption) match {

178

case Some(a) if a >= 0 && a <= 150 => Some(a)

179

case Some(_) =>

180

errors += ValidationError("age", "Age must be between 0 and 150")

181

None

182

case None =>

183

errors += ValidationError("age", "Valid age is required")

184

None

185

}

186

187

val email = input.get("email") match {

188

case Some(e) if e.contains("@") => Some(e)

189

case _ =>

190

errors += ValidationError("email", "Valid email is required")

191

None

192

}

193

194

val errorList = errors.result()

195

if (errorList.isEmpty) {

196

Right(User(name.get, age.get, email.get))

197

} else {

198

Left(errorList)

199

}

200

}

201

```

202

203

**Numeric Range Validation:**

204

205

```scala

206

import scala.collection.compat._

207

208

def parsePort(portStr: String): Option[Int] = {

209

portStr.toIntOption.filter(port => port > 0 && port <= 65535)

210

}

211

212

def parsePercentage(percentStr: String): Option[Double] = {

213

percentStr.toDoubleOption.filter(pct => pct >= 0.0 && pct <= 100.0)

214

}

215

216

def parseTimeout(timeoutStr: String): Option[Long] = {

217

timeoutStr.toLongOption.filter(_ > 0)

218

}

219

220

// Usage with validation

221

def configureServer(config: Map[String, String]): Either[String, ServerConfig] = {

222

for {

223

port <- config.get("port")

224

.flatMap(parsePort)

225

.toRight("Invalid port number")

226

timeout <- config.get("timeout")

227

.flatMap(parseTimeout)

228

.toRight("Invalid timeout value")

229

successRate <- config.get("success.rate")

230

.flatMap(parsePercentage)

231

.toRight("Invalid success rate percentage")

232

} yield ServerConfig(port, timeout, successRate)

233

}

234

```

235

236

### Error Handling Best Practices

237

238

**Combining with Validation:**

239

240

```scala

241

import scala.collection.compat._

242

243

// Combine multiple optional parses

244

def parseCoordinate(x: String, y: String): Option[(Double, Double)] = {

245

for {

246

xVal <- x.toDoubleOption

247

yVal <- y.toDoubleOption

248

} yield (xVal, yVal)

249

}

250

251

// Parse with default values

252

def parseWithDefaults(config: Map[String, String]): AppConfig = {

253

AppConfig(

254

threads = config.get("threads").flatMap(_.toIntOption).getOrElse(4),

255

memory = config.get("memory").flatMap(_.toLongOption).getOrElse(1024L),

256

ratio = config.get("ratio").flatMap(_.toDoubleOption).getOrElse(0.75)

257

)

258

}

259

260

// Parse with validation chains

261

def parsePositiveInt(s: String): Option[Int] = {

262

s.toIntOption.filter(_ > 0)

263

}

264

265

def parseNonEmptyString(s: String): Option[String] = {

266

Option(s).filter(_.trim.nonEmpty)

267

}

268

```

269

270

### Implementation Details

271

272

The string parsing methods handle various edge cases:

273

274

- **Whitespace**: Leading and trailing whitespace is automatically trimmed

275

- **Empty Strings**: Return None rather than throwing exceptions

276

- **Overflow**: Numbers outside the target type's range return None

277

- **Special Float Values**: "NaN", "Infinity", "-Infinity" are properly parsed for Float/Double

278

- **Case Sensitivity**: Boolean parsing is case-insensitive

279

- **Null Safety**: Null strings are handled gracefully

280

281

These parsing methods provide a safe, functional approach to string conversion that integrates well with Scala's Option-based error handling patterns.