Static typing and dynamic typing usually are offering null references as solution for the not an object problem. With strong-static typing systems, there are not null references, because there is nothing that can be treated as wild-card type, as Object() in Java and void * in C and C++ — everything have a type. So, there are some flow control structures allowing the programmer to handle the not an object problem using composite data types. Classical examples are the Maybe Monad and the Either Monad in Haskell. Both of them, allow you to control the behaviour of the program using a composite type to bind null reference requirements.
-- | The type of strict optional values. data Maybe a = Nothing | Just !a deriving (Eq, Ord, Show, Read)
Maybe is a composite data type with algebraic form which encapsulates a type instance or an empty instance called Nothing. It meets the Monad requirements since it has the curried form * → * and allows you to hide the real instance inside all Monadic operations of the encapsulated instance. How it is being used? Please review the following example.
module Main (main) where import Data.Maybe sum2 :: Maybe Int -> Maybe Int -> Maybe Int sum2 Nothing Nothing = Nothing sum2 Nothing n = Nothing sum2 n Nothing = Nothing sum2 (Just n) (Just m) = Just $ n + m main :: IO () main = let a = sum2 (Just 10) Nothing in print $ fromJust a
The example above receives a two composite Maybe Int variables and returns a Maybe Int instance. We can tell that the function above is safe, because it handles null references properly. Only with Just !a input variables it is executing the sum2 operation, in other case, it preserves the Nothing instance. To make it safe, we can ask if the resulting output is really a valid variable that can be handled by fromJust function.
main :: IO () main = let a = sum2 (Just 10) Nothing in if isNothing a then print "Nothing" else print $ fromJust a
But there is no not an object reference “until it is declared”. So, this is a composite type of kind Algebraic Data Type due to its curried form. But there is another powerful composite type that allows you to encapsulate dual results, not as it is being made with the Maybe Monad, but with two type variables allowing the composition between two optional types, which is called Either Monad.
-- | The strict choice type. data Either a b = Left !a | Right !b deriving (Eq, Ord, Read, Show)
Either encapsulates the type variables a and b. For example, due to its strong-static typing nature, Haskell does not allow to operate between floating point numbers and integer numbers, so we need explicit transformation functions to operate on them. So, the following example sum3 works with this approach and a type * → * → *.
module Main (main) where import Data.Either sum3 :: Either Int Float -> Either Int Float -> Either Int Float sum3 (Right a) (Left b) = Right $ a + fromIntegral b sum3 (Left a) (Right b) = Right $ fromIntegral a + b sum3 (Right a) (Right b) = Right $ a + b sum3 (Left a) (Left b) = Left $ a + b main :: IO () main = let a = sum3 (Right 0.1) (Right 0.2) in case a of Left n -> print $ "Left: " ++ show n Right n -> print $ "Right: " ++ show n
So, the Either data type on the example above is encapsulating the Int and Float types. And you can combine many composite types as you need, for example we can do another version handling Either (Maybe Int) Float and still it will be valid, allowing null references for the integer part of the composite type. So, the limit on null references is embedded in the language, and usage of null references or not an object instances depends exclusively on you. Just imagine the amount of errors that can be reduced with strong-static typing.