Polishing the boilerplate: a dis-unsafely safe cast

Hal Daume III hdaume@ISI.EDU
Mon, 24 Mar 2003 16:34:40 -0800 (PST)


This is getting off-topic, but...

I don't like this solution at all :).  It is very dependent on read being
a true inverse of show, which isn't always the case.  Perhaps more
importantly though, I don't like the fact that it will readily convert
between Int and Float, etc.  For instance, in almost all of my
applications I use something that looks like:

> newtype Log = Log !Double

where 'show (Log d) = show (exp l)' and 'read s = Log (log (read s))'.  If
I wanted to have a function applied to only Logs, having it also applied
to Floats/Doubles/etc because of the show/read connection would be very
bad and would require me to wrap things up in more and more constructors
just to get unique show instances.

Not to mention that it's terribly inefficient.

If our concern is to stay within a standard subset of Haskell, using
H98+FFI, we get unsafePerformIO with which we can write
unsafeCoerce.  Using simply String ids as in the referenced paper would
make this sort-of-safe, provided users don't give bogus instances of
Typeable.

--
 Hal Daume III                                   | hdaume@isi.edu
 "Arrest this man, he talks in maths."           | www.isi.edu/~hdaume

On Mon, 24 Mar 2003 oleg@pobox.com wrote:

> 
> 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.
> _______________________________________________
> Haskell mailing list
> Haskell@haskell.org
> http://www.haskell.org/mailman/listinfo/haskell
>