or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

function-functions.mdindex.mdlist-functions.mdmath-logic.mdobject-functions.mdstring-type.md

function-functions.mddocs/

0

# Function Functions

1

2

Ramda provides 55 powerful functions for function composition, currying, and higher-order operations. These functions enable the creation of elegant, reusable code through functional programming patterns.

3

4

## Function Composition

5

6

### compose

7

Right-to-left function composition (mathematical style).

8

9

```javascript { .api }

10

/**

11

* @param {...Function} fns - Functions to compose (right to left)

12

* @returns {Function} Composed function

13

*/

14

R.compose(...fns)

15

16

// Mathematical composition: (f ∘ g)(x) = f(g(x))

17

const transform = R.compose(

18

R.join('-'), // 4. Join with dashes

19

R.map(R.toLower), // 3. Convert to lowercase

20

R.split(' '), // 2. Split by spaces

21

R.trim // 1. Remove whitespace (executed first)

22

);

23

24

transform(' Hello World '); // => 'hello-world'

25

26

// Step by step execution:

27

// 1. R.trim(' Hello World ') => 'Hello World'

28

// 2. R.split(' ', 'Hello World') => ['Hello', 'World']

29

// 3. R.map(R.toLower, ['Hello', 'World']) => ['hello', 'world']

30

// 4. R.join('-', ['hello', 'world']) => 'hello-world'

31

32

// Mathematical operations

33

const calculate = R.compose(

34

Math.abs, // 3. Get absolute value

35

R.subtract(10), // 2. Subtract 10

36

R.multiply(2) // 1. Multiply by 2 (executed first)

37

);

38

39

calculate(3); // => 4

40

// Steps: 3 * 2 = 6, 6 - 10 = -4, abs(-4) = 4

41

```

42

43

### pipe

44

Left-to-right function composition (natural reading order).

45

46

```javascript { .api }

47

/**

48

* @param {...Function} fns - Functions to compose (left to right)

49

* @returns {Function} Composed function

50

*/

51

R.pipe(...fns)

52

53

// Natural reading order: data flows left to right

54

const processUser = R.pipe(

55

R.prop('name'), // 1. Get name property

56

R.trim, // 2. Remove whitespace

57

R.toLower, // 3. Convert to lowercase

58

R.split(' '), // 4. Split into words

59

R.map(R.capitalize), // 5. Capitalize each word

60

R.join(' ') // 6. Join back together

61

);

62

63

const user = { name: ' john doe ' };

64

processUser(user); // => 'John Doe'

65

66

// Data processing pipeline

67

const analyzeNumbers = R.pipe(

68

R.filter(R.is(Number)), // 1. Keep only numbers

69

R.map(Math.abs), // 2. Get absolute values

70

R.sort(R.subtract), // 3. Sort ascending

71

R.takeLast(3), // 4. Take top 3

72

R.reduce(R.add, 0) // 5. Sum them

73

);

74

75

analyzeNumbers([1, -5, 'x', 3, -8, 2]); // => 10

76

// Steps: [1, -5, 3, -8, 2] → [1, 5, 3, 8, 2] → [1, 2, 3, 5, 8] → [3, 5, 8] → 16

77

```

78

79

### o

80

Binary function composition (curried compose for exactly 2 functions).

81

82

```javascript { .api }

83

/**

84

* @param {Function} f - Second function to apply

85

* @param {Function} g - First function to apply

86

* @returns {Function} Composed function f(g(x))

87

*/

88

R.o(f, g)

89

90

const slugify = R.o(R.join('-'), R.split(' '));

91

slugify('Hello World'); // => 'Hello-World'

92

93

// More readable than nested calls

94

const getFirstWord = R.o(R.head, R.split(' '));

95

getFirstWord('Hello World'); // => 'Hello'

96

97

// Curried usage

98

const withStringLength = R.o(R.length);

99

const nameLength = withStringLength(R.prop('name'));

100

nameLength({ name: 'Alice' }); // => 5

101

```

