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
add a b = do
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