
This came up in #haskell -- rather than just provide coerce, cast (or a primitive that cast can be built on, as well as other things) can give a type equality witness of some kind. Some possible signatures are: cast :: (Typeable a, Typeable b) => Maybe (p a -> p b) -- Leibniz-style equality cast :: (Typeable a, Typeable b) => p a -> Maybe (p b) -- another form cast :: (Typeable a, Typeable b) => Maybe (Is a b) -- GADT-style equality cast :: (Typeable a, Typeable b) => MightBe a b -- another form -- With data Is a b where { Refl :: Is a a } -- standard equality GADT data MightBe a b where -- version with built-in Maybe, for more convenient types Is :: MightBe a a Isn't :: MightBe a b Any of these lets you write any of the other ones, of course. But "cast" doesn't let you write any of them, because it only goes in one direction. The GADT form can let you write code like foo :: forall a. Typeable a => a -> ... foo x | Is <- cast :: MightBe a Int = ...x... | Is <- cast :: MightBe a Char = ...x... | otherwise = ... Without coming up with a new name for x. It's possible to provide other functions on top of this primitive, like cast :: (Typeable a, Typeable b) => proxy a -> qroxy b -> ... -- to save some type annotations cast :: (Typeable a, Typeable b) => (a ~ b => r) -> Maybe r -- to use an expression directly cast :: (Typeable a, Typeable b) => a -> Maybe b -- the original Of the primitives I mentioned, the first two are H2010. The last two are nice to use directly, but use extensions (this probably doesn't matter so much for new-typeable). The Maybe form lets you say things like `fmap (\Is -> ...) (cast :: ...)` -- unfortunately, there's no "proper" Is type in base right now. The fourth version might save some typing, and might be slightly better performance-wise, since it saves a ⊥ (it's just represented like Bool at runtime)? Probably it all gets inlined so that doesn't matter anyway. Regardless of which particular primitive is used, it would be great for new-typeable to provide something stronger than cast. Shachaf

On Sun, Mar 3, 2013 at 11:43 PM, Shachaf Ben-Kiki
Any of these lets you write any of the other ones, of course. But "cast" doesn't let you write any of them, because it only goes in one direction.
Note: It turns out this isn't actually true, since Is can derive Typeable: λ> data Is :: * -> * -> * where Refl :: Is a a deriving Typeable λ> let cast' :: forall a b. (Typeable a, Typeable b) => Maybe (Is a b); cast' = cast (Refl :: Is a a) It probably still makes sense to provide an equality witness in a less roundabout way, though. Shachaf