102

103

### flow

104

Left-to-right function composition with seed value.

105

106

```javascript { .api }

107

/**

108

* @param {*} seed - Initial value to flow through functions

109

* @param {Array} pipeline - Array of functions to apply in sequence

110

* @returns {*} Final result after applying all functions

111

*/

112

R.flow(seed, pipeline)

113

114

// Process data through a pipeline

115

R.flow(9, [Math.sqrt, R.negate, R.inc]); // => -2

116

// Same as: R.inc(R.negate(Math.sqrt(9)))

117

118

// Transform and validate user input

119

const processInput = input => R.flow(input, [

120

R.trim,

121

R.toLower,

122

R.split(' '),

123

R.filter(R.complement(R.isEmpty)),

124

R.take(3)

125

]);

126

127

processInput(' Hello Beautiful World '); // => ['hello', 'beautiful', 'world']

128

129

// Data-first style (more intuitive than pipe sometimes)

130

R.flow([1, 2, 3, 4, 5], [

131

R.map(R.multiply(2)),

132

R.filter(R.gt(R.__, 5)),

133

R.reduce(R.add, 0)

134

]); // => 18

135

```

136

137

## Currying and Partial Application

138

139

### curry

140

Convert a regular function to a curried function.

141

142

```javascript { .api }

143

/**

144

* @param {Function} fn - Function to curry

145

* @returns {Function} Curried version of the function

146

*/

147

R.curry(fn)

148

149

// Regular function

150

function add3(a, b, c) {

151

return a + b + c;

152

}

153

154

const curriedAdd = R.curry(add3);

155

156

// All these are equivalent:

157

curriedAdd(1, 2, 3); // => 6

158

curriedAdd(1)(2, 3); // => 6

159

curriedAdd(1, 2)(3); // => 6

160

curriedAdd(1)(2)(3); // => 6

161

162

// Partial application becomes natural

163

const add5 = curriedAdd(2)(3); // Waiting for one more argument

164

add5(4); // => 9

165

166

// Real-world example

167

const greet = R.curry((greeting, name, punctuation) =>

168

`${greeting}, ${name}${punctuation}`

169

);

170

171

const sayHello = greet('Hello');

172

const casualGreet = sayHello(R.__, '!');

173

casualGreet('Alice'); // => 'Hello, Alice!'

174

```

175

176

### curryN

177

Curry with explicit arity.

178

179

```javascript { .api }

180

/**

181

* @param {Number} arity - Number of arguments to curry

182

* @param {Function} fn - Function to curry

183

* @returns {Function} Curried function with specified arity

184

*/

185

R.curryN(arity, fn)

186

187

// Useful for variadic functions

188

const sumAll = (...args) => R.sum(args);

189

const curriedSum = R.curryN(3, sumAll);

190

191

curriedSum(1)(2)(3); // => 6

192

curriedSum(1, 2)(3); // => 6

193

194

// Working with functions that have default parameters

195

const withDefaults = (a, b = 10, c = 20) => a + b + c;

196

const curriedWithDefaults = R.curryN(2, withDefaults);

197

198

curriedWithDefaults(5)(15); // => 40 (5 + 15 + 20)

199

```

200

201

### partial, partialRight

202

Partially apply arguments from left or right.

203

204

```javascript { .api }

205

/**

206

* @param {Function} fn - Function to partially apply

207

* @param {Array} leftArgs - Arguments to apply from the left

208

* @returns {Function} Partially applied function

209

*/

210

R.partial(fn, leftArgs)

211

212

const multiply4 = (a, b, c, d) => a * b * c * d;

213

214

// Apply from left

215

const doubleAndSomething = R.partial(multiply4, [2, 3]);

216

doubleAndSomething(4, 5); // => 120 (2 * 3 * 4 * 5)

217

218

// Apply from right

219

const somethingAndTen = R.partialRight(multiply4, [2, 5]);

220

somethingAndTen(3, 4); // => 120 (3 * 4 * 2 * 5)

221

222

// Real-world example: pre-configured API calls

223

const apiCall = (method, url, headers, data) => ({ method, url, headers, data });

224

const postJSON = R.partial(apiCall, ['POST', '/api/users', {'Content-Type': 'application/json'}]);

225

226

postJSON({name: 'Alice'});

227

// => { method: 'POST', url: '/api/users', headers: {...}, data: {name: 'Alice'} }

228

```

