or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arbitrary.mdcogen.mdgenerators.mdindex.mdproperties.mdproperty-collections.mdshrinking.mdstateful-testing.mdtest-execution.md

cogen.mddocs/

0

# Function Generation

1

2

ScalaCheck's Cogen (co-generator) framework enables the generation of functions for testing higher-order functions and functional programming patterns. Co-generators work by using input values to perturb random seeds, creating deterministic function generation based on the input space.

3

4

## Capabilities

5

6

### Core Cogen Trait

7

8

The fundamental co-generator abstraction that perturbs random seeds based on input values.

9

10

```scala { .api }

11

sealed trait Cogen[T] {

12

def perturb(seed: Seed, t: T): Seed

13

def cogen[A](t: T, g: Gen[A]): Gen[A]

14

def contramap[S](f: S => T): Cogen[S]

15

}

16

17

object Cogen {

18

def apply[T](implicit ev: Cogen[T]): Cogen[T]

19

def apply[T](f: T => Long): Cogen[T]

20

def apply[T](f: (Seed, T) => Seed): Cogen[T]

21

def perturb[T](seed: Seed, t: T)(implicit c: Cogen[T]): Seed

22

}

23

```

24

25

**Usage Examples:**

26

```scala

27

// Custom co-generator from simple function

28

case class UserId(id: Long)

29

implicit val cogenUserId: Cogen[UserId] = Cogen(_.id)

30

31

// Co-generator from seed transformation

32

case class Coordinate(x: Double, y: Double)

33

implicit val cogenCoordinate: Cogen[Coordinate] = Cogen { (seed, coord) =>

34

seed.reseed(coord.x.toLong).reseed(coord.y.toLong)

35

}

36

37

// Use co-generator to create deterministic perturbation

38

val seed = Seed.random()

39

val perturbedSeed = Cogen.perturb(seed, UserId(42))

40

```

41

42

### Function Generation Factory Methods

43

44

Utilities for creating co-generators from various function types.

45

46

```scala { .api }

47

def it[T, U](f: T => Iterator[U])(implicit U: Cogen[U]): Cogen[T]

48

def perturbPair[A, B](seed: Seed, ab: (A, B))(implicit ca: Cogen[A], cb: Cogen[B]): Seed

49

def perturbArray[A](seed: Seed, as: Array[A])(implicit ca: Cogen[A]): Seed

50

def domainOf[A, B](f: A => B)(implicit cb: Cogen[B]): Cogen[A]

51

```

52

53

**Usage Examples:**

54

```scala

55

// Co-generator from iterator function

56

case class WordList(words: List[String])

57

implicit val cogenWordList: Cogen[WordList] = Cogen.it(_.words.iterator)

58

59

// Co-generator from function composition

60

val stringLengthCogen: Cogen[String] = Cogen.domainOf((s: String) => s.length)

61

```

62

63

### Primitive Type Co-generators

64

65

Co-generators for basic Scala and Java primitive types.

66

67

```scala { .api }

68

implicit val cogenUnit: Cogen[Unit]

69

implicit val cogenBoolean: Cogen[Boolean]

70

implicit val cogenByte: Cogen[Byte]

71

implicit val cogenShort: Cogen[Short]

72

implicit val cogenChar: Cogen[Char]

73

implicit val cogenInt: Cogen[Int]

74

implicit val cogenLong: Cogen[Long]

75

implicit val cogenFloat: Cogen[Float]

76

implicit val cogenDouble: Cogen[Double]

77

```

78

79

**Usage Examples:**

80

```scala

81

// Generate functions using primitive co-generators

82

val intToStringFuncs: Gen[Int => String] = Gen.function1(Arbitrary.arbitrary[String])

83

84

val boolPredicates: Gen[Double => Boolean] = Gen.function1(Arbitrary.arbitrary[Boolean])

85

86

// Test higher-order functions

87

val mapProp = forAll { (f: Int => String, list: List[Int]) =>

88

list.map(f).length == list.length

89

}

90

```

91

92

### Numeric Type Co-generators

93

94

Co-generators for big numeric types and mathematical structures.

95

96

```scala { .api }

97

implicit val cogenBigInt: Cogen[BigInt]

98

implicit val cogenBigDecimal: Cogen[BigDecimal]

99

```

100

101

**Usage Examples:**

