or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

conversions.mdgeneric.mdhlist.mdhmap.mdindex.mdlift.mdnat.mdpoly.mdrecords.mdsized.mdsybclass.mdtypeable.mdtypeoperators.md

lift.mddocs/

0

# Function Lifting

1

2

The Lift module in shapeless provides utilities for lifting ordinary functions of arbitrary arity into various contexts, such as Option. This enables functional programming patterns where operations can safely handle absent values.

3

4

## Core Functionality

5

6

### LiftO - Lifting into Option

7

8

```scala { .api }

9

object Lift {

10

/**

11

* Lifts a function of arbitrary arity into Option context

12

* All arguments must be Some for function to be applied

13

*/

14

def liftO[InF, InL <: HList, R, OInL <: HList, OutF](f: InF)

15

(implicit

16

hlister: FnHListerAux[InF, InL => R],

17

mapper: MapperAux[get.type, OInL, InL],

18

folder: MapFolder[OInL, Boolean, isDefined.type],

19

unhlister: FnUnHListerAux[OInL => Option[R], OutF]

20

): OutF

21

}

22

```

23

24

The `liftO` function transforms regular functions to work with Option types, automatically handling the case where any argument is None.

25

26

**Usage Examples:**

27

28

```scala

29

import shapeless._

30

31

// Lift binary function

32

val add: (Int, Int) => Int = _ + _

33

val addOption = Lift.liftO(add) // (Option[Int], Option[Int]) => Option[Int]

34

35

val result1 = addOption(Some(5), Some(3)) // Some(8)

36

val result2 = addOption(Some(5), None) // None

37

val result3 = addOption(None, Some(3)) // None

38

39

// Lift ternary function

40

val combine: (String, Int, Boolean) => String =

41

(s, i, b) => s"$s-$i-$b"

42

43

val combineOption = Lift.liftO(combine)

44

// Type: (Option[String], Option[Int], Option[Boolean]) => Option[String]

45

46

val combined1 = combineOption(Some("hello"), Some(42), Some(true))

47

// Some("hello-42-true")

48

49

val combined2 = combineOption(Some("hello"), None, Some(true))

50

// None

51

```

52

53

## Arity Support

54

55

The `liftO` function works with functions of any arity from 0 to 22:

56

57

### Nullary Functions

58

59

```scala

60

import shapeless._

61

62

val constant: () => String = () => "fixed"

63

val constantOption = Lift.liftO(constant) // () => Option[String]

64

65

val result = constantOption() // Some("fixed")

66

```

67

68

### Unary Functions

69

70

```scala

71

import shapeless._

72

73

val double: Int => Int = _ * 2

74

val doubleOption = Lift.liftO(double) // Option[Int] => Option[Int]

75

76

val doubled1 = doubleOption(Some(21)) // Some(42)

77

val doubled2 = doubleOption(None) // None

78

```

79

80

### Higher Arity Functions

81

82

```scala

83

import shapeless._

84

85

// Quaternary function

86

val process4: (String, Int, Double, Boolean) => String =

87

(s, i, d, b) => s"$s:$i:$d:$b"

88

89

val process4Option = Lift.liftO(process4)

90

// Type: (Option[String], Option[Int], Option[Double], Option[Boolean]) => Option[String]

91

92

val result = process4Option(Some("test"), Some(1), Some(2.5), Some(false))

93

// Some("test:1:2.5:false")

94

95

// Missing any argument results in None

96

val resultNone = process4Option(Some("test"), None, Some(2.5), Some(false))

97

// None

98

```

99

100

## Advanced Usage Patterns

101

102

### Lifting Complex Operations

103

104

```scala

105

import shapeless._

106

107

// Complex business logic function

108

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

109

case class Product(name: String, price: Double)

110

case class Order(user: User, product: Product, quantity: Int)

111

112

val createOrder: (User, Product, Int) => Order = Order.apply

113

val createOrderOption = Lift.liftO(createOrder)

114

// Type: (Option[User], Option[Product], Option[Int]) => Option[Order]

115

116

val user = Some(User("Alice", 30))

117

val product = Some(Product("Widget", 19.99))

118

val quantity = Some(2)

119

120

val order = createOrderOption(user, product, quantity)

121

// Some(Order(User("Alice", 30), Product("Widget", 19.99), 2))

122

123

// Missing user information

124

val failedOrder = createOrderOption(None, product, quantity)

125

// None

126

```

127

128

### Pipeline Operations

129

130