229

230

### flip

231

Reverse the order of the first two arguments.

232

233

```javascript { .api }

234

/**

235

* @param {Function} fn - Function whose first two arguments to flip

236

* @returns {Function} Function with first two arguments reversed

237

*/

238

R.flip(fn)

239

240

const divide = (a, b) => a / b;

241

const flippedDivide = R.flip(divide);

242

243

divide(10, 2); // => 5

244

flippedDivide(10, 2); // => 0.2 (2 / 10)

245

246

// Useful with curried functions

247

const subtract = R.subtract; // (a, b) => a - b

248

const subtractFrom = R.flip(subtract); // (a, b) => b - a

249

250

const minus5 = subtract(R.__, 5); // x - 5

251

const subtract5From = subtractFrom(R.__, 5); // 5 - x

252

253

minus5(10); // => 5 (10 - 5)

254

subtract5From(10); // => -5 (5 - 10)

255

```

256

257

## The Placeholder (`R.__`)

258

259

The placeholder allows flexible partial application by marking positions for future arguments.

260

261

```javascript { .api }

262

// Basic placeholder usage

263

const divide = R.divide; // (a, b) => a / b

264

265

const divideBy2 = divide(R.__, 2); // (x / 2)

266

const half = divideBy2(10); // => 5

267

268

const tenDividedBy = divide(10, R.__); // (10 / x)

269

const result = tenDividedBy(2); // => 5

270

271

// Multiple placeholders

272

const clamp = R.clamp; // (min, max, value) => clampedValue

273

274

const clampTo100 = clamp(0, 100, R.__);

275

clampTo100(150); // => 100

276

clampTo100(-10); // => 0

277

278

const clampFrom50 = clamp(50, R.__, R.__);

279

const clampFrom50To200 = clampFrom50(200);

280

clampFrom50To200(75); // => 75

281

282

// Complex example: building specialized filters

283

const filter = R.filter;

284

const propEq = R.propEq;

285

286

const activeUsers = filter(propEq('active', true), R.__);

287

const adminUsers = filter(propEq('role', 'admin'), R.__);

288

289

const users = [

290

{ name: 'Alice', active: true, role: 'admin' },

291

{ name: 'Bob', active: false, role: 'user' },

292

{ name: 'Carol', active: true, role: 'user' }

293

];

294

295

activeUsers(users); // => [Alice, Carol]

296

adminUsers(users); // => [Alice]

297

```

298

299

## Function Transformation

300

301

### unary, binary, nAry

302

Limit function arity.

303

304

```javascript { .api }

305

/**

306

* @param {Function} fn - Function to limit

307

* @returns {Function} Function accepting only specified number of arguments

308

*/

309

R.unary(fn) // Accept only 1 argument

310

R.binary(fn) // Accept only 2 arguments

311

R.nAry(n, fn) // Accept only n arguments

312

313

const logArgs = (...args) => console.log('Arguments:', args);

314

315

const logOne = R.unary(logArgs);

316

const logTwo = R.binary(logArgs);

317

318

logArgs(1, 2, 3, 4); // Logs: Arguments: [1, 2, 3, 4]

319

logOne(1, 2, 3, 4); // Logs: Arguments: [1]

320

logTwo(1, 2, 3, 4); // Logs: Arguments: [1, 2]

321

322

// Useful with higher-order functions

323

['1', '2', '3'].map(parseInt); // => [1, NaN, NaN] (parseInt gets index as 2nd arg)

324

['1', '2', '3'].map(R.unary(parseInt)); // => [1, 2, 3]

325

```

