如何解构SNat(单身人士)

时间:2017-08-27 13:57:34

标签: haskell singleton type-theory

我正在试验Haskell中的从属类型,并且在“单身人士”包的paper中遇到了以下内容:

replicate2 :: forall n a. SingI n => a -> Vec a n
replicate2 a = case (sing :: Sing n) of
  SZero -> VNil
  SSucc _ -> VCons a (replicate2 a)

所以我试着自己实现这个,只是想知道它是如何工作的:

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE TypeOperators       #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}

import           Data.Singletons
import           Data.Singletons.Prelude
import           Data.Singletons.TypeLits

data V :: Nat -> * -> * where
  Nil  :: V 0 a
  (:>) :: a -> V n a -> V (n :+ 1) a

infixr 5 :>

replicateV :: SingI n => a -> V n a
replicateV = replicateV' sing
  where replicateV' :: Sing n -> a -> V n a
        replicateV' sn a = case sn of
            SNat -> undefined -- what can I do with this?

现在问题是Sing的{​​{1}}实例没有NatSZero。只有一个名为SSucc的构造函数。

SNat

这与允许匹配的其他单身人士不同,例如> :info Sing data instance Sing n where SNat :: KnownNat n => Sing n STrue,例如以下(无用)示例:

SFalse

您可以使用data Foo :: Bool -> * -> * where T :: a -> Foo True a F :: a -> Foo False a foo :: forall a b. SingI b => a -> Foo b a foo a = case (sing :: Sing b) of STrue -> T a SFalse -> F a 获取基本类型,但这当然允许GHC检查输出向量的类型:

fromSing

所以我的问题是:如何实施-- does not typecheck replicateV2 :: SingI n => a -> V n a replicateV2 = replicateV' sing where replicateV' :: Sing n -> a -> V n a replicateV' sn a = case fromSing sn of 0 -> Nil n -> a :> replicateV2 a

修改

erisco给出的答案解释了为什么我解构replicateV的方法不起作用。但即使使用SNat库,我也无法使用GHC的内置type-natural类型为replicateV数据类型实施V。< / p>

例如,以下代码编译:

Nat

但是这似乎没有给编译器提供足够的信息来推断replicateV :: SingI n => a -> V n a replicateV = replicateV' sing where replicateV' :: Sing n -> a -> V n a replicateV' sn a = case TN.sToPeano sn of TN.SZ -> undefined (TN.SS sn') -> undefined 是否n。例如,以下内容给出了编译器错误:

0

这会出现以下错误:

replicateV :: SingI n => a -> V n a
replicateV = replicateV' sing
  where replicateV' :: Sing n -> a -> V n a
        replicateV' sn a = case TN.sToPeano sn of
            TN.SZ       -> Nil
            (TN.SS sn') -> undefined

所以,我原来的问题仍然存在,我仍然无法对src/Vec.hs:25:28: error: • Could not deduce: n1 ~ 0 from the context: TN.ToPeano n1 ~ 'TN.Z bound by a pattern with constructor: TN.SZ :: forall (z0 :: TN.Nat). z0 ~ 'TN.Z => Sing z0, in a case alternative at src/Vec.hs:25:13-17 ‘n1’ is a rigid type variable bound by the type signature for: replicateV' :: forall (n1 :: Nat) a1. Sing n1 -> a1 -> V n1 a1 at src/Vec.hs:23:24 Expected type: V n1 a1 Actual type: V 0 a1 • In the expression: Nil In a case alternative: TN.SZ -> Nil In the expression: case TN.sToPeano sn of { TN.SZ -> Nil (TN.SS sn') -> undefined } • Relevant bindings include sn :: Sing n1 (bound at src/Vec.hs:24:21) replicateV' :: Sing n1 -> a1 -> V n1 a1 (bound at src/Vec.hs:24:9) 做任何有用的事情。

2 个答案:

答案 0 :(得分:4)

这里有两种自然观念。一个是&#34;字面自然&#34; (即0,1,2等),另一个是#34; Peano naturals&#34; (即Z,S Z,S(S Z)等)。纸张使用的那个显然是Peano自然,但单身人士使用的是字面自然。

值得庆幸的是,还有另一个名为type-natural的软件包,它定义了Peano自然以及conversion to literal naturalsconversion from literal naturals

答案 1 :(得分:1)

从评论中,我担心我必须遗漏一些非常明显的东西,但这是我的看法。整点:

replicate2 :: forall n a. SingI n => a -> Vec a n
replicate2 a = case (sing :: Sing n) of
  SZero -> VNil
  SSucc _ -> VCons a (replicate2 a)

是,为了在函数具有常规返回类型VNil :: Vec a 0时返回Vec a n,您需要将n专门化为0,并进行模式匹配GADT提供了一种方法,只要你有一个构造函数,如SZero,暗示n ~ 0

现在,singleton包中的SNat没有这样的构造函数。据我所知,获得一个的唯一方法是为自然构建一个全新的单例类型并实现必要的类型族。也许你可以用Nat包裹的方式来做,所以你比Peano结构更接近SZero :: Sing (SN 0)SNonZero :: Sing (SN n),但我不知道

当然,还有另一种方法来专门化一个返回Vec a n返回Vec a 0的函数,即类型类。

如果您愿意放弃某些显式单例机制并切换到类型类(并且还允许重叠和不可判定的实例),则以下似乎可行。我不得不稍微修改V的定义以使用n :- 1而不是n :+ 1,但我不会认为会造成问题。

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE ScopedTypeVariables   #-}

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE OverlappingInstances  #-}
{-# LANGUAGE UndecidableInstances  #-}

import           Data.Singletons
import           Data.Singletons.Prelude
import           Data.Singletons.TypeLits

data V :: Nat -> * -> * where
  Nil  :: V 0 a
  (:>) :: a -> V (n :- 1) a -> V n a

infixr 5 :>

class VC n a where
  replicateV :: a -> V n a

instance VC 0 a where
  replicateV _ = Nil

instance VC (n :- 1) a => VC n a where
  replicateV x = x :> replicateV x

instance (Show a) => Show (V n a) where
  show Nil = "Nil"
  show (x :> v) = show x ++ " :> " ++ show v

headV :: V (n :+ 1) a -> a
headV (x :> _) = x

tailV :: ((n :+ 1) :- 1) ~ n => V (n :+ 1) a -> V n a
tailV (_ :> v) = v

main = do print (replicateV False   :: V 0 Bool)
          print (replicateV 1       :: V 1 Int)
          print (replicateV "Three" :: V 3 String)