使用haskell的单例,如何编写`fromList :: [a] - > Vec a n`?

时间:2017-09-24 23:30:12

标签: haskell singleton dependent-type singleton-type

作为理解singletons之旅的一部分,我试图弥合编译时安全性与将运行时值提升到相关类型安全性之间的差距。

我认为,“运行时”值的最小示例是一个采用无界列表的函数,并将其转换为大小索引的向量。以下骨架提供长度索引向量,但我无法确定如何编写fromList

我已经考虑过让函数采用size参数,但我怀疑它可以保持隐含。

{-# LANGUAGE GADTs                #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE TemplateHaskell      #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE TypeInType           #-}
{-# LANGUAGE UndecidableInstances #-}

import           Data.Singletons
import           Data.Singletons.TH

$(singletons
  [d|
    data Nat = Z | S Nat deriving (Show)
  |])

data Vec a n where
  Nil :: Vec a Z
  Cons :: a -> Vec a n -> Vec a (S n)

instance Show a => Show (Vec a n) where
  show Nil = "Nil"
  show (Cons x xs) = show x ++ " :< " ++ show xs

fromListExplicit :: forall (n :: Nat) a. SNat n -> [a] -> Vec a n
fromListExplicit SZ _ = Nil
fromListExplicit (SS n) (x : xs) = Cons x (fromListExplicit n xs)

ex1 = fromListExplicit (SS (SS (SS SZ))) [1..99]
-- 1 :< 2 :< 3 :< Nil

fromListImplicit :: (?????) => [a] -> Vec a n
fromListImplicit = ?????

main :: IO ()
main = do
  xs <- readLn :: IO [Int]
  print $ fromListImplicit xs

2 个答案:

答案 0 :(得分:5)

使用Haskell可能 ,因为Haskell还没有完全依赖类型(尽管GHC might in the future)。注意

fromList :: [a] -> Vec a n

an都是普遍量化的,这意味着用户应该能够选择他们的n并获得正确大小的Vec。这是没有意义的!诀窍是n并不是供用户选择 - 它必须是输入列表的长度。 (出于同样的原因,fromList :: Integer -> [a] -> Vec a n将不再有用 - 大小提示必须是类型级别的。)

期待像Idris这样依赖类型的语言,你可以定义

fromList : (l : List elem) -> Vec (length l) elem

事实上他们是define this in the standard library

那么,可以你做什么?没有说Vec的长度等于输入列表的大小(需要将“输入列表的长度”提升到类型级别),你可以说它有一些长度。

data SomeVec a where { SomeVec :: Vec a n -> SomeVec a }

list2SomeVec :: [a] -> SomeVec a
list2SomeVec [] = SomeVec Nil
list2SomeVec (x:xs) = case list2SomeVec xs of
                        SomeVec ys -> SomeVec (x `Cons` ys)

这并不是非常有用,但总比没有好。

答案 1 :(得分:2)

您可以使用存在量化的类型变量来执行此操作,如@ Alec的答案,或等效地通过重写继续传递样式。诀窍是给fromList一个延续(函数),它是Vec大小的多态;然后,在延续中,您可以访问表示大小的类型变量:

data Vec n a where
  Nil :: Vec Z a
  Cons :: a -> Vec n a -> Vec (S n) a

deriving instance (Show a) => Show (Vec n a)

fromList :: [a] -> (forall n. Vec n a -> r) -> r
fromList [] k = k Nil
fromList (x : xs) k = fromList xs $ \ xs' -> k (Cons x xs')

-- fromList [1, 2, 3] show == "Cons 1 (Cons 2 (Cons 3 Nil))"

您无法知道n的实际,因为它在编译时不可用。

如果您将Nat替换为GHC.TypeLits中的KnownNat,我认为您可以通过从n创建SomeNatfromJust (someNatVal (fromIntegral (length xs)))约束natVal使用var min = d3.min(data, function(d){return d.value}); var max = d3.max(data, function(d){return d.value}); var absMax = Math.max(Math.abs(min), Math.abs(max)); x.domain([-absMax, absMax]); 运行时长度,然后在运行时使用{{1}}获取实际长度值。我不是很熟悉如何做到这一点,它可能需要ghc-typelits-natnormalise插件,但它可能是一个起点。