326

327

### once

328

Ensure a function is called only once.

329

330

```javascript { .api }

331

/**

332

* @param {Function} fn - Function to call only once

333

* @returns {Function} Function that can only be called once

334

*/

335

R.once(fn)

336

337

let counter = 0;

338

const increment = () => ++counter;

339

const incrementOnce = R.once(increment);

340

341

incrementOnce(); // => 1

342

incrementOnce(); // => 1 (same result, function not called again)

343

incrementOnce(); // => 1

344

345

// Real-world example: expensive initialization

346

const expensiveSetup = R.once(() => {

347

console.log('Performing expensive setup...');

348

return { initialized: true, timestamp: Date.now() };

349

});

350

351

const result1 = expensiveSetup(); // Logs message, returns object

352

const result2 = expensiveSetup(); // No log, returns same object

353

console.log(result1 === result2); // => true

354

```

355

356

### memoizeWith

357

Cache function results based on arguments.

358

359

```javascript { .api }

360

/**

361

* @param {Function} keyGen - Function to generate cache key from arguments

362

* @param {Function} fn - Function to memoize

363

* @returns {Function} Memoized function

364

*/

365

R.memoizeWith(keyGen, fn)

366

367

// Expensive fibonacci calculation

368

const fib = R.memoizeWith(R.identity, n => {

369

console.log(`Calculating fib(${n})`);

370

return n < 2 ? n : fib(n - 1) + fib(n - 2);

371

});

372

373

fib(10); // Calculates and caches intermediate results

374

fib(8); // Returns cached result immediately

375

376

// Custom cache key generation

377

const expensiveUserLookup = R.memoizeWith(

378

user => `${user.id}-${user.version}`,

379

user => {

380

console.log(`Looking up user ${user.id}`);

381

// Expensive database/API call

382

return { ...user, enrichedData: 'expensive-computation' };

383

}

384

);

385

```

386

387

## Function Analysis and Inspection

388

389

### apply

390

Apply function to argument array.

391

392

```javascript { .api }

393

/**

394

* @param {Function} fn - Function to apply

395

* @param {Array} args - Array of arguments

396

* @returns {*} Result of applying fn to args

397

*/

398

R.apply(fn, args)

399

400

const nums = [1, 2, 3, 4, 5];

401

402

R.apply(Math.max, nums); // => 5 (same as Math.max(...nums))

403

R.apply(R.add, [10, 20]); // => 30

404

R.apply(R.concat, [['a'], ['b']]); // => ['a', 'b']

405

406

// Useful in function compositions

407

const getMaxScore = R.pipe(

408

R.pluck('score'), // Extract scores: [85, 92, 78]

409

R.apply(Math.max) // Find maximum: 92

410

);

411

412

const students = [

413

{ name: 'Alice', score: 85 },

414

{ name: 'Bob', score: 92 },

415

{ name: 'Carol', score: 78 }

416

];

417

418

getMaxScore(students); // => 92

419

```

420

421

### call

422

Call function with provided arguments.

423

424

```javascript { .api }

425

/**

426

* @param {Function} fn - Function to call

427

* @param {...*} args - Arguments to pass to fn

428

* @returns {*} Result of calling fn with args

429

*/

430

R.call(fn, ...args)

431

432

R.call(R.add, 1, 2); // => 3

433

R.call(Math.max, 5, 10, 3); // => 10

434

435

// Useful in converge patterns

436

const average = R.converge(R.divide, [R.sum, R.length]);

437

const weightedAverage = R.converge(

438

R.call,

439

[

440

R.pipe(R.pluck('weight'), R.reduce(R.multiply, 1)),

441

R.pluck('value')

442

]

443

);

444

```

445

446

### juxt

447

Apply multiple functions to same arguments.