102

```scala

103

val bigIntFunctions: Gen[BigInt => Boolean] = Gen.function1(Gen.prob(0.5))

104

105

val bigDecimalTransforms: Gen[BigDecimal => BigDecimal] =

106

Gen.function1(Arbitrary.arbitrary[BigDecimal])

107

```

108

109

### String and Symbol Co-generators

110

111

Co-generators for textual and symbolic types.

112

113

```scala { .api }

114

implicit val cogenString: Cogen[String]

115

implicit val cogenSymbol: Cogen[Symbol]

116

```

117

118

**Usage Examples:**

119

```scala

120

// Generate string processing functions

121

val stringProcessors: Gen[String => Int] = Gen.function1(Arbitrary.arbitrary[Int])

122

123

val symbolMappers: Gen[Symbol => String] = Gen.function1(Gen.alphaStr)

124

125

// Test string operations

126

val stringConcatProp = forAll { (f: String => String, s: String) =>

127

val result = f(s)

128

result.length >= 0 // Functions can return any string

129

}

130

```

131

132

### Collection Type Co-generators

133

134

Co-generators for various collection types, enabling function generation over collections.

135

136

```scala { .api }

137

implicit val cogenBitSet: Cogen[BitSet]

138

implicit def cogenArray[A](implicit ca: Cogen[A]): Cogen[Array[A]]

139

implicit def cogenList[A](implicit ca: Cogen[A]): Cogen[List[A]]

140

implicit def cogenVector[A](implicit ca: Cogen[A]): Cogen[Vector[A]]

141

implicit def cogenStream[A](implicit ca: Cogen[A]): Cogen[Stream[A]]

142

implicit def cogenSeq[CC[x] <: Seq[x], A](implicit ca: Cogen[A]): Cogen[CC[A]]

143

implicit def cogenSet[A](implicit ca: Cogen[A], ord: Ordering[A]): Cogen[Set[A]]

144

implicit def cogenSortedSet[A](implicit ca: Cogen[A]): Cogen[SortedSet[A]]

145

implicit def cogenMap[K, V](implicit ck: Cogen[K], cv: Cogen[V], ord: Ordering[K]): Cogen[Map[K, V]]

146

implicit def cogenSortedMap[K, V](implicit ck: Cogen[K], cv: Cogen[V]): Cogen[SortedMap[K, V]]

147

```

148

149

**Usage Examples:**

150

```scala

151

// Generate functions over collections

152

val listProcessors: Gen[List[Int] => Boolean] = Gen.function1(Gen.prob(0.3))

153

154

val mapTransformers: Gen[Map[String, Int] => Int] = Gen.function1(Gen.posNum[Int])

155

156

// Test collection operations

157

val filterProp = forAll { (predicate: Int => Boolean, list: List[Int]) =>

158

val filtered = list.filter(predicate)

159

filtered.length <= list.length

160

}

161

162

val mapProp = forAll { (transform: String => Int, map: Map[String, String]) =>

163

val transformed = map.mapValues(transform)

164

transformed.keySet == map.keySet

165

}

166

```

167

168

### Higher-Order Type Co-generators

169

170

Co-generators for Option, Either, Try, and other wrapper types.

171

172

```scala { .api }

173

implicit def cogenOption[A](implicit ca: Cogen[A]): Cogen[Option[A]]

174

implicit def cogenEither[A, B](implicit ca: Cogen[A], cb: Cogen[B]): Cogen[Either[A, B]]

175

implicit def cogenTry[A](implicit ca: Cogen[A]): Cogen[Try[A]]

176

```

177

178

**Usage Examples:**

179

```scala

180

// Generate functions over optional values

181

val optionProcessors: Gen[Option[String] => Int] = Gen.function1(Gen.choose(0, 100))

182

183

val eitherHandlers: Gen[Either[String, Int] => Boolean] = Gen.function1(Gen.prob(0.5))

184

185

// Test optional operations

186

val optionMapProp = forAll { (f: String => Int, opt: Option[String]) =>

187

opt.map(f).isDefined == opt.isDefined

188

}

189

190

val eitherFoldProp = forAll { (left: String => Int, right: Int => Int, e: Either[String, Int]) =>

191

val result = e.fold(left, right)

192

result >= 0 || result < 0 // Any integer is valid

193

}

194

```

195

