sin*_*yma 34 haskell type-safety
似乎采用/返回Int(即ByteString.hGet和Data.List.length)的常见模式与使用强烈描述类型的Haskell模式相反,因为这些情况中的许多只能处理正数.使用它是不是更好Word,或者这些功能是否部分存在Int?
mac*_*ron 27
确实,Haskell类型系统的表现力鼓励用户为他们定义的实体分配精确类型.然而,经验丰富的Haskellers将很容易地承认,必须在最终类型精度(除了Haskell类型系统的当前限制之外并不总是可达到)和方便之间取得平衡.简而言之,精确类型仅对某一点有用.除此之外,他们往往只会造成额外的官僚作风,几乎没有收获.
让我们用一个例子说明问题.考虑阶乘函数.对于n大于1的所有因子,阶乘n是偶数,而阶乘1不是非常有趣,所以让我们忽略那个.因此,为了确保我们在Haskell中的阶乘函数的实现是正确的,我们可能会想要引入一个新的数字类型,它只能表示无符号的偶数整数:
module (Even) where
newtype Even = Even Integer
instance Num Even where
...
fromInteger x | x `mod` 2 == 0 = Even x
| otherwise = error "Not an even number."
instance Integral Even where
...
toInteger (Even x) = x
Run Code Online (Sandbox Code Playgroud)
我们将此数据类型密封在不导出构造函数的模块中,使其成为抽象,并使其成为所有相关类型类Int的实例.现在我们可以将以下签名赋予factorial:
factorial :: Int -> Even
Run Code Online (Sandbox Code Playgroud)
factorial肯定的类型比我们刚刚说它返回时更精确Int.但你会发现factorial用这种类型定义真的很烦人,因为你需要一个乘法版本乘以一个(偶数或奇数)Int和一个Even和产生和Even.更重要的是,你可能不得不在客户端代码中toInteger调用结果引入无关的调用factorial,这可能是杂乱和噪声的重要来源,几乎没有收获.此外,所有这些转换功能都可能对性能产生负面影响.
另一个问题是,在引入新的,更精确的类型时,您通常最终必须复制所有类型的库函数.例如,如果您引入List1 a非空列表的类型,则必须重新实现Data.List已提供的许多函数,但[a]仅限于此.当然,可以制作ListLike类型类的这些函数方法.但是你很快就会得到各种类型的adhoc类型和其他样板,同样没有多少收获.
最后一点是,不应该考虑Word成为无符号变体Int.所述的Haskell报告叶的实际尺寸Int未指定的,并且只保证这种类型的应能够在范围代表整数[ - 2 29,2 29 - 1].Word据说该类型提供未指定宽度的无符号整数.不能保证在任何符合要求的实现中,a Word的宽度对应于a的宽度Int.
虽然我提出了防止过度类型扩散的观点,但我确实承认引入一种Natural天然物质可能会很好.不过说到底,是否哈斯克尔应该有一个专门类型的自然数中,除了Int,Integer以及各种Word* 的类型,在很大程度上是口味的问题.目前的情况可能在很大程度上只是历史事故.
gla*_*erl 22
同样的推理也适用于C.使用更精确类型的原因是为了防止错误.在这种情况下,错误,例如在没有意义的情况下尝试使用负数.但是Word,如unsigned int在C中那样,上溢或下溢的行为就是环绕.如果您尝试在期望Word(或unsigned int)的情况下使用负数,则不会让编译器对您大喊大叫,甚至在运行时也不会出现异常.你得到一个很大的正数.你无法区分任何其他("合法")大正数!
看这个:
Prelude Data.Word> -1 :: Word
4294967295
Run Code Online (Sandbox Code Playgroud)
你不会犯错误而不能犯错,而是让他们无法察觉.使用Int(和int),您至少可以手动检查负值.随着Word和unsigned int,你什么都没有.
有价值的是无符号类型,它通过抛出异常来对溢出或下溢作出反应.这仍然不会使错误变得不可能,但它会使它们更容易被发现.但是,它会带来性能成本.*我不知道是否可以在编译时排除它们,但这似乎并不容易.
*至少,x86需要额外的指令 - 每次操作后! - 检查是否发生上溢或下溢.我不知道是否有一个"免费"的架构,虽然它会很好.或者可能是我们对浮点数(可能代替最负数)的区别NaN值,它将用于表示不可代表的值...
我的第一个猜测是,无符号算术有一些问题会导致愚蠢的错误,如果你不注意:
Prelude Data.Word> let x = 0 :: Word in if x - 1 > x then "Ouch" else "Ohayoo"
"Ouch"
Run Code Online (Sandbox Code Playgroud)
它会在多态函数中出现一些看似正确的问题:
Prelude Data.Word> let f acc x y = if x <= y then f (acc + 1) x (y - 1) else acc
Prelude Data.Word> :t f
f :: (Num a1, Num a, Ord a) => a1 -> a -> a -> a1
Run Code Online (Sandbox Code Playgroud)
使用标准长度功能:
Prelude Data.Word> let len' x = if null x then 0 else 1 + len' (tail x) :: Int
Prelude Data.Word> :t len'
len' :: [a] -> Int
Prelude Data.Word> f 0 0 (len' [1,2,3])
4
Run Code Online (Sandbox Code Playgroud)
并使用长度函数returing Word:
Prelude Data.Word> let len x = if null x then 0 else 1 + len (tail x) :: Word
Prelude Data.Word> :t len
len :: [a] -> Word
Prelude Data.Word> f 0 0 (len [1,2,3])
...diverges...
Run Code Online (Sandbox Code Playgroud)
当然,如果这些函数返回Word而不是Int,那么您需要继续将Words转换为Ints以在其他常见位置使用它.