6. Updating variants¶

6.1. Mapping a single type: `mapVariant`¶

We can easily apply a function `f :: A -> B` to a variant so that its value type `A` is replaced with `B`. If the value in the variant has type `A`, then `f` is applied to it to get the new value. Example:

```x,y :: V '[String,Int]
x = V "test"
y = V @Int 10

> mapVariant ((+5) :: Int -> Int) x
V @String "test"

> mapVariant ((+5) :: Int -> Int) y
V @Int 15
```

Note that the resulting variant may contain the same type more than once. To avoid this, we can either use nubVariant or directly use `mapNubVariant`:

```> :t mapVariant (length :: String -> Int) x
mapVariant (length :: String -> Int) x :: V '[Int, Int]

> :t mapNubVariant (length :: String -> Int) x
mapNubVariant (length :: String -> Int) x :: V '[Int]

> mapNubVariant (length :: String -> Int) x
V @Int 4
```

Generic code can be written with the `MapVariant a b cs` constraint and the `ReplaceAll` type family so that: ```mapVariant :: MapVariant a b cs => (a -> b) -> V cs -> V (ReplaceAll a b cs)```

6.2. Mapping by index: `mapVariantAt`¶

If we know the index of the value type we want to map, we can use `mapVariantAt`. Example:

```x,y :: V '[String,Int]
x = V "test"
y = V @Int 10

> mapVariantAt @0 length x
V @Int 4

> mapVariantAt @0 length y
V @Int 10

> mapVariantAt @1 (+5) x
V @[Char] "test"

> mapVariantAt @1 (+5) y
V @Int 15
```

Note that the compiler uses the type of the element whose index is given as first argument to infer the type of the functions `length` and `+5`, hence we don’t need type ascriptions.

We can use `mapVariantAtM` to perform an applicative (or monadic) update. For example:

```add :: Int -> Int -> IO Integer
putStrLn "Converting the result into Integer!"
return (fromIntegral a + fromIntegral b)

> mapVariantAtM @1 (add 5) x
V @[Char] "test"

> mapVariantAtM @1 (add 5) y
Converting the result into Integer!
V @Integer 15
```

6.3. Mapping only the first matching type: `mapVariantFirst`¶

A variant can have the same type more than once in its value type list. mapVariant updates all the matching types in the list but sometimes that’s not what we want. We can use mapVariantAt if we know the index of the type we want to update. We can also use `mapVariantFirst` as follows if we want to update only the first matching type:

```vv :: V '[Int,Int,Int]
vv = toVariantAt @1 5

> r0 = mapVariant (show :: Int -> String) vv
> r1 = mapVariantFirst (show :: Int -> String) vv

> :t r0
r0 :: V '[String,String,String]

> :t r1
r1 :: V '[String, Int, Int]

> r0
V @[Char] "5"

> r1
V @Int 5
```

We can also apply an applicative (or monadic) function with `mapVariantFirstM`:

```printRetShow :: Show a => a -> IO String
printRetShow a = do
print a
return (show a)

> r2 = mapVariantFirstM (printRetShow @Int) vv
> r2
V @Int 5

> :t r2
r2 :: IO (V '[String, Int, Int])
```

6.4. TODO¶

• foldMapVariantFirst[M]
• foldMapVariant
• alterVariant
• traverseVariant