为什么 Haskell(有时)被称为“最佳命令式语言"? [英] Why is Haskell (sometimes) referred to as "Best Imperative Language"?

查看:26
本文介绍了为什么 Haskell(有时)被称为“最佳命令式语言"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(我希望这个问题是切中要害的——我试图寻找答案,但没有找到明确的答案.如果这恰好是题外话或已经回答,请适度/删除它.)

我记得曾听/读过几次关于 Haskell 是最好的命令式语言的半开玩笑的评论,这当然听起来很奇怪,因为 Haskell 通常以其功能性而闻名/em> 功能.

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

解决方案

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

while :: (Monad m) =>米布尔 ->m () ->米 ()而条件行动 = 做c <- 条件如果 c然后行动 >>同时执行操作否则返回()

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

但是 Haskell 还具有(非常独特的)能力,可以使用 Monad 类描述允许的副作用.例如,如果我们有一个函数:

foo :: (MonadWriter [String] m) =>薄荷

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

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

不能打印到控制台或建立网络连接等.结合抽象能力,你可以编写作用于任何产生流的计算"等的函数.

这完全是关于 Haskell 的抽象能力,这使它成为一种非常好的命令式语言.

然而,错误的一半是语法.我发现 Haskell 在命令式风格中使用起来非常冗长和笨拙.下面是一个使用上述 while 循环的命令式计算示例,它查找链表的最后一个元素:

lastElt :: [a] ->输入口lastElt [] = 失败空列表!!"lastElt xs = 做lst <- newIORef xsret <- newIORef (head xs)while (not .null <$> readIORef lst) $ do(x:xs) <- readIORef lstwriteIORef lst xs写IORef ret x读取IORef ret

所有的 IORef 垃圾、双重读取、必须绑定读取的结果、fmapping (<$>) 以对内联计算的结果进行操作......这就是全部只是看起来很复杂.从功能的角度来看,这很有意义,但是命令式语言倾向于将这些细节中的大部分隐藏起来,以使其更易于使用.

诚然,也许如果我们使用不同的 while 样式组合器,它会更简洁.但是,如果您将这种哲学运用得足够深入(使用一组丰富的组合器来清楚地表达自己),那么您又会回到函数式编程.命令式 Haskell 不像设计良好的命令式语言那样流动",例如蟒蛇.

总而言之,通过语法改头换面,Haskell 可能是最好的命令式语言.但是,根据整容的本质,它会用外在美丽和虚假的东西代替内在美丽和真实的东西.

EDIT:对比 lastElt 与这个 python 音译:

def last_elt(xs):assert xs, "空列表!!"lst = xsret = xs.head而 lst:ret = lst.headlst = lst.tail返回 ret

行数相同,但每行的噪音要小得多.

<小时>

编辑 2

就其价值而言,Haskell 中的替换如下所示:

lastElt = 返回.最后的

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

lastElt [] = 失败在空列表上调用了不安全的 lastElt"lastElt [x] = 返回 xlastElt (_:xs) = lastElt xs

或者,如果您希望它适用于任何 Foldable 数据结构并认识到您实际上并不需要 IO 来处理错误:

import Data.Foldable(Foldable, foldMap)导入 Data.Monoid (Monoid(..), Last(..))lastElt :: (Foldable t) =>t a ->也许一个lastElt = getLast .foldMap (Last . Just)

Map,例如:

λ➔ let example = fromList [(10, "spam"), (50, "eggs"), (20, "ham")] :: Map Int Stringλ➔ lastElt 例子只是鸡蛋"

(.) 运算符是 函数组合.>

(I hope this question is on-topic -- I tried searching for an answer but didn't find a definitive answer. If this happens to be off-topic or already answered, please moderate/remove it.)

I remember having heard/read the half-joking comment about Haskell being the best imperative language a few times, which of course sounds weird as Haskell is usually best known for its functional features.

So my question is, what qualities/features (if any) of Haskell give reason to justify Haskell being deemed the best imperative language -- or is it actually more of a joke?

解决方案

I consider it a half-truth. Haskell has an amazing ability to abstract, and that includes abstraction over imperative ideas. For example, Haskell has no built-in imperative while loop, but we can just write it and now it does:

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

This level of abstraction is difficult for many imperative languages. This can be done in imperative languages that have closures; eg. Python and C#.

But Haskell also has the (highly unique) ability to characterize allowed side-effects, using the Monad classes. For example, if we have a function:

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

This can be an "imperative" function, but we know that it can only do two things:

  • "Output" a stream of strings
  • return an Int

It can't print to the console or establish network connections, etc. Combined with the abstraction ability, you can write functions which act on "any computation that produces a stream", etc.

It's really all about Haskell's abstraction abilities that makes it a very fine imperative language.

However, the false half is the syntax. I find Haskell pretty verbose and awkward to use in an imperative style. Here is an example imperative-style computation using the above while loop, which finds the last element of a linked list:

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

All that IORef garbage, the double read, having to bind the result of a read, fmapping (<$>) to operate on the result of an inline computation... it's all just very complicated looking. It makes a whole lot of sense from a functional perspective, but imperative languages tend to sweep most of these details under the rug to make them easier to use.

Admittedly, perhaps if we used a different while-style combinator it would be cleaner. But if you take that philosophy far enough (using a rich set of combinators to express yourself clearly), then you arrive at functional programming again. Imperative-style Haskell just doesn't "flow" like a well-designed imperative language, e.g. python.

In conclusion, with a syntactic face-lift, Haskell might well be the best imperative language. But, by the nature of face lifts, it would be replacing something internally beautiful and real with something externally beautiful and fake.

EDIT: Contrast lastElt with this python transliteration:

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

Same number of lines, but each line has quite a bit less noise.


EDIT 2

For what it's worth, this is how a pure replacement in Haskell looks like:

lastElt = return . last

That's it. Or, if you forbid me from using Prelude.last:

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

Or, if you want it to work on any Foldable data structure and recognize that you don't actually need IO to handle errors:

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

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

with Map, for example:

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

The (.) operator is function composition.

这篇关于为什么 Haskell(有时)被称为“最佳命令式语言"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