On Mon, Mar 4, 2013 at 8:43 AM, Shachaf Ben-Kiki
This came up in #haskell -- rather than just provide coerce, cast (or a primitive that cast can be built on, as well as other things) can give a type equality witness of some kind. Some possible signatures are:
cast :: (Typeable a, Typeable b) => Maybe (p a -> p b) -- Leibniz-style equality cast :: (Typeable a, Typeable b) => p a -> Maybe (p b) -- another form cast :: (Typeable a, Typeable b) => Maybe (Is a b) -- GADT-style equality cast :: (Typeable a, Typeable b) => MightBe a b -- another form -- With data Is a b where { Refl :: Is a a } -- standard equality GADT data MightBe a b where -- version with built-in Maybe, for more convenient types Is :: MightBe a a Isn't :: MightBe a b
Any of these lets you write any of the other ones, of course. But "cast" doesn't let you write any of them, because it only goes in one direction.
gcast does, though
The GADT form can let you write code like
foo :: forall a. Typeable a => a -> ... foo x | Is <- cast :: MightBe a Int = ...x... | Is <- cast :: MightBe a Char = ...x... | otherwise = ...
Without coming up with a new name for x. It's possible to provide other functions on top of this primitive, like
cast :: (Typeable a, Typeable b) => proxy a -> qroxy b -> ... -- to save some type annotations cast :: (Typeable a, Typeable b) => (a ~ b => r) -> Maybe r -- to use an expression directly cast :: (Typeable a, Typeable b) => a -> Maybe b -- the original
Of the primitives I mentioned, the first two are H2010. The last two are nice to use directly, but use extensions (this probably doesn't matter so much for new-typeable). The Maybe form lets you say things like `fmap (\Is -> ...) (cast :: ...)` -- unfortunately, there's no "proper" Is type in base right now. The fourth version might save some typing, and might be slightly better performance-wise, since it saves a ⊥ (it's just represented like Bool at runtime)? Probably it all gets inlined so that doesn't matter anyway.
Regardless of which particular primitive is used, it would be great for new-typeable to provide something stronger than cast.
Shachaf
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
-- Your ship was destroyed in a monadic eruption.

Shachaf's idea (below) makes sense to me, though I'd present it slightly differently. * Currently, cast :: (Typeable a, Typeable b) => a -> Maybe b provides a function to convert a value of type 'a' to 'b'. But it does not provide a way to convert a [a] to [b], or a->Int to b->Int. * Of course these things can be done with 'map', but that's a pain, and the type involved might not be an instance of Functor * So the Right Thing is provide a witness for a~b, wrapped as usual in a GADT. Then it will "lift" automatically to any type, such as those above. * The type GHC.TypeLits.(:~:) is the current library version of a GADT-wrapped equality So we need newCast :: (Typeable a, Typeable b) => Maybe (a :~: b) As usual this relies absolutely on the veracity of Typeable, but now we only derive it mechanically that should be assured. Iavor/Pedro, what do you think? Simon | -----Original Message----- | From: libraries-bounces@haskell.org [mailto:libraries- | bounces@haskell.org] On Behalf Of Shachaf Ben-Kiki | Sent: 04 March 2013 07:44 | To: libraries@haskell.org | Subject: new-typeable, new cast? | | This came up in #haskell -- rather than just provide coerce, cast (or a | primitive that cast can be built on, as well as other things) can give a | type equality witness of some kind. Some possible signatures | are: | | cast :: (Typeable a, Typeable b) => Maybe (p a -> p b) -- Leibniz- | style equality | cast :: (Typeable a, Typeable b) => p a -> Maybe (p b) -- another | form | cast :: (Typeable a, Typeable b) => Maybe (Is a b) -- GADT-style | equality | cast :: (Typeable a, Typeable b) => MightBe a b -- another form | -- With | data Is a b where { Refl :: Is a a } -- standard equality GADT | data MightBe a b where -- version with built-in Maybe, for more | convenient types | Is :: MightBe a a | Isn't :: MightBe a b | | Any of these lets you write any of the other ones, of course. But "cast" | doesn't let you write any of them, because it only goes in one | direction. | | The GADT form can let you write code like | | foo :: forall a. Typeable a => a -> ... | foo x | | Is <- cast :: MightBe a Int = ...x... | | Is <- cast :: MightBe a Char = ...x... | | otherwise = ... | | Without coming up with a new name for x. It's possible to provide other | functions on top of this primitive, like | | cast :: (Typeable a, Typeable b) => proxy a -> qroxy b -> ... -- to | save some type annotations | cast :: (Typeable a, Typeable b) => (a ~ b => r) -> Maybe r -- to | use an expression directly | cast :: (Typeable a, Typeable b) => a -> Maybe b -- the original | | Of the primitives I mentioned, the first two are H2010. The last two are | nice to use directly, but use extensions (this probably doesn't matter | so much for new-typeable). The Maybe form lets you say things like `fmap | (\Is -> ...) (cast :: ...)` -- unfortunately, there's no "proper" Is | type in base right now. The fourth version might save some typing, and | might be slightly better performance-wise, since it saves a ⊥ (it's just | represented like Bool at runtime)? Probably it all gets inlined so that | doesn't matter anyway. | | Regardless of which particular primitive is used, it would be great for | new-typeable to provide something stronger than cast. | | Shachaf | | _______________________________________________ | Libraries mailing list | Libraries@haskell.org | http://www.haskell.org/mailman/listinfo/libraries

On Mon, Mar 4, 2013 at 1:07 AM, Simon Peyton-Jones
Shachaf's idea (below) makes sense to me, though I'd present it slightly differently.
* Currently, cast :: (Typeable a, Typeable b) => a -> Maybe b provides a function to convert a value of type 'a' to 'b'. But it does not provide a way to convert a [a] to [b], or a->Int to b->Int.
* Of course these things can be done with 'map', but that's a pain, and the type involved might not be an instance of Functor
* So the Right Thing is provide a witness for a~b, wrapped as usual in a GADT. Then it will "lift" automatically to any type, such as those above.
* The type GHC.TypeLits.(:~:) is the current library version of a GADT-wrapped equality So we need newCast :: (Typeable a, Typeable b) => Maybe (a :~: b)
As usual this relies absolutely on the veracity of Typeable, but now we only derive it mechanically that should be assured.
Iavor/Pedro, what do you think?
Simon
I sent this message to the wrong address before:
On Mon, Mar 4, 2013 at 12:44 AM, Shachaf Ben-Kiki
On Mon, Mar 4, 2013 at 12:28 AM, Gábor Lehel
wrote: Any of these lets you write any of the other ones, of course. But "cast" doesn't let you write any of them, because it only goes in one direction.
gcast does, though
You're complete right. Somehow I'd managed not to notice gcast. So the primitive I want already exists -- there isn't much point to the rest of the message.
Thanks! Shachaf
As Gábor pointed out, Data.Typeable already has gcast, which gives you Leibniz-style equality: λ> data a :~: b where Refl :: a :~: a λ> :t gcast Refl gcast Refl :: forall a b. (Typeable a, Typeable b) => Maybe ((:~:) * a b) So this isn't needed as a primitive. I didn't realize HEAD had a standard version of Refl -- given that, it might makes sense to add this as a convenience definition in Data.Typeable. But it's not as important as I'd thought. Shachaf

| λ> data a :~: b where Refl :: a :~: a
| λ> :t gcast Refl
| gcast Refl :: forall a b. (Typeable a, Typeable b) => Maybe ((:~:) *
| a b)
That is a ferociously-unintuitive use of 'gcast'! It too me dome while to convince myself that it was right. Though it does indeed work. I'd see (gcast Refl) as the primitive, with 'gcast' itself as a simple derived function.
I'm not sure what name to give the new cast function
?? :: Typeable a, Typeable b) => Maybe (a :~: b)
Do we need gcast1 and gcast2 any more, in our new polykinded world? Can they be depreceated?
Simon
| -----Original Message-----
| From: Shachaf Ben-Kiki [mailto:shachaf@gmail.com]
| Sent: 04 March 2013 10:18
| To: Simon Peyton-Jones
| Cc: libraries@haskell.org; Stephanie Weirich; Richard Eisenberg
| (eir@cis.upenn.edu); Dimitrios Vytiniotis; Iavor Diatchki; Conor
| McBride; Pedro Magalhães
| Subject: Re: new-typeable, new cast?
|
| On Mon, Mar 4, 2013 at 1:07 AM, Simon Peyton-Jones
|

On Mon, Mar 4, 2013 at 4:24 AM, Simon Peyton-Jones
| λ> data a :~: b where Refl :: a :~: a | λ> :t gcast Refl | gcast Refl :: forall a b. (Typeable a, Typeable b) => Maybe ((:~:) * | a b)
That is a ferociously-unintuitive use of 'gcast'! It too me dome while to convince myself that it was right. Though it does indeed work. I'd see (gcast Refl) as the primitive, with 'gcast' itself as a simple derived function.
I'm not sure what name to give the new cast function ?? :: Typeable a, Typeable b) => Maybe (a :~: b)
Do we need gcast1 and gcast2 any more, in our new polykinded world? Can they be depreceated?
Simon
A couple of people have suggested "same". (I wonder if this would also subsume eqSingNat/eqSingSym?). By the way -- since (:~:) seems to be in HEAD now, but not yet released, is there a chance of renaming it? ":~:" is pretty awkward to type, given all the new TypeOperators options these days; how about "=="? Shachaf
participants (3)
-
Gábor Lehel
-
Shachaf Ben-Kiki
-
Simon Peyton-Jones