This post is not meant to be a suggestion that you should use this code for anything. I found the exploration educational and I’m sharing because I find the results interesting. This post is a Literate Haskell file.

Programmers often mean different things when they say “cast”. One thing they sometimes mean is to be able to use a value of one type as another type, converting as possible.

We’ll use dynamic typing to allow us to check the conversions at runtime.

```
> module DynamicCast (DynamicCastable(..), dynamicCast, Opaque) where
> import Data.Dynamic
> import Data.Void
> import Control.Applicative
> import Control.Monad
> import Data.Typeable
> import Text.Read (readMaybe)
> import Data.Traversable (sequenceA)
```

But we don’t want to expose the dynamic typing outside of this module, in case people become confused and try to use a Dynamic they got from elsewhere. Really we’re just using the Dynamic as an opaque intermediate step.

`> newtype Opaque = Opaque Dynamic`

Types can define how they both enter and exit the intermediate representation. This both allows casting existing types to new types, but also can allow casting new types to existing types without changing the instances for those existing types.

```
> class (Typeable a) => DynamicCastable a where
> toOpaque :: a -> Opaque
> toOpaque = Opaque . toDyn
>
> fromOpaque :: Opaque -> Maybe a
> fromOpaque (Opaque dyn) = fromDynamic dyn
```

And finally the cast itself.

```
> dynamicCast :: (DynamicCastable a, DynamicCastable b) => a -> Maybe b
> dynamicCast = fromOpaque . toOpaque
```

Let’s see some examples.

We’ll say that Integer and simple lists (including String) represent themselves and define no specific conversions.

```
> instance DynamicCastable Integer
> instance (Typeable a) => DynamicCastable [a]
```

Int is represented however Integer represents itself.

Anything that can convert to Integer can convert to Int.

Any String that parses using read as an Int can also convert to an Int.

```
> instance DynamicCastable Int where
> toOpaque = toOpaque . toInteger
> fromOpaque o = fromInteger <$> fromOpaque o <|> (readMaybe =<< fromOpaque o)
```

And now

`dynamicCast (1 :: Int) :: Maybe Integer`

Just 1

`dynamicCast (1 :: Integer) :: Maybe Int`

Just 1

This is pretty obvious and boring, but perhaps it gives us confidence that this is going to work at all. Let’s try something fancier.

Void is the type with no inhabitants, so it can never be converted to.

```
> instance DynamicCastable Void where
> fromOpaque _ = Nothing
```

Either is represented as just the item it contains, and any item can be contained in an Either.

```
> instance (DynamicCastable a, DynamicCastable b) => DynamicCastable (Either a b) where
> toOpaque (Left x) = toOpaque x
> toOpaque (Right x) = toOpaque x
>
> fromOpaque o = Left <$> fromOpaque o <|> Right <$> fromOpaque o
```

And now

`dynamicCast 1 :: Maybe (Either Int Void)`

Just (Left 1)

`dynamicCast 1 :: Maybe (Either Void Int)`

Just (Right 1)

`dynamicCast (Left 1 :: Either Int Void) :: Maybe Int`

Just 1

Maybe is very similar, store the Just as the unwrapped value, and store Nothing as Void.

```
> instance (DynamicCastable a) => DynamicCastable (Maybe a) where
> toOpaque (Just x) = toOpaque x
> toOpaque Nothing = toOpaque (undefined :: Void)
>
> fromOpaque = fmap Just . fromOpaque
```

`dynamicCast (Left 1 :: Either Int Void) :: Maybe (Maybe Int)`

Just (Just 1)

To be able to cast the contents of a Functor, the possible failure also lives in the Functor, so we need a wrapper.

```
> newtype FunctorCast f a = FunctorCast (f (Maybe a))
> mkFunctorCast :: (Functor f) => f a -> FunctorCast f a
> mkFunctorCast = FunctorCast . fmap Just
> runFunctorCast :: FunctorCast f a -> f (Maybe a)
> runFunctorCast (FunctorCast x) = x
> runTraversableFunctorCast :: (Traversable f) => Maybe (FunctorCast f a) -> Maybe (f a)
> runTraversableFunctorCast = join . fmap (sequenceA . runFunctorCast)
> instance (Functor f, DynamicCastable a, Typeable f) => DynamicCastable (FunctorCast f a) where
> toOpaque = Opaque . toDyn . fmap toOpaque . runFunctorCast
> fromOpaque (Opaque dyn) = FunctorCast . fmap fromOpaque <$> fromDynamic dyn
```

`runTraversableFunctorCast $ dynamicCast (mkFunctorCast ["1"]) :: Maybe [Either Void Integer]`

Just [Right 1]

## 4 Responses

## Stephen Diehl •

Stephen Diehl reposted this Article on twitter.com.

## Ondřej Súkup •

Ondřej Súkup liked this Article on twitter.com.

## hardentoo •

hardentoo reposted this Article on twitter.com.

## singpolyma •

singpolyma reposted this Article on lobste.rs.