New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Eliminators and Typeclasses #4
Comments
Also, I have just played a bit around with your eliminators package a few months ago, but writing this was easy. I this would not be some constraint, but instead a normal type, I think I would have figured it out myself! 🙂 |
Urk. I believe this is a GHC bug—in particular, Trac #11715—rearing its ugly head. In GHC Core, the I'm afraid the only workaround for the time being is to just create the defunctionalization symbols by hand: {-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
module Foo where
import Data.Kind
import Data.Singletons
import Data.Singletons.Prelude.List
import GHC.TypeNats
type TypeClassInduction a (n :: Nat) (f :: [Type] -> Type) (t :: Type -> Constraint)
= t (f (Replicate n a))
data TypeClassInductionSym0 :: k ~> Nat ~> ([Type] -> Type) ~> (Type -> Constraint) ~> Constraint
data TypeClassInductionSym1 :: k -> Nat ~> ([Type] -> Type) ~> (Type -> Constraint) ~> Constraint
data TypeClassInductionSym2 :: k -> Nat -> ([Type] -> Type) ~> (Type -> Constraint) ~> Constraint
data TypeClassInductionSym3 :: k -> Nat -> ([Type] -> Type) -> (Type -> Constraint) ~> Constraint
type instance Apply TypeClassInductionSym0 a = TypeClassInductionSym1 a
type instance Apply (TypeClassInductionSym1 a) n = TypeClassInductionSym2 a n
type instance Apply (TypeClassInductionSym2 a n) f = TypeClassInductionSym3 a n f
type instance Apply (TypeClassInductionSym3 a n f) t = TypeClassInduction a n f t |
okay, thank you! |
I'm not sure either! All of the code in this library is built around predicates |
I don't know either 😀 This was more out of need (all this hassle leads me trying to write a good old I, unfortunately, have not much experience with "other" dependently typed languages, I have just played around with idris a bit and that's all. |
In any case, you're barking up the wrong tree with I suspect what you really want is something akin to a type-level eliminator. Something like: type family ElimNatC (p :: Nat ~> Constraint) (s :: Nat) (pZ :: p @@ Z) (pS :: forall (n :: Nat). p @@ n -> p @@ (S n)) :: p @@ s where ... Sadly, this is not possible in today's GHC, as its support for higher-ranked kinds in type families is extremely limited (see Trac #11719). |
yeah, I knew that this is not a well-defined function, I hoped there is something like |
Not much, I'm afraid. You're sailing through relatively uncharted waters on the fringes of GHC's type system, and you shouldn't be surprised if you hit impasses like this. There may be some other way to define your |
Thank you for your patience 🙂 |
I don't want to bother you, but...could a different formulation of eliminators maybe work when i try something like like this? type CanShow a n = HList (Replicate n a) -> String
$(genDefunSymbols [''CanShow])
canShowInduction :: forall (n :: Nat) (a :: Type) y (as :: [Type]).
(SingI n, Show a)
=> CanShow a n
canShowInduction = elimNat @(CanShowSym1 a) @n (sing @Nat @n) base step
where
base :: CanShow a 0
base = show
step :: forall (k :: Nat).
Sing k
-> CanShow a k
-> CanShow a (k :+ 1)
step _ _ = case replicateSucc @k @a of
Refl -> show with a different formulation of step, so maybe something like this: type ShowConstraint a n = Show (HList(Replicate n a))
type CanShow a n = HList (Replicate n a) -> String
$(genDefunSymbols [''CanShow])
canShowInduction :: forall (n :: Nat) (a :: Type) y.
(SingI n, Show a)
=> CanShow a n
canShowInduction = elimNat @(CanShowSym1 a) @n (sing @Nat @n) base step
where
-- (how can i get the (ShowConstraint a 0) from here?)
base :: CanShow a 0
base = show
step :: forall (k :: Nat). (ShowConstraint a k)
--- (ShowConstraint a k = Show(HList(Replicate a k))
Sing k
-> CanShow a (k :+ 1)
step _ = case replicateSucc @k @a of
-- (so we have (Replicate a (k + 1) ~ (a : Replicate a (k)))
Refl -> show I don't know whether this would actually help or just move the problem into eliminators. |
I can't believe it...I've just solved it 😀 Eliminators is awesome!! data Dict ctxt where
Dict :: ctxt => Dict ctxt
type TypeClassInduction a (f :: [Type] -> Type) (t :: Type -> Constraint) (n :: Nat)
= Dict (t (f (Replicate n a)))
-- due to https://ghc.haskell.org/trac/ghc/ticket/11715 we can't generate it
data TypeClassInductionSym0 :: k ~> ([Type] -> Type) ~> (Type -> Constraint) ~> Nat ~> Type
data TypeClassInductionSym1 :: k -> ([Type] -> Type) ~> (Type -> Constraint) ~> Nat ~> Type
data TypeClassInductionSym2 :: k -> ([Type] -> Type) -> (Type -> Constraint) ~> Nat ~> Type
data TypeClassInductionSym3 :: k -> ([Type] -> Type) -> (Type -> Constraint) -> Nat ~> Type
type instance Apply TypeClassInductionSym0 a = TypeClassInductionSym1 a
type instance Apply (TypeClassInductionSym1 a) f = TypeClassInductionSym2 a f
type instance Apply (TypeClassInductionSym2 a f) t = TypeClassInductionSym3 a f t
type instance Apply (TypeClassInductionSym3 a f t) n = TypeClassInduction a f t n
typeClassInduction :: forall (n :: Nat) (a :: Type) (f :: [Type] -> Type) (t :: Type -> Constraint).
(SingI n, t (f '[]), (t a))
=> (forall xs x. Dict (t (f xs)) -> Dict (t x) -> Dict (t (f (x ': xs)))) -> TypeClassInduction a f t n
typeClassInduction cStep = elimNat @(TypeClassInductionSym3 a f t) @n (sing @Nat @n) base step
where
base :: TypeClassInduction a f t 0
base = Dict @(t (f (Replicate 0 a)))
step :: forall (k :: Nat).
Sing k
-> TypeClassInduction a f t k
-> TypeClassInduction a f t (k :+ 1)
step _ prev = case replicateSucc @k @a of
Refl -> (cStep prev (Dict @(t a))) usage newtype HoHeList (n :: Nat) a = HoHeList
{ unHoHeList :: HList (Replicate n a)
}
instance (SingI n, Show a) => Show (HoHeList (n :: Nat) a) where
show :: forall xs. xs ~ (Replicate n a) => HoHeList n a -> String
show homogenous = let unhom :: HList xs = unHoHeList homogenous in
case typeClassInduction @n @a @HList @Show constrStep of
Dict -> show unhom
where
constrStep :: forall (a :: Type) (ts :: [Type]).
Dict (Show (HList ts)) -> Dict (Show a) -> Dict (Show (HList (a : ts)))
constrStep Dict Dict = Dict ghci *V2.HList> vals = 1 :&: 2 :&: 3 :&: HNil
*V2.HList> proveHomogenous vals
1:&:2:&:3:&:[]
*V2.HList> :t proveHomogenous vals
proveHomogenous vals :: Num t1 => HoHeList 3 t1
*V2.HList> hohe = proveHomogenous vals
*V2.HList> show hohe
"1:&:2:&:3:&:[]" I just needed a bit of...creativity 😀 unbelievable that this works EDIT: Do you know what actually happens when I run |
While I'm glad you found a solution, it now occurs to me that this is all completely overkill for the problem you're trying to solve. The root of the issue is that you have these two instance (Show (HList '[])) where
show _ = "[]"
instance (Show a, Show (HList ts)) => (Show (HList (a : ts))) where
show (a :&: rest) = show a ++ ":&:" ++ show rest These will only ever apply if you know statically that an If I may, I'd like to propose a much simpler solution that doesn't use {-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Foo where
import Data.Kind
import Data.Singletons.Prelude.List
import GHC.TypeNats
data HList (ts :: [Type]) where
HNil :: HList '[]
(:&:) :: t -> HList ts -> HList (t ': ts)
infixr 5 :&:
type family AllC (f :: Type -> Constraint) (xs :: [Type]) :: Constraint where
AllC _ '[] = ()
AllC f (x:xs) = (f x, AllC f xs)
deriving instance AllC Show xs => Show (HList xs)
newtype HoHeList (n :: Nat) a = HoHeList
{ unHoHeList :: HList (Replicate n a)
}
deriving instance AllC Show (Replicate n a) => Show (HoHeList n a) The key here is in the use of Now maybe some day we can define |
It certainly does something: it computes a |
Well, at least I learned something! Here's something interesting: At least in GHCi, printing a If I am not building first the type-level list and then convert it to a replicateH2 :: SNat n -> a -> HoHeList n a
replicateH2 n a =
HoHeList (unsafeCoerce (iterate (unsafeCoerce . (a :&:)) HNil
!! fromInteger (fromSing n))) and then show it, I don't notice any slowdown between the eliminator-based show definition and not using not a type-level list. I can print I have not tested your approach yet and I don't have time until tomorrow, but I think it would require building the actual type-level list. EDIT: Since I have a domainMatches :: forall (n :: Nat) (a :: Type) (b :: Type). SingI n => ToDomain (Replicate n (a -> b)) :~: Replicate n a
domainMatches = elimNat @(WhyDomainMatchesSym2 a b) @n (sing @Nat @n) base step
where
base :: WhyDomainMatches a b 0
base = Refl
step :: forall (k :: Nat).
Sing k
-> WhyDomainMatches a b k
-> WhyDomainMatches a b (k :+ 1)
step _ = case replicateSucc @k @(a -> b) of
Refl -> case replicateSucc @k @a of
Refl -> cong @_ @_ @((:$$) a) This seems like a great opportunity for trying to figure out what GHC is doing (for the first time). EDIT 2: yeah, |
Am I abusing the github ticket-system in your view? Is this even interesting to you? |
I don't think this is abusing GitHub at all! This stuff is far from obvious (both from a GHC programmer's perspective, and from the perspective of someone accustomed to dependent types in other languages). I can't say I'm surprised to learn that code using If you're convinced that your proofs are correct, you can always make a rewrite {-# RULES "typeClassInductionTerminates" typeClassInduction x = unsafeCoerce Dict #-} I can't guarantee that's the only bottleneck in your code, but it would at least remove an obvious culprit. |
I think you misunderstood me (or I wasn't very clear)! > homMany = replicateH2 (sing @Nat @200000) 1
> homMany
(just prints a lot of 1s)
> (+1) <$> homMany
(just prints a lot of 2s)
> foldr (+) 0 homMany
20000 (all 3 are using replicateH2 :: SNat n -> a -> HoHeList n a
replicateH2 n a =
HoHeList (unsafeCoerce (iterate (unsafeCoerce . (a :&:)) HNil
!! fromInteger (fromSing n))) to avoid actually building the type-level list. Having a type-level list of more than 2000 Elements starts eating all my RAM (so actually evaluating I don't notice any slowdown compared to the just calling |
I am wondering whether Eliminators can help prove that this is valid:
I have a normal HList:
and a homogenous case
now, i want to create a show-instance:
and prove it via (i am using your cong):
it seems like I could just use
elimNat
again, but I am not sure. I get these errors that I am not sure how to solve:$(genDefunSymbols [''TypeClassInduction])
results in
removing it results
genDefunSymbols
inThis is understandable since I am not returning a type, but instead a constraint. Is there a way to solve this?
(also,
forall xs x. (t (f xs), t x) => t (f (x ': xs))
is not valid haskell...but it seemed natural to write)Edit:
replicateSucc is defined as
The text was updated successfully, but these errors were encountered: