Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何强制Haskell检查列表的长度?_Haskell_Types_Type Level Computation - Fatal编程技术网

如何强制Haskell检查列表的长度?

如何强制Haskell检查列表的长度?,haskell,types,type-level-computation,Haskell,Types,Type Level Computation,我们可以这样定义函数f和g: f :: [a] -> [a] -> [a] f = (++) g :: [a] -> [a] -> [a] g = zipWith (+) f和g都将两个列表作为参数并返回一个新列表,但它们不同:f返回一个较长的列表,其长度是输入的总和,g同时处理具有相同长度的列表。如何向Haskell解释这个问题?根据我收集的信息,您希望您的g在执行操作之前先检查列表的长度是否相同。这相对容易: -- Correct type signature a

我们可以这样定义函数
f
g

f :: [a] -> [a] -> [a]
f = (++)

g :: [a] -> [a] -> [a]
g = zipWith (+)

f
g
都将两个列表作为参数并返回一个新列表,但它们不同:
f
返回一个较长的列表,其长度是输入的总和,
g
同时处理具有相同长度的列表。如何向Haskell解释这个问题?

根据我收集的信息,您希望您的
g
在执行操作之前先检查列表的长度是否相同。这相对容易:

-- Correct type signature as @chris mentioned
g :: Num a => [a] -> [a] -> [a]
g xs ys = if length xs == length ys then zipWith (+) xs ys else error "Incompatible lists"
但是,这是一种非常糟糕的抛出错误的方法,最好使用
Maybe
monad:

g :: Num a => [a] -> [a] -> Maybe [a]
g xs ys = if length xs == length ys then Just $ zipWith (+) xs ys else Nothing

您想要的是将列表的长度编码到类型系统中。换句话说,在类型系统中对自然数进行编码并对其进行操作。这是可能的,尽管它涉及一些类型的欺骗。有很多图书馆可以做到这一点,其中之一就是
TaggedList
将其长度标记为类型级自然数。那么函数的类型将如下所示

import Data.List.Tagged as T
import TypeLevel.NaturalNumber.Operations (Plus)

f :: TaggedList n a -> TaggedList m a -> TaggedList (Plus n m) a
f = T.append

g :: (Num a) => TaggedList n a -> TaggedList n a -> TaggedList n a
g x = T.zipf (T.map (+) x) -- apparently the Tagged library lacks zipWith
                           -- so we implement it ourselves
这清楚地区分了列表的长度


另请参见。

实际上
g
将具有类型
Num a=>[a]->[a]->[a]
,因此它不适用于任意列表,仅适用于数字列表。另外,你能澄清你的问题吗?@chris好的,我可以强制
f::Num a=>[a]->[a]->[a]
,所以这两种类型的声明没有区别。我的观点是,Haskell不会同时抱怨
f(g[1..10][11..20])[21..30]
g(f[1..10][11..20])[21..30]
,但我希望Haskell能够为后一个问题提供警告或错误(编译时,而不是运行时)。@SaltyEgg这将需要跟踪类型中的长度参数。在Haskell中可以笨拙地执行此操作,但您有一个相当困难的限制,即只有最现代的GHC才有类型级计算(如整数加法)的开始。你可能更希望这样做的是Idris或Agda。@chris将其视为zipWith(,)。我认为OP希望长度以Idris向量之类的类型表示,而不仅仅是在运行时检查。@Lee是的,如果我误用了
g
@SaltyEgg Haskell不能这样工作,我希望Haskell在编译代码时能够抱怨,它怎么可能知道您是否在编译时传入了不同大小的列表?我的大部分数据来自外部硬件或大文件,这些文件在编译程序时甚至不一定存在,因此它无法检查这样的代码。Haskell列表不是向量,它们更像是链表,所以您可以尝试查看
Repa
库中的向量?我相信它们的大小被编码到了类型中,但我可能错了。我检查了,Repa编码的是向量的形状(1D、2D、3D等),而不是大小。您可以使用类型级别的
Nats
,但这会变得很难看,速度也很慢。通常最好不要使用类型系统来进行计算,有更优雅的方式来表达您希望对运行时错误处理所做的操作。您所说的是对的,因此我不会投反对票,但OP对自己问题的了解比您少,因此他可能也没有想到其他列表类型。