448

449

```javascript { .api }

450

/**

451

* @param {Array<Function>} fns - Array of functions to apply

452

* @returns {Function} Function that returns array of results

453

*/

454

R.juxt(fns)

455

456

const getStats = R.juxt([

457

R.length,

458

R.sum,

459

R.mean,

460

Math.min,

461

Math.max

462

]);

463

464

getStats([1, 2, 3, 4, 5]); // => [5, 15, 3, 1, 5]

465

466

// Real-world example: form validation

467

const validateUser = R.juxt([

468

R.pipe(R.prop('email'), R.test(/@/)), // Has @ in email

469

R.pipe(R.prop('age'), R.gte(R.__, 18)), // At least 18

470

R.pipe(R.prop('name'), R.complement(R.isEmpty)) // Name not empty

471

]);

472

473

const user = { email: 'alice@example.com', age: 25, name: 'Alice' };

474

validateUser(user); // => [true, true, true]

475

```

476

477

### converge

478

Apply multiple functions to same input, then combine results.

479

480

```javascript { .api }

481

/**

482

* @param {Function} converging - Function to combine results

483

* @param {Array<Function>} branching - Functions to apply to input

484

* @returns {Function} Converged function

485

*/

486

R.converge(converging, branching)

487

488

// Calculate average: sum / length

489

const average = R.converge(R.divide, [R.sum, R.length]);

490

average([1, 2, 3, 4, 5]); // => 3

491

492

// Create full name from object

493

const fullName = R.converge(R.concat, [

494

R.prop('firstName'),

495

R.pipe(R.prop('lastName'), R.concat(' '))

496

]);

497

498

fullName({ firstName: 'John', lastName: 'Doe' }); // => 'John Doe'

499

500

// Complex example: calculate compound interest

501

const compoundInterest = R.converge(

502

(principal, rate, time, compound) =>

503

principal * Math.pow(1 + rate / compound, compound * time),

504

[

505

R.prop('principal'),

506

R.prop('rate'),

507

R.prop('time'),

508

R.prop('compoundFreq')

509

]

510

);

511

512

const investment = {

513

principal: 1000,

514

rate: 0.05,

515

time: 10,

516

compoundFreq: 4

517

};

518

519

compoundInterest(investment); // => 1643.62

520

```

521

522

## Higher-Order Function Utilities

523

524

### lift, liftN

525

Lift functions to work with wrapped values (applicative functors).

526

527

```javascript { .api }

528

/**

529

* @param {Function} fn - Function to lift

530

* @returns {Function} Lifted function that works with arrays/wrapped values

531

*/

532

R.lift(fn)

533

534

// Lift binary function to work with arrays

535

const liftedAdd = R.lift(R.add);

536

liftedAdd([1, 2], [10, 20]); // => [11, 21, 12, 22]

537

538

// Lift ternary function

539

const liftedAdd3 = R.liftN(3, (a, b, c) => a + b + c);

540

liftedAdd3([1, 2], [10], [100, 200]); // => [111, 211, 112, 212]

541

542

// Real-world: generate test combinations

543

const createUser = R.liftN(3, (name, age, role) => ({ name, age, role }));

544

const testUsers = createUser(

545

['Alice', 'Bob'],

546

[25, 30],

547

['admin', 'user']

548

);

549

// => 8 user objects with all combinations

550

```

551

552

### ap

553

Apply wrapped functions to wrapped values.

554

555

```javascript { .api }

556

/**

557

* @param {Array<Function>} wrappedFns - Array of functions

558

* @param {Array} wrappedVals - Array of values

559

* @returns {Array} Results of applying each function to each value

560

*/

561

R.ap(wrappedFns, wrappedVals)

562

563

const functions = [R.multiply(2), R.add(3)];

564

const values = [1, 2, 3];

565

566

R.ap(functions, values); // => [2, 4, 6, 4, 5, 6]

567

568

// S combinator behavior with two functions

569

R.ap(R.concat, R.toUpper)('ramda'); // => 'ramdaRAMDA'

570

```

