Dynamic typing

Haskell has a type called Dynamic which resembles a dynamically-typed variable in some other languages. Dynamic is a sort of wrapper that can hold a value of any type.

We might use this, for example, to construct a heterogeneous list – that is, a list containing elements of different types.

The function toDyn creates a Dynamic. Here we apply it to arguments of three different types – a Bool, an Integer, and a String – to construct a list of three Dynamics.

import Data.Dynamic
import Data.Foldable

mixedList =
    [ toDyn True
    , toDyn (5 :: Integer)
    , toDyn "hey"
    ]

This example program iterates over the list and prints a message describing each element. (We will define the message function later below.)

main =
    for_ mixedList $ \d ->
        putStrLn (message d)

The only thing we can do with a Dynamic is use the fromDynamic function to attempt to cast it back to its original type. If the cast succeeds, it returns Just the original value; otherwise it returns Nothing.

We have written the recognizeType function to generate two kinds of messages: one if the dynamic type is Integer, and another if the type is Bool. The asum function chooses the first Just among the list of Maybes.

recognizeType d =
  asum
    [ (fromDynamic d :: Maybe Integer) >>= \x ->
        Just (show x ++ " is an integer")
    , (fromDynamic d :: Maybe Bool) >>= \x ->
        Just ("The answer is " ++ (if x then "yes" else "no"))
    ]

It is possible that neither of the possibilities above succeeded. A Dynamic can hold a value of any type, and since there is an unlimited number of types in the world, we cannot check them all. So we have a last resort: if all else fails and the asum produces Nothing, we return an apologetic message that contains the name of the type.

message d =
    case (recognizeType d) of
        Just x -> x
        Nothing -> "Unrecognized type: " ++ show (dynTypeRep d)

Since we neglected to write a case to handle the String type, the third message says “unrecognized type”.

$ runhaskell dynamic.hs
The answer is yes
5 is an integer
Unrecognized type: [Char]