196

### Function Type Co-generators

197

198

Co-generators for function values and partial functions.

199

200

```scala { .api }

201

implicit def cogenFunction0[Z](implicit cz: Cogen[Z]): Cogen[() => Z]

202

implicit def cogenPartialFunction[A, B](

203

implicit aa: Arbitrary[A],

204

cb: Cogen[B]

205

): Cogen[PartialFunction[A, B]]

206

```

207

208

**Usage Examples:**

209

```scala

210

// Generate higher-order functions

211

val thunkProcessors: Gen[(() => Int) => String] = Gen.function1(Gen.alphaStr)

212

213

val partialFunctionCombiners: Gen[PartialFunction[Int, String] => Boolean] =

214

Gen.function1(Gen.prob(0.6))

215

216

// Test function composition

217

val compositionProp = forAll { (f: Int => String, g: String => Boolean, x: Int) =>

218

val composed = g.compose(f)

219

composed(x) == g(f(x))

220

}

221

```

222

223

### Exception and Error Co-generators

224

225

Co-generators for exception hierarchy types.

226

227

```scala { .api }

228

implicit val cogenException: Cogen[Exception]

229

implicit val cogenThrowable: Cogen[Throwable]

230

```

231

232

**Usage Examples:**

233

```scala

234

val exceptionHandlers: Gen[Exception => String] = Gen.function1(Gen.alphaStr)

235

236

val errorProcessors: Gen[Throwable => Int] = Gen.function1(Gen.choose(-100, 100))

237

238

// Test exception handling

239

val catchProp = forAll { (handler: Exception => String, ex: Exception) =>

240

try {

241

throw ex

242

} catch {

243

case e: Exception => handler(e).length >= 0

244

}

245

}

246

```

247

248

### Duration and Time Co-generators

249

250

Co-generators for temporal types.

251

252

```scala { .api }

253

implicit val cogenFiniteDuration: Cogen[FiniteDuration]

254

implicit val cogenDuration: Cogen[Duration]

255

```

256

257

**Usage Examples:**

258

```scala

259

val durationTransforms: Gen[FiniteDuration => Long] = Gen.function1(Gen.posNum[Long])

260

261

val timeoutHandlers: Gen[Duration => Boolean] = Gen.function1(Gen.prob(0.4))

262

263

// Test duration operations

264

val durationProp = forAll { (f: FiniteDuration => Long, d: FiniteDuration) =>

265

val result = f(d)

266

result >= Long.MinValue && result <= Long.MaxValue

267

}

268

```

269

270

### UUID Co-generator

271

272

Co-generator for universally unique identifiers.

273

274

```scala { .api }

275

implicit val cogenUUID: Cogen[UUID]

276

```

277

278

**Usage Examples:**

279

```scala

280

val uuidProcessors: Gen[UUID => String] = Gen.function1(Gen.alphaNumStr)

281

282

val uuidPredicates: Gen[UUID => Boolean] = Gen.function1(Gen.prob(0.5))

283

```

284

285

## Function Generation Patterns

286

287

### Testing Higher-Order Functions

288

289

```scala

290

// Test map operation on lists

291

val mapProperty = forAll { (f: Int => String, list: List[Int]) =>

292

val mapped = list.map(f)

293

mapped.length == list.length &&

294

(list.isEmpty ==> mapped.isEmpty) &&

295

(list.nonEmpty ==> mapped.nonEmpty)

296

}

297

298

// Test filter operation

299

val filterProperty = forAll { (predicate: String => Boolean, list: List[String]) =>

300

val filtered = list.filter(predicate)

301

filtered.length <= list.length &&

302

filtered.forall(predicate) &&

303

filtered.forall(list.contains)

304

}

305

306

// Test fold operations

307

val foldProperty = forAll { (f: (Int, String) => Int, zero: Int, list: List[String]) =>

308

val result = list.foldLeft(zero)(f)

309

list.isEmpty ==> (result == zero)

310

}

311

```

312

313

### Function Composition Testing

314

315

```scala

316

val compositionProperty = forAll {

317

(f: Int => String, g: String => Boolean, h: Boolean => Double, x: Int) =>

318

319

val composed = h.compose(g).compose(f)

320

val stepwise = h(g(f(x)))

321

322

composed(x) == stepwise

323

}

324

325

val andThenProperty = forAll { (f: Int => String, g: String => Boolean, x: Int) =>

326

(f andThen g)(x) == g(f(x))

327

}

328

```