571

572

## Debugging and Error Handling

573

574

### tap

575

Execute side effect function then return original value.

576

577

```javascript { .api }

578

/**

579

* @param {Function} fn - Side effect function

580

* @param {*} x - Value to pass through

581

* @returns {*} Original value unchanged

582

*/

583

R.tap(fn, x)

584

585

// Debug pipeline values

586

const debugPipeline = R.pipe(

587

R.map(R.multiply(2)),

588

R.tap(console.log), // Logs: [2, 4, 6, 8]

589

R.filter(R.gt(R.__, 5)),

590

R.tap(console.log), // Logs: [6, 8]

591

R.reduce(R.add, 0)

592

);

593

594

debugPipeline([1, 2, 3, 4]); // => 14

595

596

// Functional logging

597

const logAndReturn = R.tap(x => console.log('Processing:', x));

598

logAndReturn(42); // Logs "Processing: 42", returns 42

599

600

// Side effects in chains

601

const processUser = R.pipe(

602

R.tap(user => analytics.track('user_processed', user)),

603

R.assoc('processed', true),

604

R.tap(user => cache.set(user.id, user))

605

);

606

```

607

608

### tryCatch

609

Functional error handling with try/catch logic.

610

611

```javascript { .api }

612

/**

613

* @param {Function} tryer - Function to attempt

614

* @param {Function} catcher - Function to handle errors

615

* @returns {Function} Function that returns tryer result or catcher result on error

616

*/

617

R.tryCatch(tryer, catcher)

618

619

// Safe JSON parsing

620

const safeParseJSON = R.tryCatch(JSON.parse, R.always({}));

621

safeParseJSON('{"valid": true}'); // => {valid: true}

622

safeParseJSON('invalid json'); // => {}

623

624

// Safe property access

625

const safeProp = key => R.tryCatch(R.prop(key), R.always(null));

626

const getName = safeProp('name');

627

getName({name: 'John'}); // => 'John'

628

getName(null); // => null (no error)

629

630

// API error handling

631

const fetchUserData = id => R.tryCatch(

632

() => api.getUser(id),

633

(error) => ({ error: error.message, id })

634

);

635

636

// Compose with other functions

637

const processData = R.pipe(

638

R.tryCatch(JSON.parse, R.always({})),

639

R.prop('data'),

640

R.defaultTo([])

641

);

642

```

643

644

### addIndex

645

Add index parameter to iteration functions.

646

647

```javascript { .api }

648

/**

649

* @param {Function} fn - Iteration function (like map, filter)

650

* @returns {Function} New function that passes index as second parameter

651

*/

652

R.addIndex(fn)

653

654

// Map with index

655

const mapIndexed = R.addIndex(R.map);

656

mapIndexed((val, idx) => `${idx}: ${val}`, ['a', 'b', 'c']);

657

// => ['0: a', '1: b', '2: c']

658

659

// Filter with index

660

const filterIndexed = R.addIndex(R.filter);

661

filterIndexed((val, idx) => idx % 2 === 0, ['a', 'b', 'c', 'd']);

662

// => ['a', 'c'] (even indices)

663

664

// Real-world: create numbered lists

665

const createNumberedList = R.addIndex(R.map)((item, index) =>

666

`${index + 1}. ${item}`

667

);

668

createNumberedList(['Buy milk', 'Walk dog', 'Code']);

669

// => ['1. Buy milk', '2. Walk dog', '3. Code']

670

671

// Combine with other operations

672

const processWithIndex = R.pipe(

673

R.addIndex(R.map)((item, idx) => ({ ...item, position: idx })),

674

R.filter(R.propEq('active', true))

675

);

676

```

677

678

These function utilities enable powerful abstractions and elegant solutions to complex programming problems through composition, partial application, and higher-order transformations.