-
Notifications
You must be signed in to change notification settings - Fork 35
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
Generate additional constructors with SingI constraints #274
Comments
So we don't have to pick a single answer to the ‘ |
I'm lost here. To help put things into context for me: Having said that: what benefit do you get from bundling |
I perhaps know more of the history here. Originally, the @Icelandjack is proposing here to have a pattern synonym behave like the constructor of old, but without storing the implicit singletons -- generating them on the fly. This might be sensible. Perhaps a better question is this: if a user uses |
Ah, that explanation clears things up nicely, thanks. That being said, I would be quite shocked if GHC were smart enough to optimize away all of this explicit dictionary-passing. As evidence, in this simple example: {-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
module Bug where
import Data.Constraint
import Data.Nat
import Data.Singletons.Prelude
import Data.Type.Equality
gen :: Sing (a :: k) -> (Sing a, Dict (SingI a))
gen sing = (sing, withSingI sing Dict)
pattern SS_ :: () => forall n. (S n ~ sn, SingI n) => Sing n -> Sing sn
pattern SS_ n <- SS (gen -> (n, Dict))
where SS_ n = SS n
f :: Sing (n :: Nat) -> n :~: n
f SZ = Refl
f (SS sx) = case f sx of Refl -> Refl If we compile Rec {
-- RHS size: {terms: 18, types: 45, coercions: 13, joins: 0/0}
Bug.$wf [InlPrag=[0], Occ=LoopBreaker]
:: forall (n :: Nat). Sing n -> (n :: Nat) GHC.Prim.~# (n :: Nat)
[GblId, Arity=1, Caf=NoCafRefs, Str=<S,1*U>]
Bug.$wf
= \ (@ (n_s4Ce :: Nat)) (w_s4Cf :: Sing n_s4Ce) ->
case w_s4Cf
`cast` (Data.Nat.D:R:SingNatz0[0] <n_s4Ce>_N
:: (Sing n_s4Ce :: *) ~R# (Data.Nat.R:SingNatz n_s4Ce :: *))
of {
SZ $d~_a4e3 ->
case GHC.Types.HEq_sc
@ Nat
@ Nat
@ n_s4Ce
@ 'Z
($d~_a4e3
`cast` (Data.Type.Equality.N:~[0] <Nat>_N <n_s4Ce>_N <'Z>_N
:: ((n_s4Ce :: Nat) ~ ('Z :: Nat) :: Constraint)
~R#
((n_s4Ce :: Nat) ~~ ('Z :: Nat) :: Constraint)))
of cobox_a4fa [Dmd=<L,A>]
{ __DEFAULT ->
CO: <n_s4Ce>_N
};
SS @ n1_a4e8 $d~_a4e9 sx_a3uE ->
case GHC.Types.HEq_sc
@ Nat
@ Nat
@ n_s4Ce
@ ('S n1_a4e8)
($d~_a4e9
`cast` (Data.Type.Equality.N:~[0] <Nat>_N <n_s4Ce>_N <'S n1_a4e8>_N
:: ((n_s4Ce :: Nat) ~ ('S n1_a4e8 :: Nat) :: Constraint)
~R#
((n_s4Ce :: Nat) ~~ ('S n1_a4e8 :: Nat) :: Constraint)))
of cobox_a4fe [Dmd=<L,A>]
{ __DEFAULT ->
case Bug.$wf @ n1_a4e8 sx_a3uE of ww_s4Ci [Dmd=<L,A>]
{ __DEFAULT ->
CO: <n_s4Ce>_N
}
}
}
end Rec } But if we change Rec {
-- RHS size: {terms: 22, types: 56, coercions: 18, joins: 0/0}
Bug.$wf [InlPrag=[0], Occ=LoopBreaker]
:: forall (n :: Nat). Sing n -> (n :: Nat) GHC.Prim.~# (n :: Nat)
[GblId, Arity=1, Caf=NoCafRefs, Str=<S,1*U>]
Bug.$wf
= \ (@ (n_s4BX :: Nat)) (w_s4BY :: Sing n_s4BX) ->
case w_s4BY
`cast` (Data.Nat.D:R:SingNatz0[0] <n_s4BX>_N
:: (Sing n_s4BX :: *) ~R# (Data.Nat.R:SingNatz n_s4BX :: *))
of {
SZ $d~_a4eD ->
case GHC.Types.HEq_sc
@ Nat
@ Nat
@ n_s4BX
@ 'Z
($d~_a4eD
`cast` (Data.Type.Equality.N:~[0] <Nat>_N <n_s4BX>_N <'Z>_N
:: ((n_s4BX :: Nat) ~ ('Z :: Nat) :: Constraint)
~R#
((n_s4BX :: Nat) ~~ ('Z :: Nat) :: Constraint)))
of cobox_a4xs [Dmd=<L,A>]
{ __DEFAULT ->
CO: <n_s4BX>_N
};
SS @ ipv_s4zN ipv1_s4zO ipv2_s4zP ->
case GHC.Types.HEq_sc
@ Nat
@ Nat
@ n_s4BX
@ ('S ipv_s4zN)
(ipv1_s4zO
`cast` (Data.Type.Equality.N:~[0] <Nat>_N <n_s4BX>_N <'S
ipv_s4zN>_N
:: ((n_s4BX :: Nat) ~ ('S ipv_s4zN :: Nat) :: Constraint)
~R#
((n_s4BX :: Nat) ~~ ('S ipv_s4zN :: Nat) :: Constraint)))
of cobox_a4ep
{ __DEFAULT ->
case GHC.Types.HEq_sc
@ Nat
@ Nat
@ ('S ipv_s4zN)
@ n_s4BX
(ipv1_s4zO
`cast` (Data.Type.Equality.N:~[0] <Nat>_N cobox_a4ep (Sym
cobox_a4ep)
:: ((n_s4BX :: Nat) ~ ('S ipv_s4zN :: Nat) :: Constraint)
~R#
(('S ipv_s4zN :: Nat) ~~ (n_s4BX :: Nat) :: Constraint)))
of cobox1_a4xw [Dmd=<L,A>]
{ __DEFAULT ->
case Bug.$wf @ ipv_s4zN ipv2_s4zP of ww_s4C1 [Dmd=<L,A>]
{ __DEFAULT ->
CO: <n_s4BX>_N
}
}
}
}
end Rec } This would certainly blow up in larger examples. As a result, I don't think I'd be quite ready to endorse the pattern synonym approach as the recommended one. Another issue we'd have to worry about is that we'd presumably want to generate To summarize: I wouldn't be opposed to generating these, provided that:
|
Thanks for that very helpful analysis @RyanGlScott . I agree completely with the two conclusions at the end of his post. |
On second thought, I'm going to say that this is something which belongs outside of
In short, I think this deserves to be its own library. |
The choice of |
Luckily, this feature does not require generating orphan instances of any kind, so it's completely possible (and acceptable) to have singletons for a datatype and the corresponding pattern synonyms defined in separate locations. (Thus, if a library author doesn't define these pattern synonyms, it's quite possible to do so yourself without remorse.) |
The original formulation generated
SingRep
constraints in eg successor branchCurrently it doesn't
But TH could generate those constructors as well
The text was updated successfully, but these errors were encountered: