[commit: base] master: Redo and cleanup the change replacing `unsafe*` with `toSing` (14bd3c1)
Iavor Diatchki
diatchki at galois.com
Fri May 31 19:10:14 CEST 2013
Repository : ssh://darcs.haskell.org//srv/darcs/packages/base
On branch : master
https://github.com/ghc/packages-base/commit/14bd3c1a38be411f1711fc80dafa28b7a6f14dda
>---------------------------------------------------------------
commit 14bd3c1a38be411f1711fc80dafa28b7a6f14dda
Author: Iavor S. Diatchki <diatchki at galois.com>
Date: Thu May 30 19:58:21 2013 -0700
Redo and cleanup the change replacing `unsafe*` with `toSing`
>---------------------------------------------------------------
GHC/TypeLits.hs | 118 +++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 92 insertions(+), 26 deletions(-)
diff --git a/GHC/TypeLits.hs b/GHC/TypeLits.hs
index f8b759e..86c8958 100644
--- a/GHC/TypeLits.hs
+++ b/GHC/TypeLits.hs
@@ -9,6 +9,8 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-} -- for <=
+{-# LANGUAGE RankNTypes #-} -- for SingI magic
+{-# LANGUAGE ScopedTypeVariables #-} -- for SingI magic
{-# OPTIONS_GHC -XNoImplicitPrelude #-}
{-| This module is an internal GHC module. It declares the constants used
in the implementation of type-level natural numbers. The programmer interface
@@ -19,8 +21,8 @@ module GHC.TypeLits
Nat, Symbol
-- * Linking type and value level
- , Sing, SingI, SingE, SingRep, sing, fromSing
- , unsafeSingNat, unsafeSingSymbol
+ , Sing, SingI, SingE, SingRep, sing, singByProxy, fromSing
+ , SomeSing(..), ToSing(..), SomeNat, SomeSymbol
-- * Working with singletons
, withSing, singThat
@@ -43,27 +45,28 @@ module GHC.TypeLits
, Nat1(..), FromNat1
-- * Kind parameters
- , OfKind(..), Demote, DemoteRep
+ , KindIs(..), Demote, DemoteRep
, KindOf
) where
-import GHC.Base(Eq((==)), Bool(..), ($), otherwise, (.))
+import GHC.Base(Eq((==)), Ord((>=)), Bool(..), ($), otherwise, (.){-, seq)-})
import GHC.Num(Integer, (-))
import GHC.Base(String)
import GHC.Read(Read(..))
import GHC.Show(Show(..))
+import GHC.Prim(magicSingI)
import Unsafe.Coerce(unsafeCoerce)
-- import Data.Bits(testBit,shiftR)
import Data.Maybe(Maybe(..))
import Data.List((++))
-- | (Kind) A kind useful for passing kinds as parameters.
-data OfKind (a :: *) = KindParam
+data KindIs (a :: *) = KindParam
{- | A shortcut for naming the kind parameter corresponding to the
-kind of a some type. For example, @KindOf Int ~ (KindParam :: OfKind *)@,
-but @KindOf 2 ~ (KindParam :: OfKind Nat)@. -}
-type KindOf (a :: k) = (KindParam :: OfKind k)
+kind of a some type. For example, @KindOf Int ~ (KindParam :: KindIs *)@,
+but @KindOf 2 ~ (KindParam :: KindIs Nat)@. -}
+type KindOf (a :: k) = (KindParam :: KindIs k)
-- | (Kind) This is the kind of type-level natural numbers.
@@ -80,12 +83,6 @@ newtype instance Sing (n :: Nat) = SNat Integer
newtype instance Sing (n :: Symbol) = SSym String
-unsafeSingNat :: Integer -> Sing (n :: Nat)
-unsafeSingNat = SNat
-
-unsafeSingSymbol :: String -> Sing (n :: Symbol)
-unsafeSingSymbol = SSym
-
--------------------------------------------------------------------------------
-- | The class 'SingI' provides a \"smart\" constructor for singleton types.
@@ -97,6 +94,12 @@ class SingI a where
-- | The only value of type @Sing a@
sing :: Sing a
+-- | A convenience function to create a singleton value, when
+-- we have a proxy argument in scope.
+singByProxy :: SingI n => proxy n -> Sing n
+singByProxy _ = sing
+
+
--------------------------------------------------------------------------------
-- | Comparison of type-level naturals.
class (m <=? n) ~ True => (m :: Nat) <= (n :: Nat)
@@ -129,16 +132,16 @@ and not their type---all types of a given kind are processed by the
same instances.
-}
-class (kparam ~ KindParam) => SingE (kparam :: OfKind k) where
+class (kparam ~ KindParam) => SingE (kparam :: KindIs k) where
type DemoteRep kparam :: *
fromSing :: Sing (a :: k) -> DemoteRep kparam
-instance SingE (KindParam :: OfKind Nat) where
- type DemoteRep (KindParam :: OfKind Nat) = Integer
+instance SingE (KindParam :: KindIs Nat) where
+ type DemoteRep (KindParam :: KindIs Nat) = Integer
fromSing (SNat n) = n
-instance SingE (KindParam :: OfKind Symbol) where
- type DemoteRep (KindParam :: OfKind Symbol) = String
+instance SingE (KindParam :: KindIs Symbol) where
+ type DemoteRep (KindParam :: KindIs Symbol) = String
fromSing (SSym s) = s
{- | A convenient name for the type used to representing the values
@@ -153,6 +156,75 @@ class (SingI a, SingE (KindOf a)) => SingRep (a :: k)
instance (SingI a, SingE (KindOf a)) => SingRep (a :: k)
+-- The type of an unknown singletons of a given kind.
+-- Note that the "type" parameter on this type is really
+-- a *kind* parameter (this is simillar to the trick used in `SingE`).
+data SomeSing :: KindIs k -> * where
+ SomeSing :: SingI (n::k) => proxy n -> SomeSing (kp :: KindIs k)
+
+-- | A definition of natural numbers in terms of singletons.
+type SomeNat = SomeSing (KindParam :: KindIs Nat)
+
+-- | A definition of strings in tterms of singletons.
+type SomeSymbol = SomeSing (KindParam :: KindIs Symbol)
+
+-- | The class of function that can promote a representation value
+-- into a singleton. Like `SingE`, this class overloads based
+-- on a *kind*.
+-- The method returns `Maybe` because sometimes
+-- the representation type contains more values than are supported
+-- by the singletons.
+class (kp ~ KindParam) => ToSing (kp :: KindIs k) where
+ toSing :: DemoteRep kp -> Maybe (SomeSing kp)
+
+instance ToSing (KindParam :: KindIs Nat) where
+ toSing n
+ | n >= 0 = Just (incoherentForgetSing (SNat n))
+ | otherwise = Nothing
+
+instance ToSing (KindParam :: KindIs Symbol) where
+ toSing n = Just (incoherentForgetSing (SSym n))
+
+
+
+{- PRIVATE:
+WARNING: This function has the scary name because,
+in general, it could lead to incoherent behavior of the `sing` method.
+
+The reason is that it converts the provided `Sing n` value,
+into the the evidence for the `SingI` class hidden in `SomeSing`.
+
+Now, for proper singleton types this should not happen,
+because if there is only one value of type `Sing n`,
+then the parameter must be the same as the value of `sing`.
+However, we have no guarantees about the definition of `Sing a`,
+or, indeed, the instance of `Sing`.
+
+We use the function in the instances for `ToSing` for
+kind `Nat` and `Symbol`, where the use is guarantted to be safe.
+
+NOTE: The implementation is a bit of a hack at present,
+hence all the very special annotation.
+-}
+incoherentForgetSing :: forall (n :: k) (kp :: KindIs k). Sing n -> SomeSing kp
+incoherentForgetSing x = withSingI x it LocalProxy
+ where
+ it :: SingI n => LocalProxy n -> SomeSing kp
+ it = SomeSing
+
+{-# LANGUAGE NOINLINE withSingI #-}
+withSingI :: Sing n -> (SingI n => a) -> a
+withSingI x = magicSingI x ((\f -> f) :: () -> ())
+
+
+
+-- PRIVATE
+data LocalProxy n = LocalProxy
+
+
+
+
+
{- | A convenience function useful when we need to name a singleton value
multiple times. Without this function, each use of 'sing' could potentially
refer to a different singleton, and one has to use type signatures to
@@ -162,6 +234,7 @@ withSing :: SingI a => (Sing a -> b) -> b
withSing f = f sing
+
{- | A convenience function that names a singleton satisfying a certain
property. If the singleton does not satisfy the property, then the function
returns 'Nothing'. The property is expressed in terms of the underlying
@@ -238,11 +311,6 @@ instance Show (a :~: b) where
{- | Check if two type-natural singletons of potentially different types
are indeed the same, by comparing their runtime representations.
-
-WARNING: in combination with `unsafeSingNat` this may lead to unsoundness:
-
-> eqSingNat (sing :: Sing 1) (unsafeSingNat 1 :: Sing 2)
-> == Just (Refl :: 1 :~: 2)
-}
eqSingNat :: Sing (m :: Nat) -> Sing (n :: Nat) -> Maybe (m :~: n)
@@ -253,8 +321,6 @@ eqSingNat x y
{- | Check if two symbol singletons of potentially different types
are indeed the same, by comparing their runtime representations.
-WARNING: in combination with `unsafeSingSymbol` this may lead to unsoundness
-(see `eqSingNat` for an example).
-}
eqSingSym:: Sing (m :: Symbol) -> Sing (n :: Symbol) -> Maybe (m :~: n)
More information about the ghc-commits
mailing list