Polishing the boilerplate: a dis-unsafely safe cast

oleg@pobox.com oleg@pobox.com
Mon, 24 Mar 2003 11:20:29 -0800 (PST)


Hello!

	The paper
	http://research.microsoft.com/~simonpj/papers/hmap/
by Ralf Laemmel and Simon Peyton Jones gave a reference
implementation of a 'cast' function. Here's another implementation:

cast:: (Show a, Read b) => a -> Maybe b

cast = read_as . show
 where
   read_as s = case readsPrec 1 s of
                [(r,[])] -> Just r
		_        -> Nothing
		
   
-- Tests from the hmap paper

-- *Main> (cast 'a') :: Maybe Char
-- Just 'a'
-- *Main> (cast 'a') :: Maybe Int 
-- Nothing
-- *Main> (cast 'a') :: Maybe Bool
-- Nothing
-- *Main> (cast True) :: Maybe Bool
-- Just True
-- *Main> (cast "True") :: Maybe Bool
-- Nothing
-- *Main> (cast "True") :: Maybe Int 
-- Nothing
-- *Main> (cast "True") :: Maybe String
-- Just "True"

-- Additional tests

-- *Main> (cast [1,2,3])::Maybe [Int]
-- Just [1,2,3]
-- *Main> (cast [1,2,3])::Maybe [Integer]
-- Just [1,2,3]
-- *Main> (cast [1,2,3])::Maybe [Float]
-- Just [1.0,2.0,3.0]
-- *Main> (cast (Just True)) :: Maybe Int
-- Nothing
-- *Main> (cast (Just True)) :: Maybe (Maybe Bool)
-- Just (Just True)
-- *Main> (cast (Nothing::Maybe Bool)) :: Maybe (Maybe Bool)
-- Just Nothing
-- *Main> (cast (Nothing::Maybe Bool)) :: Maybe (Maybe Int)
-- Just Nothing
-- *Main> (cast (Nothing::Maybe Bool)) :: Maybe (Maybe Char)
-- Just Nothing


Granted, the cast function here cannot handle exponential types. OTH,
the cast function given here can cast an Int to an Integer or to a Float. Most
of all, the cast function here is implemented entirely in Haskell 98,
with no extensions whatsoever -- not existential types, let alone
unsafeCoerce.


An aside: OCaml has a function called Obj.magic: 'a -> 'b,
which is unsafeCoerce with a positive name. If OCaml did not provide
that function, it can be easily emulated:

# let door_to_hell x =
     Marshal.from_string (Marshal.to_string () []) 0;;
val door_to_hell : 'a -> 'b = <fun>

This is a terminating function with a signature a->b. It critically
relies on marshalling and unmarshalling. BTW, the function breaks all
kinds of free theorems:

#  let (fake_id: 'a->'a) = function x -> door_to_hell x;;
val fake_id : 'a -> 'a = <fun>
# fake_id 123;;
- : int = 0

We can write a totally polymorphic terminating function a->a that is
patently not the identity.