```scala

131

import shapeless._

132

133

// Chain lifted functions

134

val parseInput: String => Int = Integer.parseInt

135

val validatePositive: Int => Int = i => if (i > 0) i else throw new Exception("negative")

136

val doubleValue: Int => Int = _ * 2

137

val formatOutput: Int => String = i => s"Result: $i"

138

139

// Lift each function

140

val parseInputOption = Lift.liftO(parseInput) // Option[String] => Option[Int]

141

val validatePositiveOption = Lift.liftO(validatePositive) // Option[Int] => Option[Int]

142

val doubleValueOption = Lift.liftO(doubleValue) // Option[Int] => Option[Int]

143

val formatOutputOption = Lift.liftO(formatOutput) // Option[Int] => Option[String]

144

145

// Create pipeline (in practice you'd handle exceptions properly)

146

def safePipeline(input: Option[String]): Option[String] = {

147

val parsed = parseInputOption(input)

148

val validated = parsed.flatMap(i => try { Some(validatePositive(i)) } catch { case _ => None })

149

val doubled = doubleValueOption(validated)

150

formatOutputOption(doubled)

151

}

152

153

val result1 = safePipeline(Some("21")) // Some("Result: 42")

154

val result2 = safePipeline(Some("-5")) // None (validation fails)

155

val result3 = safePipeline(Some("abc")) // None (parsing fails)

156

val result4 = safePipeline(None) // None (no input)

157

```

158

159

### Working with Case Classes

160

161

```scala

162

import shapeless._

163

164

case class Point3D(x: Double, y: Double, z: Double)

165

case class Vector3D(x: Double, y: Double, z: Double)

166

167

val createPoint: (Double, Double, Double) => Point3D = Point3D.apply

168

val createVector: (Double, Double, Double) => Vector3D = Vector3D.apply

169

170

val createPointOption = Lift.liftO(createPoint)

171

val createVectorOption = Lift.liftO(createVector)

172

173

// Safe construction from potentially missing coordinates

174

def safePoint(x: Option[Double], y: Option[Double], z: Option[Double]): Option[Point3D] =

175

createPointOption(x, y, z)

176

177

def safeVector(x: Option[Double], y: Option[Double], z: Option[Double]): Option[Vector3D] =

178

createVectorOption(x, y, z)

179

180

val point = safePoint(Some(1.0), Some(2.0), Some(3.0)) // Some(Point3D(1.0, 2.0, 3.0))

181

val invalidPoint = safePoint(Some(1.0), None, Some(3.0)) // None

182

183

// Vector operations

184

val addVectors: (Vector3D, Vector3D) => Vector3D =

185

(v1, v2) => Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z)

186

187

val addVectorsOption = Lift.liftO(addVectors)

188

189

val v1 = Some(Vector3D(1, 2, 3))

190

val v2 = Some(Vector3D(4, 5, 6))

191

val sum = addVectorsOption(v1, v2) // Some(Vector3D(5.0, 7.0, 9.0))

192

```

193

194

### Integration with Validation

195

196

```scala

197

import shapeless._

198

199

// Validation functions

200

def validateEmail(email: String): Option[String] =

201

if (email.contains("@")) Some(email) else None

202

203

def validateAge(age: Int): Option[Int] =

204

if (age >= 0 && age <= 120) Some(age) else None

205

206

def validateName(name: String): Option[String] =

207

if (name.nonEmpty) Some(name) else None

208

209

// User creation function

210

case class ValidUser(name: String, email: String, age: Int)

211

val createUser: (String, String, Int) => ValidUser = ValidUser.apply

212

val createUserOption = Lift.liftO(createUser)

213

214

// Safe user creation with validation

215

def createValidUser(name: String, email: String, age: Int): Option[ValidUser] = {

216

val validName = validateName(name)

217

val validEmail = validateEmail(email)

218

val validAge = validateAge(age)

219

220

createUserOption(validName, validEmail, validAge)

221

}

222

223

val user1 = createValidUser("Alice", "alice@example.com", 30) // Some(ValidUser(...))

224

val user2 = createValidUser("", "alice@example.com", 30) // None (empty name)

225

val user3 = createValidUser("Alice", "invalid-email", 30) // None (bad email)

226

val user4 = createValidUser("Alice", "alice@example.com", -5) // None (invalid age)

227

```

228

229

## Implementation Details

230

231

The `liftO` function uses several type classes internally:

232

233

- **FnHLister**: Converts function to HList function

234

- **Mapper**: Maps `get` over Option HList to extract values

235

- **MapFolder**: Folds over Option HList with `isDefined` to check all are present

236

- **FnUnHLister**: Converts HList function back to regular function

237

238

This enables the lifting to work generically across functions of any arity while maintaining type safety.

239

240

Function lifting provides a powerful way to work with potentially absent values in a compositional manner, enabling robust and safe functional programming patterns.