# Basics 19 - Double-Lifted Data

%[https://youtu.be/x3Sl2w0t4Ak] 

# What is Double-Lifted Data

Consider this:

```plaintext
let opt: int option = Some 1
let res: Result<int option, exn> = Ok opt
```

1. `opt` is an `int option`, which means value 1 is packed in `option` container
    
2. `opt` is then packed in a `Result`
    
3. Simply put, 1 is double-lifted
    

While any combination of double-lifting is theoretically possible, some of the cases you will practically and frequently encounter are:

1. `Result<'T option, 'TError>`:
    
    1. Indicates the result of an operation that may fail
        
    2. In `Ok` case, you may or may not have data
        
    3. In `Error` case, you have an exception
        
    4. Example: executing a DB query that gets one result/row/document. There are various outcomes:
        
        1. DB operation failed, `Error` case
            
        2. DB operation succeeded, found the record, `Ok` case -&gt; `Some` case
            
        3. DB operation succeeded, no record found, `Ok` case -&gt; `None` case
            
2. `Result<'T seq, 'TError> or Result<'T list, 'TError> or Result<'T array, 'TError>`:
    
    1. Indicates the result of an operation that may fail
        
    2. In `Ok` case, you will get a `seq`/`list`/`array` of items
        
    3. In `Error` case, you have an exception
        
    4. Example: executing a DB query that gets multiple results/rows/documents. There are various outcomes:
        
        1. DB operation failed, `Error` case
            
        2. DB operation succeeded, found the records, `Ok` case -&gt; `'T seq/list/array`
            
            1. In `Ok` case, there may be 0 or more items in `'T seq/list/array`
                

# Problem with Double-Lifted Data

Consider this:

```plaintext
let res: Result<int option, exn> = Ok (Some 1)

let add1 num = num + 1
```

1. We have `add1` function which can be used for mapping
    
2. How do we `use` add1 with `res`?
    
3. `Result.map add1 res` won't work. In this case `Result.map` expects a function (mapper) that operates on `int option`, not `int`.
    
4. There is no point in changing `add1`; `add1` is our business logic, typically reusable
    

There are various ways to solve this issue.

## Wrapper Function or Lamba Expression

The wrapper function `add1Wrapper` (or the lambda expression) takes `int option` as input:

```plaintext
let add1 num = num + 1
let add1Wrapper opt = Option.map add1 opt

Result.map add1Wrapper res

//// OR ////

Result.map (fun opt -> Option.map add1 opt) res
```

The problem with this approach is that the burden is now yours... for how many such functions can you create wrappers or lambda expressions.

## Lifted Function

Remember `map` function can be used as a function lifter. Function `liftedAdd1` is the lifted version of `add1`:

```plaintext
let add1 num = num + 1

let liftedAdd1 = Option.map add1

Result.map liftedAdd1 res

//// OR ////

let x = Result.map (Option.map add1) res
```

The problem with this approach, once again, is that the burden is yours... for how many such functions can you create lifted versions.

Let's look at another approach.

# Functions that are aware of Double-Lifted Data

```plaintext
module Result =
    let optMap mapper result = Result.map (Option.map mapper) result
    let optBind binder result = Result.map (Option.bind binder) result
```

Here we have created two functions `optMap` and `optBind`, and put those in the `Result` module. This is done for consistency, you can call `Result.optMap` and `Result.optBind`.

```plaintext
let add1 num = num + 1

let add1ifEven num =
    if num % 2 = 0 then Some(num + 1) else None

let res: Result<int option, exn> = Ok(Some 1)

Result.optMap add1 res |> printfn "%A" // Ok (Some 2)

Result.optBind add1ifEven res |> printfn "%A" // Ok None
```

# Concluding Remarks

1. Dealing with double-lifted data isn't the most straight-forward
    
2. For rare cases, you may use lambda expressions or lifted functions
    
3. Best to create a library of functions that are aware of double-lifted data, as we will in coming articles
    

If you have reached so far, congratulations.

Keep reading!
