Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upType-indexed Typeable #16
Conversation
bgamari
added some commits
Aug 29, 2016
| dynApply (Dynamic t1 f) (Dynamic t2 x) = | ||
| case funResultTy t1 t2 of | ||
| Just t3 -> Just (Dynamic t3 ((unsafeCoerce f) x)) | ||
| Nothing -> Nothing |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
RyanGlScott
Oct 2, 2016
Contributor
This implementation of dynApply doesn't seem right, especially since it uses unsafeCoerce. Perhaps this definition was copy-pasted from above and wasn't updated? For reference, the definition in your branch is:
dynApply :: Dynamic -> Dynamic -> Maybe Dynamic
dynApply (Dynamic (TRFun ta tr) f) (Dynamic ta' x)
| Just HRefl <- ta `eqTypeRep` ta' = Just (Dynamic tr (f x))
dynApply _ _ = Nothing
RyanGlScott
Oct 2, 2016
Contributor
This implementation of dynApply doesn't seem right, especially since it uses unsafeCoerce. Perhaps this definition was copy-pasted from above and wasn't updated? For reference, the definition in your branch is:
dynApply :: Dynamic -> Dynamic -> Maybe Dynamic
dynApply (Dynamic (TRFun ta tr) f) (Dynamic ta' x)
| Just HRefl <- ta `eqTypeRep` ta' = Just (Dynamic tr (f x))
dynApply _ _ = Nothing
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 2, 2016
Contributor
A few points as I drove by:
- This proposal depends on a definition for
:~~:, which I believe is absent. someTypeRepdoes not look all that useful (the one that takes a proxy). Remove?- Should the
TyConconstructor take the kind parameters used to instantiate kind-polymorphic tycons? - Will
TypeRepbe concrete? It's unclear from this proposal. I vote "no".
Thanks thanks thanks!
|
A few points as I drove by:
Thanks thanks thanks! |
bgamari
added some commits
Oct 2, 2016
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bgamari
Oct 2, 2016
Contributor
A few points as I drove by:
Thanks!
This proposal depends on a definition for
:~~:, which I believe is absent.
Good catch.
someTypeRepdoes not look all that useful (the one that takes a proxy). Remove?
Indeed it. I don't remember why I originally introduced it but I agree, it doesn't seem to serve a purpose.
Should the
TyConconstructor take the kind parameters used to instantiate kind-polymorphic tycons?
This is a rather tricky trade-off.
Under a type-indexed TyCon scheme, the compiler would need to emit a polymorphic function binding for every time declaration. For instance, in the case of,
data Proxy (a :: k) :: Type
we would need to emit,
proxyTyCon :: TypeRep k -> TyCon (Proxy (a :: k))
And then producing evidence for, e.g., typeRep @(Proxy Int) we would emit,
TRApp (TRCon (proxyTyCon (typeRep @Type))) (typeRep @Int)
By contrast under the current non-indexed scheme we would emit,
TRApp (TRCon proxyTyCon) (typeRep @Int)
In effect this has turned what is a static data reference under the current, non-indexed scheme into a polymorphic function call. I worry what effect this would have on both compile- and run-time as we currently try quite hard to use efficient encodings for TyCon
I was proposing that we keep this pattern unidirectional for this very reason (which the proposal really ought to make more clear).
I really don't think we lose much power under the proposed non-indexed scheme; the only thing we cannot do its instantiate a TRCon at a new kind (which we couldn't do with perfect reliability with an indexed TyCon due to the imprecision of UnboxedTupleRep).
Will
TypeRepbe concrete? It's unclear from this proposal. I vote "no".
I agree except for limited exposure via pattern synonyms and perhaps through an internal module (e.g. Data.Typeable.Internal)..
Thanks thanks thanks!
Thanks!
Thanks!
Good catch.
Indeed it. I don't remember why I originally introduced it but I agree, it doesn't seem to serve a purpose.
This is a rather tricky trade-off.
we would need to emit,
And then producing evidence for, e.g.,
By contrast under the current non-indexed scheme we would emit,
In effect this has turned what is a static data reference under the current, non-indexed scheme into a polymorphic function call. I worry what effect this would have on both compile- and run-time as we currently try quite hard to use efficient encodings for I was proposing that we keep this pattern unidirectional for this very reason (which the proposal really ought to make more clear). I really don't think we lose much power under the proposed non-indexed scheme; the only thing we cannot do its instantiate a
I agree except for limited exposure via pattern synonyms and perhaps through an internal module (e.g.
Thanks! |
bgamari
added some commits
Oct 2, 2016
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 2, 2016
Contributor
You make some compelling arguments about indexing TyCon (or, rather, not indexing it). I was thrown off by your explicit comment saying that TRCon is bidirectional, which is why I then assumed to meant to index TyCon all along.
Of course, I'm +1 on this direction, but it's hard to give a final stamp of approval without the full API written out. But perhaps it's too early to insist on a full API.
|
You make some compelling arguments about indexing Of course, I'm +1 on this direction, but it's hard to give a final stamp of approval without the full API written out. But perhaps it's too early to insist on a full API. |
| -- | Kind-heterogeneous type equality | ||
| data (a :: k1) :~~: (b :: k2) where | ||
| HRefl :: a :~~: a | ||
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
AshleyYakeley
Oct 3, 2016
Nice. Consider adding a heterogenous version of class TestEquality to go with your :~~:.
AshleyYakeley
Oct 3, 2016
Nice. Consider adding a heterogenous version of class TestEquality to go with your :~~:.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
| data SomeTypeRep where | ||
| SomeTypeRep :: forall a. TypeRep a -> SomeTypeRep | ||
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
AshleyYakeley
Oct 3, 2016
A more general "Some" class might be more useful? data Some f = forall a. Some (f a)
AshleyYakeley
Oct 3, 2016
A more general "Some" class might be more useful? data Some f = forall a. Some (f a)
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
adamgundry
Oct 6, 2016
Contributor
It might indeed.
That said, now that we have TypeInType, would it be that crazy to rename SomeTypeRep to Type (or * in old fashioned language)? At the term level, we should just be able to write functions that manipulate runtime representations of types! (Here's a half-baked exploration of this idea using the existing (GHC 8.0.1) Typeable.)
adamgundry
Oct 6, 2016
Contributor
It might indeed.
That said, now that we have TypeInType, would it be that crazy to rename SomeTypeRep to Type (or * in old fashioned language)? At the term level, we should just be able to write functions that manipulate runtime representations of types! (Here's a half-baked exploration of this idea using the existing (GHC 8.0.1) Typeable.)
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 6, 2016
Contributor
Well, this new indexed TypeRep is just the singleton associated with Type. But until we can do this right, I wouldn't want to make forward-compatibility troubles by naming this existential Type, even though it is indeed isomorphic to Type.
goldfirere
Oct 6, 2016
Contributor
Well, this new indexed TypeRep is just the singleton associated with Type. But until we can do this right, I wouldn't want to make forward-compatibility troubles by naming this existential Type, even though it is indeed isomorphic to Type.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
dcoutts
Oct 3, 2016
As someone interested in serialising these things (e.g. for Cloud Haskell), I don't mind how it's done too much provided that it is still quick! Doesn't matter too much if it's more complicated or not backwards compatible.
dcoutts
commented
Oct 3, 2016
|
As someone interested in serialising these things (e.g. for Cloud Haskell), I don't mind how it's done too much provided that it is still quick! Doesn't matter too much if it's more complicated or not backwards compatible. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bgamari
Oct 6, 2016
Contributor
@dcoutts indeed the cost associated with serialization of the full TypeRep is potential a sticky point (although no worse than the current scheme). However, you can always just serialize the fingerprint (which IIRC is precisely what Cloud Haskell does).
|
@dcoutts indeed the cost associated with serialization of the full |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bgamari
Oct 8, 2016
Contributor
Icelandjack notifications@github.com writes:
For
Data.DynamicI would like to seeSDynamic:data SDynamic s where SDynamic :: TypeRep a -> s a -> SDynamic sAlright, I don't see any reason why we couldn't provide this. That being
said, I'm not a fan of the name; whyS?
|
Icelandjack notifications@github.com writes:
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 8, 2016
Contributor
While this SDynamic might be useful, I'm not sure why this construction, specially, should appear in Data.Dynamic. There are a good many (infinite number?) similar constructs that we might include. Why is this one better than all its siblings?
And, of course, a separate library could provide this functionality. Indeed, Data.Dynamic need not be in base at all. (Perhaps it really shouldn't be.)
|
While this And, of course, a separate library could provide this functionality. Indeed, |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Icelandjack
Oct 8, 2016
Contributor
@bgamari: Probably Shape or Static based on
SDynamicis a mid-point between fully dynamic & fully static types. We statically know some "Shape" information, but not all info about type. e.g.,SDynamic Maybecontains a value that is definitely aMaybe tyfor some typety, but the typetycan vary between values of typeSDynamic Maybe.
I don't care for the name particularly.
We could look to ... for functions to include, some like value :: Value f -> f Any won't make sense given the different representation (type-indexed type representation vs Any).
data Wrapper a = Wrapper
(forall g. g a -> SDynamic g)
(forall g. SDynamic g -> g a)
-- Unwrap a value to get at the thing inside, while still being able
-- to wrap it up again.
data Unwrapped f where
In :: f a -> Wrapper a -> Unwrapped f
mapSDynamic :: (f ~> g) -> (SDynamic f -> SDynamic g)
ofSDynamic :: (forall a. f a -> b) -> (SDynamic f -> b)
...@goldfirere That would be fine too, do you think discussions pertaining to Dynamic should be moved elsewhere? We could look to mirror the gcast family of functions (I haven't played with these at all)
gcast :: (Typeable a, Typeable b) => s a -> Maybe (s b)
gcast1 :: (Typeable t, Typeable t') => s (t a) -> Maybe (s (t' a))
gcast2 :: (Typeable t, Typeable t') => s (t a b) -> Maybe (s (t' a b))
-- ???????
data SDynamic s where SDynamic :: TypeRep a -> s a -> SDynamic s
data SDynamic1 s a where SDynamic1 :: TypeRep t -> s (t a) -> SDynamic1 s a
data SDynamic2 s a b where SDynamic2 :: TypeRep t -> s (t a b) -> SDynamic2 s a bconstraints chose the following quantified constraints forall a. p a, forall a. p (f a) and forall f a. p (t f a).
|
@bgamari: Probably Shape or Static based on
I don't care for the name particularly. We could look to ... for functions to include, some like data Wrapper a = Wrapper
(forall g. g a -> SDynamic g)
(forall g. SDynamic g -> g a)
-- Unwrap a value to get at the thing inside, while still being able
-- to wrap it up again.
data Unwrapped f where
In :: f a -> Wrapper a -> Unwrapped f
mapSDynamic :: (f ~> g) -> (SDynamic f -> SDynamic g)
ofSDynamic :: (forall a. f a -> b) -> (SDynamic f -> b)
...@goldfirere That would be fine too, do you think discussions pertaining to gcast :: (Typeable a, Typeable b) => s a -> Maybe (s b)
gcast1 :: (Typeable t, Typeable t') => s (t a) -> Maybe (s (t' a))
gcast2 :: (Typeable t, Typeable t') => s (t a b) -> Maybe (s (t' a b))
-- ???????
data SDynamic s where SDynamic :: TypeRep a -> s a -> SDynamic s
data SDynamic1 s a where SDynamic1 :: TypeRep t -> s (t a) -> SDynamic1 s a
data SDynamic2 s a b where SDynamic2 :: TypeRep t -> s (t a b) -> SDynamic2 s a b
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 9, 2016
Contributor
I believe (and IIRC, my opinion is shared somewhat widely) that base should be as small as possible. Dynamic and friends are a feature relatively few people need. In its current form, the safety of the Dynamic construction is not specified by the Report (I think), and therefore it is appropriate that the implementation be blessed by putting it in base. With the new TypeRep, this is no longer the case, as Dynamic can be built on TypeRep without any special considerations.
I do think we should make sure that a solid implementation of Dynamic can be built on our new design of TypeRep, as a check on our design. But we don't have to actually package that implementation.
|
I believe (and IIRC, my opinion is shared somewhat widely) that I do think we should make sure that a solid implementation of |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Icelandjack
Oct 13, 2016
Contributor
dynHead :: Dynamic -> Maybe Dynamic
dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do
App rl rx <- splitApp rxs
Refl <- rl `eqT` (typeRep :: TypeRep [])
return (Dyn rx (head xs))can look nicer
pattern (:<->:) :: () => fa ~ f a => TypeRep f -> TypeRep a -> TypeRep fa
pattern f :<->: a <- (splitApp -> Just (App f a))
where f :<->: a = TypeApp f a
dynHead :: Dynamic -> Maybe Dynamic
dynHead (Dyn (f :<->: a) xs) = do
Refl <- f `eqT` (typeRep :: TypeRep [])
return (Dyn a (head xs))
dynHead _ = NothingIf we pattern match on the list constructor directly, we can pattern match on the list directly too!
import GHC.Types (type (~~))
pattern List :: () => [] ~~ t => TypeRep t
pattern List <- (eqT (typeRep @(Type -> Type) @[]) -> Just HRefl)
where List = typeRep @(Type -> Type) @[]
dynHead :: Dynamic -> Maybe Dynamic
dynHead = \case
Dyn (List :<->: a) (x:_) ->
Just (Dyn a (head xs))
_ -> NothingLook how neat this looks! (apart from the :<->: noise)
pattern Pair :: () => (,) ~~ b => TypeRep b
pattern Pair <- (eqT (typeRep @(Type -> Type -> Type) @(,)) -> Just HRefl)
where Pair = typeRep @(Type -> Type -> Type) @(,)
dynSwap :: Dynamic -> Maybe Dynamic
dynSwap (Dyn (Pair :<->: a :<->: b) (x, y)) = Just (Dyn (Pair :<->: b :<->: a) (y, x))
...(dynSwap does not compile without a type signature, is this a bug?)
or
pattern Pair_ :: () => ab ~ (a, b) => T a -> T b -> T ab
pattern Pair_ a b = Pair :<->: a :<->: b
dynSwap :: Dynamic -> Maybe Dynamic
dynSwap (Dyn (Pair_ a b) (x, y)) = Just (Dyn (Pair_ b a) (y, x))It would be great if we didn't have to create new pattern synonym for each ruddy type constructor so it would be great if we could define Con and use visible type application proposed by #11350
pattern Con :: forall (u :: k1) (b :: k2). TypeRep u => u ~~ b => TypeRep b
pattern Con <- (eqT (typeRep @k1 @u) -> Just HRefl)
where Con = typeRep @k1 @u
dynHead (Dyn (Con @[] :<->: a) (x:_)) = Just (Dyn a (head xs))
dynSwap (Dyn (Con @(,) :<->: a :<->: b) (x, y)) = Just (Dyn (Con @(,) :<->: b :<->: a) (y, x))
isPairOfIntAndFloat (Dyn (Con @(,) :<->: Con @Int :<->: Con @Float)) = True
isPairOfIntAndFloat _ = False
dynHead :: Dynamic -> Maybe Dynamic
dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do
App rl rx <- splitApp rxs
Refl <- rl `eqT` (typeRep :: TypeRep [])
return (Dyn rx (head xs))can look nicer pattern (:<->:) :: () => fa ~ f a => TypeRep f -> TypeRep a -> TypeRep fa
pattern f :<->: a <- (splitApp -> Just (App f a))
where f :<->: a = TypeApp f a
dynHead :: Dynamic -> Maybe Dynamic
dynHead (Dyn (f :<->: a) xs) = do
Refl <- f `eqT` (typeRep :: TypeRep [])
return (Dyn a (head xs))
dynHead _ = NothingIf we pattern match on the list constructor directly, we can pattern match on the list directly too! import GHC.Types (type (~~))
pattern List :: () => [] ~~ t => TypeRep t
pattern List <- (eqT (typeRep @(Type -> Type) @[]) -> Just HRefl)
where List = typeRep @(Type -> Type) @[]
dynHead :: Dynamic -> Maybe Dynamic
dynHead = \case
Dyn (List :<->: a) (x:_) ->
Just (Dyn a (head xs))
_ -> NothingLook how neat this looks! (apart from the pattern Pair :: () => (,) ~~ b => TypeRep b
pattern Pair <- (eqT (typeRep @(Type -> Type -> Type) @(,)) -> Just HRefl)
where Pair = typeRep @(Type -> Type -> Type) @(,)
dynSwap :: Dynamic -> Maybe Dynamic
dynSwap (Dyn (Pair :<->: a :<->: b) (x, y)) = Just (Dyn (Pair :<->: b :<->: a) (y, x))
...( or pattern Pair_ :: () => ab ~ (a, b) => T a -> T b -> T ab
pattern Pair_ a b = Pair :<->: a :<->: b
dynSwap :: Dynamic -> Maybe Dynamic
dynSwap (Dyn (Pair_ a b) (x, y)) = Just (Dyn (Pair_ b a) (y, x))It would be great if we didn't have to create new pattern synonym for each ruddy type constructor so it would be great if we could define pattern Con :: forall (u :: k1) (b :: k2). TypeRep u => u ~~ b => TypeRep b
pattern Con <- (eqT (typeRep @k1 @u) -> Just HRefl)
where Con = typeRep @k1 @u
dynHead (Dyn (Con @[] :<->: a) (x:_)) = Just (Dyn a (head xs))
dynSwap (Dyn (Con @(,) :<->: a :<->: b) (x, y)) = Just (Dyn (Con @(,) :<->: b :<->: a) (y, x))
isPairOfIntAndFloat (Dyn (Con @(,) :<->: Con @Int :<->: Con @Float)) = True
isPairOfIntAndFloat _ = False |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 14, 2016
Contributor
I can see the usefulness of :<->:, though I would spell it :@:. And, yes, we need visible type patterns.
As for dynSwap: no, it's not a bug. The pattern-match introduces an equality constraint, and therefore the return type cannot be inferred, according to the OutsideIn algorithm. In this case, however, the equality constraint affects only existentially-bound variables, so it probably is OK, but that would be an exception to the rule.
|
I can see the usefulness of As for |
glaebhoerl
and others
added some commits
Oct 16, 2016
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Icelandjack
Oct 24, 2016
Contributor
Your plan adds a superclass to Typeable
class Typeable k => Typeable (a :: k)but this proposal doesn't: which is the right one and will it require UndecidableSuperClasses?
|
Your plan adds a superclass to class Typeable k => Typeable (a :: k)but this proposal doesn't: which is the right one and will it require |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
goldfirere
Oct 24, 2016
Contributor
I'm +1 on the kind superclass, if the solver doesn't choke on it.
|
I'm +1 on the kind superclass, if the solver doesn't choke on it. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bgamari
Oct 25, 2016
Contributor
Icelandjack notifications@github.com writes:
Your plan adds a superclass to
Typeableclass Typeable k => Typeable (a :: k) wherebut this proposal doesn't: which is the right one and does will it require
UndecidableSuperClasses?Indeed; I was discussing alternative designs with Stephanie this weekend
and this is one of them. It would require UndecidableSuperClasses as
well as some care in the Typeable solver (which I have yet to figure out).
|
Icelandjack notifications@github.com writes:
|
bgamari
added
the
Under discussion
label
Jan 24, 2017
nomeata
removed
the
Under discussion
label
Feb 27, 2017
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
nomeata
Feb 27, 2017
Contributor
This proposal has not attracted discussion for a while. Please consider submitting it to the Committee for discussion, if you think it is ready, or close it, if you have abandoned this proposal.
|
This proposal has not attracted discussion for a while. Please consider submitting it to the Committee for discussion, if you think it is ready, or close it, if you have abandoned this proposal. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
Can we wrap this up? This is already in 8.2, right? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bgamari
Mar 24, 2017
Contributor
Indeed it is in 8.2. Ultimately time ran out to both bring finish the implementation and continue pushing the proposal process forward, so we "grandfathered" this proposal in.
|
Indeed it is in 8.2. Ultimately time ran out to both bring finish the implementation and continue pushing the proposal process forward, so we "grandfathered" this proposal in. |
bgamari
closed this
Mar 24, 2017
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Icelandjack
Apr 17, 2017
Contributor
I'll add a link for some use of a SDynamic like structure for interested readers: StackOverflow
We can use the same trick of delaying when a constraint is discovered to build the leaves of a memo trie for
Typeable a => f a. Conceptually the leaves of the trie are each one of the followingGDynamics.{-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} import Data.Typeable import Control.Monad (liftM) data GDynamic f where GDynamic :: Typeable a => f a -> GDynamic f unGDynamic :: Typeable a => GDynamic f -> Maybe (f a) unGDynamic (GDynamic f) = gcast fWhen constructing the trie, we don't have the
Typeable ainstances needed to construct theGDynamics. We only have aTypeRep. Instead we will steal theTypeable ainstance provided when the value is accessed. AGDynamicvalue up to aTypeable ainstance is theTypeRep, ..
|
I'll add a link for some use of a
|
bgamari commentedOct 1, 2016
•
edited
Edited 1 time
-
bgamari
edited Oct 2, 2016 (most recent)
Here is a proposal for augmenting our existing
Typeablemechanism with a variant,Type.Reflection, which provides a more strongly typed variant as originally described in A Reflection on Types (Peyton Jones, et al. 2016).Rendered