329

330

### Partial Function Testing

331

332

```scala

333

val partialFunctionProperty = forAll {

334

(pf: PartialFunction[Int, String], x: Int) =>

335

336

pf.isDefinedAt(x) ==> {

337

val result = pf(x)

338

result != null // Partial functions should not return null when defined

339

}

340

}

341

342

val liftProperty = forAll { (pf: PartialFunction[String, Int], s: String) =>

343

val lifted = pf.lift

344

pf.isDefinedAt(s) ==> lifted(s).isDefined

345

}

346

```

347

348

### Custom Co-generator Creation

349

350

```scala

351

// Complex custom type with co-generator

352

case class Person(name: String, age: Int, emails: List[String])

353

354

implicit val cogenPerson: Cogen[Person] = Cogen { (seed, person) =>

355

val seed1 = Cogen.perturb(seed, person.name)

356

val seed2 = Cogen.perturb(seed1, person.age)

357

Cogen.perturb(seed2, person.emails)

358

}

359

360

// Test functions over custom types

361

val personProperty = forAll { (f: Person => Boolean, p: Person) =>

362

val result = f(p)

363

result == true || result == false // Boolean functions return booleans

364

}

365

366

// Recursive data structure co-generator

367

sealed trait Tree[+A]

368

case class Leaf[A](value: A) extends Tree[A]

369

case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]

370

371

implicit def cogenTree[A](implicit ca: Cogen[A]): Cogen[Tree[A]] = Cogen {

372

case Leaf(value) => Cogen { (seed, _) => Cogen.perturb(seed, value) }

373

case Branch(left, right) => Cogen { (seed, _) =>

374

val seed1 = cogenTree[A].perturb(seed, left)

375

cogenTree[A].perturb(seed1, right)

376

}

377

}.perturb

378

```

379

380

### Function Generation with Constraints

381

382

```scala

383

// Generate only pure functions (deterministic)

384

def pureFunction[A, B](implicit ca: Cogen[A], ab: Arbitrary[B]): Gen[A => B] = {

385

Gen.function1[A, B](ab.arbitrary)(ca, ab)

386

}

387

388

// Generate functions with specific properties

389

def monotonicIntFunction: Gen[Int => Int] = {

390

// This is a simplified example - real monotonic function generation is complex

391

Gen.function1[Int, Int](Gen.choose(0, 1000))

392

.suchThat { f =>

393

// Test monotonicity on small sample

394

val sample = (1 to 10).toList

395

sample.zip(sample.tail).forall { case (x, y) => f(x) <= f(y) }

396

}

397

}

398

399

// Generate bijective functions (for small domains)

400

def bijectiveFunction[A](domain: List[A])(implicit ca: Cogen[A]): Gen[A => A] = {

401

Gen.oneOf(domain.permutations.toSeq).map { permutation =>

402

val mapping = domain.zip(permutation).toMap

403

(a: A) => mapping.getOrElse(a, a)

404

}

405

}

406

```

407

408

## Advanced Function Generation

409

410

### Stateful Function Generation

411

412

```scala

413

// Generate functions that maintain internal state

414

case class Counter(initial: Int) {

415

private var count = initial

416

def increment(): Int = { count += 1; count }

417

def current: Int = count

418

}

419

420

val statefulProperty = forAll { (init: Int) =>

421

val counter = Counter(init)

422

val first = counter.increment()

423

val second = counter.increment()

424

425

first == init + 1 && second == init + 2

426

}

427

```

428

429

### Function Generation for Testing Frameworks

430

431

```scala

432

// Generate test predicates

433

val predicateProperty = forAll { (predicate: List[Int] => Boolean, lists: List[List[Int]]) =>

434

val results = lists.map(predicate)

435

results.forall(r => r == true || r == false) // All results are booleans

436

}

437

438

// Generate validation functions

439

case class ValidationResult(isValid: Boolean, errors: List[String])

440

441

val validatorProperty = forAll {

442

(validator: String => ValidationResult, inputs: List[String]) =>

443

444

val results = inputs.map(validator)

445

results.forall(r => r.isValid || r.errors.nonEmpty) // Invalid results have errors

446

}

447

```