RE: simulating dynamic dispatch
You might also find the 'cast' function in Section 3 of "Scrap your boilerplate" useful. http://research.microsoft.com/~simonpj/papers/hmap/ I'm not certain, but it has the right smell. Simon | -----Original Message----- | From: oleg@pobox.com [mailto:oleg@pobox.com] | Sent: 21 March 2003 04:19 | To: hdaume@ISI.EDU; haskell@haskell.org | Subject: Re: simulating dynamic dispatch | | | > i'm hoping to be able to simulate a sort of dynamic dispatch based on | > class instances. | | It seems you want to dispatch based not on a type but on the | constraint of a type. | | You code almost worked. Here's the a bit updated and working version. | | class Foo a where { foo :: a -> Bool } | class Bar a where { bar :: a -> Bool } | | data FB = forall a . Foo a => MkFoo a | forall a . Bar a => MkBar a | | instance Foo FB where | foo (MkFoo x) = foo x | | instance Bar FB where | bar (MkBar x) = bar x | | -- some instances for the test | instance Foo Int where | foo x = x == 0 | | instance Bar Char where | bar x = x == 'a' | | | test x = case x of | (MkFoo a) -> Just $ foo a | (MkBar a) -> Just $ bar a | -- _ -> Nothing | | | -- *Main> test $ MkFoo (0::Int) | -- Just True | -- *Main> test $ MkFoo (10::Int) | -- Just False | -- *Main> test $ MkBar 'a' | -- Just True | -- *Main> test $ MkBar 'b' | -- Just False | | _______________________________________________ | Haskell mailing list | Haskell@haskell.org | http://www.haskell.org/mailman/listinfo/haskell
Excellent paper! I have started using some of the techniques already! But, back to the real question :). I now no longer think such a thing is possible. Essentially what I want (given 'cast :: Typeable a, Typeable b => a -> Maybe b' [1]), is to write something like: test :: forall a r . Typeable a => (forall b . (Typeable b, Foo b) => b -> r) -> a -> Maybe r test f a = liftM f (cast a) but then 'b' is ambiguous. I'm not sure there is any way around this. Can someone help? - Hal [1] is there any reason why parens are requires around class contexts? i've always found this odd... -- Hal Daume III | hdaume@isi.edu "Arrest this man, he talks in maths." | www.isi.edu/~hdaume On Fri, 21 Mar 2003, Simon Peyton-Jones wrote:
You might also find the 'cast' function in Section 3 of "Scrap your boilerplate" useful. http://research.microsoft.com/~simonpj/papers/hmap/ I'm not certain, but it has the right smell. Simon
| -----Original Message----- | From: oleg@pobox.com [mailto:oleg@pobox.com] | Sent: 21 March 2003 04:19 | To: hdaume@ISI.EDU; haskell@haskell.org | Subject: Re: simulating dynamic dispatch | | | > i'm hoping to be able to simulate a sort of dynamic dispatch based on | > class instances. | | It seems you want to dispatch based not on a type but on the | constraint of a type. | | You code almost worked. Here's the a bit updated and working version. | | class Foo a where { foo :: a -> Bool } | class Bar a where { bar :: a -> Bool } | | data FB = forall a . Foo a => MkFoo a | forall a . Bar a => MkBar a | | instance Foo FB where | foo (MkFoo x) = foo x | | instance Bar FB where | bar (MkBar x) = bar x | | -- some instances for the test | instance Foo Int where | foo x = x == 0 | | instance Bar Char where | bar x = x == 'a' | | | test x = case x of | (MkFoo a) -> Just $ foo a | (MkBar a) -> Just $ bar a | -- _ -> Nothing | | | -- *Main> test $ MkFoo (0::Int) | -- Just True | -- *Main> test $ MkFoo (10::Int) | -- Just False | -- *Main> test $ MkBar 'a' | -- Just True | -- *Main> test $ MkBar 'b' | -- Just False | | _______________________________________________ | Haskell mailing list | Haskell@haskell.org | http://www.haskell.org/mailman/listinfo/haskell
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.
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
participants (3)
-
Hal Daume III -
oleg@pobox.com -
Simon Peyton-Jones