Haskell 相当于 assert(0)

Haskell equivalent of assert(0)

我正在尝试找出编写 Haskell 等同于 assert(0) 的最佳实践。我知道类型安全规定必须从 scanList 返回一个整数,但是我想知道是否有比我写的更好的方法。有什么办法可以避免卡在那里的任意数字 923

module Main (main) where

import Control.Exception (assert)

l = [
    Left "1",
    Left "1",
    Right 1,
    Right 1,
    Left "9"]

scanList :: [ Either String Int ] -> Int
scanList [    ] = 0
scanList (x:xs) = case x of
    Right i -> i + scanList xs
    Left  s ->
        if read s < 8
        then read s + scanList xs
        else assert False $ 923

main = do
    print $ scanList l

来自 assert 的文档:

If the first argument evaluates to True, then the result is the second argument. Otherwise an AssertionFailed exception is raised, containing a String with the source file and line number of the call to assert.

因此,与其将 False 作为第一个参数,您实际上可以在那里检查您的 if 条件:

scanList (x:xs) = case x of
    Right i -> i + scanList xs
    Left  s ->
        assert (read s < 8) (read s + scanList xs)

更惯用的 Haskell 设计是保持纯函数 总数 。使用 assert 时,您将抛出异常,这使得函数 partial。这意味着您不能再信任函数的类型。它声称具有 [Either String Int] -> Int 类型,但在各种条件下会在 运行 时间出现异常而失败。

总函数要么保留在 Either monad 中,要么可以转换为 Maybe:

import Text.Read

scanList :: (Num a, Read a, Ord a) => [Either String a] -> Maybe a
scanList [] = Just 0
scanList (x:xs) =
  case x of
    Right i -> fmap (+ i) $ scanList xs
    Left s -> 
      case readMaybe s of
        Just i -> if i < 8 then fmap (+ i) $ scanList xs else Nothing
        Nothing -> Nothing

您可以大大简化代码,但我选择使其在结构上尽可能接近 OP。

对于像 [Either String a] -> Maybe a 这样的类型,任何调用者都知道他们必须处理 JustNothing 两种情况,而不必求助于阅读函数的代码或文档有问题。