Advertisement
NLinker

Dependent types using singleton types

Jun 30th, 2016
226
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. {-# language DataKinds, PolyKinds, ScopedTypeVariables, UndecidableInstances,
  2.     FlexibleInstances, FlexibleContexts, GADTs, TypeFamilies, RankNTypes,
  3.     LambdaCase, TypeOperators, ConstraintKinds #-}
  4.  
  5. import GHC.TypeLits
  6. import Data.Proxy
  7. import Data.Singletons.Prelude
  8. import Data.Singletons.Decide
  9. import Data.Constraint
  10.  
  11. -- Witnesses
  12. --------------------------------------------------------------------------------
  13. -- for https://blog.jle.im/entry/practical-dependent-types-in-haskell-1.html
  14. {-
  15.  
  16. Generally, the best practice is to avoid the functions
  17. and exports from GHC.TypeLits and instead use the *singletons*
  18. infrastructure because the native TypeLits operations don't
  19. interoperate with singletons by default.
  20.  
  21. So:
  22.   - Don't use KnownNat or KnownSymbol,
  23.     and don't use the ugly "(KnownNat n) => Proxy n -> t" idiom
  24.   - An explicit parameter is "Sing x"
  25.   - An implicit parameter is "SingI x"
  26.   - Convert from explicit to implicit with "singInstance" or "withSingI"
  27.   - Convert from implicit to explicit with "sing"
  28.  
  29. The conversion from explicit to implicit essentially works the same way
  30. as in Edward Kmett's Data.Reflection library.
  31.  
  32. The upshot here is that once we have singleton functions, we automatically
  33. have all the explicit witnesses for function results, and we also get
  34. all the implicit witnesses by the general "explicit->implicit" conversion.
  35.  
  36. -}
  37.  
  38. -- TypeNat witnesses
  39.  
  40. -- explicit witnesses are given by singleton functions
  41.  
  42. n1 = sing :: Sing 2
  43. n2 = sing :: Sing 3
  44.  
  45. n3 = n1 %:+ n2 -- addition
  46. n4 = n1 %:* n2 -- multiplication
  47.  
  48. n3' :: Integer
  49. n3' = fromSing n3 -- 5
  50.  
  51. n4' :: Integer
  52. n4' = fromSing n4 -- 6
  53.  
  54. -- implicit witnesses
  55.  
  56. -- Note the funky Num class on the kind level
  57. -- Also, we only use "Dict" here for illustration purposes, in real code
  58. -- we would just create the SingI instances on the fly, as needed.
  59. implicitWitnessTest :: SNum (KindOf a) => Sing a -> Sing b -> Dict (SingI (a :+ b))
  60. implicitWitnessTest a b = withSingI (a %:+ b) Dict
  61.  
  62. -- leave "a" implicit plus return the singletons analogue of Dict:
  63. implicitWitnessTest2 ::
  64.   forall a b. SingI (a :: Nat) => Sing b -> SingInstance (a :+ b)
  65. implicitWitnessTest2 b = singInstance ((sing :: Sing a) %:+ b)
  66.  
  67. -- Also: convert a piece of demoted data to some singleton
  68. -- This is a just a sometimes nicer version of "toSing"
  69. withSomeSingtest :: IO ()
  70. withSomeSingtest = do
  71.   withSomeSing True (\case STrue -> print "got true"; SFalse -> print "got false")
  72.  
  73.  
  74. {-
  75. So, the witness generators are just singleton functions + conversion.
  76.  
  77. Note here that "Sing" is often a lot more convenient to use that "SingI".
  78. That's because if we do any sort of computation on singleton values, we
  79. need explicit parameters. Also, if there's any sort of ambiguity about
  80. which parameters we pass to which functions, or about the order of
  81. parameters, we need to specify it with type annotations, proxies or
  82. explicit singletons, and explicit singletons are again the cleanest way.
  83.  
  84. 95% of the time singleton arguments are operationally just like normal
  85. arguments, so there's no reason to make them implicit. Similarly,
  86. "withNatOp" is more straightforwardly expressed with explicit Sing
  87. parameters. In short: most of the time use Sing instead of SingI or Proxy.
  88.  
  89. -}
  90.  
  91. -- List singletons & manipulation
  92. --------------------------------------------------------------------------------
  93.  
  94. type KnownNats (xs :: [Nat]) = SingI xs
  95. type NatList (xs :: [Nat]) = Sing xs
  96. type SomeNats = SomeSing ('KProxy :: KProxy [Nat])
  97.  
  98. -- This one throws error on negative input, although not on conversion,
  99. -- only on subsequent operations.
  100. someNatsValPos :: [Integer] -> SomeNats
  101. someNatsValPos = toSing
  102.  
  103. -- Since the safe conversion is missing from *singletons*, let's
  104. -- implement it
  105. safeIntegerSing :: Integer -> Maybe (SomeSing (KindOf 0))
  106. safeIntegerSing =
  107.   fmap (\(SomeNat (p :: Proxy n)) -> SomeSing (sing :: Sing n)) . someNatVal
  108.  
  109. sequenceSome :: [SomeSing ('KProxy :: KProxy k)] -> SomeSing ('KProxy :: KProxy [k])
  110. sequenceSome = foldr (\(SomeSing x) (SomeSing xs) -> SomeSing (SCons x xs)) (SomeSing SNil)  
  111.  
  112. someNatsVal :: [Integer] -> Maybe SomeNats
  113. someNatsVal = fmap sequenceSome . traverse safeIntegerSing
  114.  
  115.  
  116. -- Note : "KnownNats ns" is equivalent to "NatList ns", so it's redundant here
  117. -- reifyNats :: [Integer] -> (forall ns. KnownNats ns => NatList ns -> r) -> r
  118. reifyNats :: [Integer] -> (forall ns. NatList ns -> r) -> r
  119. reifyNats = withSomeSing
  120.  
  121. reifyNats' :: [Integer] -> r -> (forall ns. NatList ns -> r) -> r
  122. reifyNats' ns r f = maybe r (\(SomeSing ns) -> f ns) (someNatsVal ns)
  123.  
  124. -- "Decision" is a bit more informative than "Maybe"
  125. sameNats :: NatList ns -> NatList ms -> Decision (ns :~: ms)
  126. sameNats = (%~)
  127.  
  128. -- We do the elimination generally.
  129. listElim ::
  130.      forall (p :: [a] -> *).
  131.      p '[]
  132.  -> (forall x xs. Sing (x ': xs) -> p xs -> p (x ': xs))
  133.  -> forall (xs :: [a]). Sing xs -> p xs
  134. listElim z f SNil         = z
  135. listElim z f (SCons x xs) = f (SCons x xs) (listElim z f xs)
  136.  
  137. -- Actually, we can do even more generally if we switch to proper
  138. -- type functions. Basically, this lets us fold with type families.
  139. listElim' ::
  140.      forall (p :: TyFun [a] * -> *).
  141.      Proxy p
  142.   -> p @@ '[]
  143.  -> (forall x xs. Sing (x ': xs) -> p @@ xs -> p @@ (x ': xs))
  144.  -> forall (xs :: [a]). Sing xs -> p @@ xs
  145. listElim' p z f SNil         = z
  146. listElim' p z f (SCons x xs) = f (SCons x xs) (listElim' p z f xs)
  147.  
  148.  
  149.  
  150. -- In general, SomeNat is equivalent to Integer,
  151. -- SomeSymbol is equivalent to String, etc...
  152. -- A demoted value carries the same amount of information as
  153. -- a boxed-up singleton.
  154.  
  155. -- This is the reason why "traverseSList" below is more general
  156. -- than your "traverseNatList" (even if we specialize on Nat)
  157.  
  158. -- "ak" is only introduced to reduce clutter. We use ~ as type-level "let" binding
  159. traverseSList ::
  160.      forall (xs :: [a]) f b ak.
  161.      (ak ~ ('KProxy :: KProxy a), Applicative f, SingKind ak)
  162.  => (DemoteRep ak -> f b)
  163.  -> SList xs -> f [b]
  164. traverseSList f = traverse f . fromSing
  165.  
  166. -- Closer to original type with more "SomeSing"
  167. traverseSList' ::
  168.      forall (xs :: [a]) f ak.
  169.      (ak ~ ('KProxy :: KProxy a), Applicative f, SingKind ak)
  170.  => (DemoteRep ak -> f (SomeSing ('KProxy :: KProxy b)))
  171.   -> SList xs -> f (SomeSing ('KProxy :: KProxy [b]))
  172. traverseSList' f = fmap sequenceSome . traverseSList f
  173.  
  174. -- The standard singleton map; this one preserves the most information
  175. mapNatList ::
  176.   Sing (f :: TyFun Nat b -> *) -> Sing (xs :: [Nat]) -> Sing (MapSym2 f xs)
  177. mapNatList = sMap
  178.  
  179. mapNatList' :: (Integer -> Integer) -> Sing (xs :: [Nat]) -> [Integer]
  180. mapNatList' f = map f . fromSing
  181.  
  182. mapNatList'' :: (Integer -> Integer) -> Sing (xs :: [Nat]) -> SomeNats
  183. mapNatList'' f = toSing . map f . fromSing
  184.  
  185.  
  186. -- The functions are the same for SymbolList etc, everything can be
  187. -- generalized to cover the Symbol stuff too.
  188. --------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement