检测 Haskell 中的底部值

发布于 2025-01-08 18:11:19 字数 710 浏览 0 评论 0 原文

我编写了一个 haskell 函数,它将列表 xs 拆分为 (init xs, last xs) ,如下所示:

split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, x)
        split' acc (x:xs) = split' (x:acc) xs

由于空列表不能以这种方式拆分,因此与空列表不匹配。但是,我不想简单地让函数出错...。因此,我定义了以下内容:

split [] = ([], undefined)

由于惰性求值,我可以定义一个安全的 init ,它只是返回空列表的空列表:

init' = fst . split

如果我尝试访问,是否有某种方法可以检测到未定义的内容它,这样

last' xs
  | isUndefined (snd xs) = ...
  | otherwise            = ...

我就知道也许要么,并且这些是表达我想要的更好的选择。但是我想知道是否有一种方法可以检测未定义的实际值,即捕获错误,例如捕获异常。

I've written a haskell function which splits a list xs into (init xs, last xs) like so:

split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, x)
        split' acc (x:xs) = split' (x:acc) xs

Since an empty list can not be split in this way, there is no match for the empty list. However, I did not want to simply error ... the function. Thus I defined the following:

split [] = ([], undefined)

Thanks to lazy evaluation I can thus define a safe init which simply returns the empty list for the empty list:

init' = fst . split

Is there some way how I could detect the undefined if I tried to access it, such that

last' xs
  | isUndefined (snd xs) = ...
  | otherwise            = ...

I do know about Maybe and Either, and that those are a better choice for expressing what I want. However I wondered if there is a way to detect an actual value of undefined, i.e. in terms of catching errors, like catching exceptions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

☆獨立☆ 2025-01-15 18:11:19

undefined 并不比使用 error 更好。事实上,Prelude 中的 undefined定义

undefined =  error "Prelude.undefined"

现在,不会导致错误的函数被称为“总函数”,即它对所有输入值都有效。

您当前实现的 split 函数具有签名

split :: [a] -> ([a], a)

这是一个问题,因为类型签名承诺结果始终包含一个列表和一个元素,这显然不可能提供泛型的空列表类型。

Haskell 中解决此问题的规范方法是更改​​类型签名,以表明有时我们没有第二项的有效值。

split :: [a] -> ([a], Maybe a)

现在您可以为获得空列表的情况编写正确的实现

split [] = ([], Nothing)
split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, Just x)
        split' acc (x:xs) = split' (x:acc) xs

现在您可以通过模式匹配来检测缺失值的情况

let (init', last') = split xs
in case last' of
    Nothing -> ... -- do something if we don't have a value
    Just x  -> ... -- do something with value x

undefined is no better than using error. In fact, undefined in Prelude is defined as

undefined =  error "Prelude.undefined"

Now, a function that can't result in an error is called a "total function", i.e. it is valid for all input values.

The split function you've currently implemented has the signature

split :: [a] -> ([a], a)

This is a problem, since the type signature promises that the result always contains a list and an element, which is clearly impossible to provide for empty lists of generic type.

The canonical way in Haskell to address this is to change the type signature to signify that sometimes we don't have a valid value for the second item.

split :: [a] -> ([a], Maybe a)

Now you can write a proper implementation for the case where you get an empty list

split [] = ([], Nothing)
split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, Just x)
        split' acc (x:xs) = split' (x:acc) xs

Now you can detect the missing value case by pattern-matching

let (init', last') = split xs
in case last' of
    Nothing -> ... -- do something if we don't have a value
    Just x  -> ... -- do something with value x
相守太难 2025-01-15 18:11:19

因为bottom包含非终止,所以函数isUn​​define必须解决暂停问题,因此不能存在。

但请注意,即使它存在,您仍然无法判断元组第二个元素中的未定义值是否通过 split 函数放入其中,或者列表的最后一个元素是否已未定义。

Because bottom subsumes non-termination, the function isUndefined would have to solve the halting problem and thus cannot exist.

But note that even if it existed, you still could not tell if the undefined value in the 2nd element of your tuple was put there through your split function or if the last element of the list was already undefined.

哑剧 2025-01-15 18:11:19

error 函数在评估之前不会执行任何操作,因此您可以执行以下操作:

split [] = ([], error "split: empty list")

last' = snd . split

The error function doesn't do anything until it is evaluated, so you can do something like:

split [] = ([], error "split: empty list")

last' = snd . split
叹梦 2025-01-15 18:11:19

来自 Haskell 2010 语言报告 >简介 # 值和类型

Haskell 中的错误在语义上等同于 ⊥(“底部”)。从技术上讲,它们与非终止没有区别,因此该语言不包含检测错误或对错误采取行动的机制。

需要明确的是,undefined 旨在成为一种将 ⊥ 插入到程序中的方法,并且考虑到(如 shang 所指出的)undefined 是根据 error 定义的,因此,“没有检测或作用于未定义的机制”。

From the Haskell 2010 Language Report > Introduction # Values and Types

Errors in Haskell are semantically equivalent to ⊥ (“bottom”). Technically, they are indistinguishable from nontermination, so the language includes no mechanism for detecting or acting upon errors.

To be clear, undefined is intended to be a way to insert ⊥ into your program, and given that (as shang noted) undefined is defined in terms of error, there is, therefore, "no mechanism for detecting or acting upon undefined".

百变从容 2025-01-15 18:11:19

虽然从语义上讲,Ingo 的答案是正确的,但如果您使用 GHC,有一种方法可以使用几个“不安全”函数,尽管不太完美,就像您传递给它一个包含异常的 IO a 类型的计算一样,它会返回True,有效。不过,这有点作弊:)。

import Control.Exception
import System.IO.Unsafe
import Unsafe.Coerce

isUndefined :: a -> Bool
isUndefined x = unsafePerformIO $ catch ((unsafeCoerce x :: IO ()) >> return False) (const $ return True :: SomeException -> IO Bool)

我知道这很可怕,但它仍然有效。但它不会检测到非终止;)

Although semantically speaking Ingo's answer is correct, if you're using GHC, there is a way using a couple of "unsafe" functions that, although not quite perfect as if you pass it a computation of type IO a which contains an exception it will return True, works. It's a bit of a cheat though :).

import Control.Exception
import System.IO.Unsafe
import Unsafe.Coerce

isUndefined :: a -> Bool
isUndefined x = unsafePerformIO $ catch ((unsafeCoerce x :: IO ()) >> return False) (const $ return True :: SomeException -> IO Bool)

I know this is horrible, but none the less it works. It won't detect non termination though ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文