为什么 Haskell(有时)被称为“最佳命令式语言”?

2024-01-03

(我希望这个问题是切中主题的——我尝试寻找答案,但没有找到明确的答案。如果这恰好偏离主题或已经得到回答,请审核/删除它。)

我记得听过/读过关于 Haskell 的半开玩笑的评论最佳命令式语言几次,这当然听起来很奇怪,因为 Haskell 通常以其功能性的特征。

所以我的问题是,Haskell 的哪些品质/特性(如果有的话)有理由证明 Haskell 被认为是最佳命令式语言——或者这实际上更像是一个笑话?


我认为这是半真半假的。 Haskell 具有惊人的抽象能力,其中包括对命令式想法的抽象。例如,Haskell 没有内置的命令式 while 循环,但我们可以直接编写它,现在它可以了:

while :: (Monad m) => m Bool -> m () -> m ()
while cond action = do
    c <- cond
    if c 
        then action >> while cond action
        else return ()

对于许多命令式语言来说,这种抽象级别是困难的。这可以用具有闭包的命令式语言来完成;例如。 Python 和 C#。

但 Haskell 还具有(高度独特的)能力描述允许的副作用,使用 Monad 类。例如,如果我们有一个函数:

foo :: (MonadWriter [String] m) => m Int

这可能是一个“命令式”函数,但我们知道它只能做两件事:

  • “输出”字符串流
  • 返回一个整数

它无法打印到控制台或建立网络连接等。结合抽象能力,您可以编写作用于“产生流的任何计算”等的函数。

Haskell 的抽象能力使其成为一种非常优秀的命令式语言。

然而,假的一半是语法。我发现 Haskell 非常冗长并且难以以命令式方式使用。这是使用上面的命令式计算的示例while循环,查找链表的最后一个元素:

lastElt :: [a] -> IO a
lastElt [] = fail "Empty list!!"
lastElt xs = do
    lst <- newIORef xs
    ret <- newIORef (head xs)
    while (not . null <$> readIORef lst) $ do
        (x:xs) <- readIORef lst
        writeIORef lst xs
        writeIORef ret x
    readIORef ret

所有 IORef 垃圾、双重读取、必须绑定读取结果、fmapping (<$>)对内联计算的结果进行操作...这一切看起来都非常复杂。这很有意义功能性的但命令式语言往往会将大部分细节隐藏起来,以使它们更易于使用。

诚然,也许如果我们使用不同的while-风格的组合器会更干净。但是,如果您将这种哲学运用得足够远(使用一组丰富的组合器来清楚地表达自己),那么您将再次达到函数式编程。命令式 Haskell 并不像设计良好的命令式语言那样“流畅”,例如Python。

总之,通过语法上的改进,Haskell 很可能是最好的命令式语言。但是,根据整容的本质,它将用外部美丽和虚假的东西取代内部美丽和真实的东西。

EDIT: 对比lastElt用这个Python音译:

def last_elt(xs):
    assert xs, "Empty list!!"
    lst = xs
    ret = xs.head
    while lst:
        ret = lst.head
        lst = lst.tail
    return ret 

线路数量相同,但每条线路的噪音要少得多。


EDIT 2

就其价值而言,这就是pureHaskell 中的替换看起来像:

lastElt = return . last

就是这样。或者,如果你禁止我使用Prelude.last:

lastElt [] = fail "Unsafe lastElt called on empty list"
lastElt [x] = return x
lastElt (_:xs) = lastElt xs

或者,如果您希望它适用于任何Foldable https://hackage.haskell.org/package/base-4.7.0.2/docs/Data-Foldable.html数据结构并认识到您实际上并不need IO处理错误:

import Data.Foldable (Foldable, foldMap)
import Data.Monoid (Monoid(..), Last(..))

lastElt :: (Foldable t) => t a -> Maybe a
lastElt = getLast . foldMap (Last . Just)

with Map, 例如:

λ➔ let example = fromList [(10, "spam"), (50, "eggs"), (20, "ham")] :: Map Int String
λ➔ lastElt example
Just "eggs"

The (.)运算符是功能组合 http://en.wikipedia.org/wiki/Function_composition.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Haskell(有时)被称为“最佳命令式语言”? 的相关文章

随机推荐