我正在尝试从列表中构造一个固定长度的向量,但不确定给编译器提供什么样的帮助来推断 fromList 的每次返回都会在类型级别将长度增加一个。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExistentialQuantification #-}

import Data.Typeable
import GHC.TypeLits

data Vec :: Nat -> * -> * where
    VNil :: Vec 0 a
    (:+) :: a -> Vec n a -> Vec (n + 1) a

infixr 5 :+

instance Show a => Show (Vec n a) where
    show VNil = " VNil"
    show (h :+ t) = show h ++ " : " ++ show t

deriving instance (Typeable n, Typeable a) => Typeable (Vec n a)

fromList :: [a] -> Vec n a
fromList xs = build xs
  where
    build (x:xs) = x :+ (build xs)
    build ([]) = error "fromList: list too short"
main.hs:27:15: error:
    • Couldn't match expected type ‘n’ with actual type ‘n + 1’
      ‘n’ is a rigid type variable bound by
        the type signature for:
          fromList :: forall a (n :: Nat). [a] -> Vec n a
        at main.hs:26:1-26
    • In the expression: build xs
      In an equation for ‘fromList’:
          fromList xs
            = build xs
            where
                build (x : xs) = x :+ (build xs)
                build ([]) = error "fromList: list too short"
    • Relevant bindings include
        fromList :: [a] -> Vec n a (bound at main.hs:27:1)
   |
27 | fromList xs = build xs
   |

Runnable code at repl.it

最佳答案

您需要编写 fromList 以便它可以提供一些未知(在编译时)Vec n an 而不是任何给定的 n 。一种方法是用连续传递风格重写它:

{-# LANGUAGE RankNTypes #-}

fromList :: [a] -> (forall n. Vec n a -> r) -> r
fromList (x:xs) cont = fromList xs (\v -> cont (x :+ v))
fromList [] cont = cont VNil

在你的例子中,编译器提示是因为 n 是一个“刚性类型变量”——换句话说,fromList 在它的类型的开头有一个隐式的 forall n,所以 fromList 的调用者可以为 fromList 提供一个任意的 n 并期望它工作.这不是您想要的,因为您希望 fromList 根据列表的长度计算 n,而不是从其调用方接收 n

因此,解决方案是让 fromList 提供 n ,通过调用本身将 n 作为 forall 类型变量的延续。

在数学术语中,您希望 n 被存在量化——存在一些 n fromList 从给定的输入生成 Vec n a——不是普遍量化的——对于所有 nfromList 将从给定的输入生成 Vec n a

除了延续传递之外,还有其他方法可以在 Haskell 中编码存在量化。这是一个替代方案:
data SomeVec a = forall n. SomeVec (Vec n a)

fromList' :: [a] -> SomeVec a
fromList' (x:xs) = case (fromList' xs) of (SomeVec v) -> SomeVec (x :+ v)
fromList' [] = SomeVec VNil

这种方法在某些情况下可能更清晰,但可能需要您为 SomeVec 实现一堆已经为 Vec 实现的实例。

(您可能还会注意到,这实际上并不是一种完全不同的方法;fromList' xs = fromList xs SomeVec。)

有关更多信息,搜索“Haskell 中的存在类型”应该会找到大量进一步的线索。

关于haskell - 无法从列表构造固定长度向量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56295092/

10-13 06:04