Fetching contributors…
Cannot retrieve contributors at this time
153 lines (105 sloc) 3.96 KB

Updating variants

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 :ref:`nubVariant <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)```

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
```

Mapping only the first matching type: `mapVariantFirst`

A variant can have the same type more than once in its value type list. :ref:`mapVariant <mapVariant>` updates all the matching types in the list but sometimes that's not what we want. We can use :ref:`mapVariantAt <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])
```

TODO

• foldMapVariantFirst[M]
• foldMapVariant
• alterVariant
• traverseVariant