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

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

问题描述

(我希望这个问题是关于主题的 - 我试着寻找答案,但没有找到明确的答案。如果这是恰当的话题或已经回答,请温和/删除它。 )



我记得几次听过/读过关于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)=>这可能是一个势在必行的功能,但我们知道它只能做两件事:




  • 输出字符串流

  • 返回一个Int


  • 它不能打印到控制台或建立网络连接等。结合抽象能力,您可以编写函数,这些函数作用于产生任何计算一个流等等。

    这实际上是关于Haskell的抽象能力,它使它成为一个非常好的命令式语言。

    然而,错误的一半是语法。我发现Haskell在命令式风格中使用起来非常冗长和尴尬。这是一个使用上面的循环进行命令式计算的例子,它找到链表的最后一个元素:

      lastElt :: [a]  - > IO a 
    lastElt [] =失败空列表!!
    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(< $> )对内联计算的结果进行操作......看起来都很复杂。从功能角度来看,它具有很大的意义,但命令式语言倾向于将这些细节中的大部分细节扫描到地毯上,以便于使用。不可否认,也许如果我们使用不同的样式的组合器,它会更干净。但是如果你把这个哲学放到了足够的地步(用一组丰富的组合器来清晰地表达自己),那么你就可以再次进入函数式编程。命令式Haskell并不像设计良好的命令式语言那样流动,例如python。



    总之,通过句法修饰,Haskell可能是最好的命令式语言。但是,由于脸部提升的性质,它将取代内部美丽和真实的东西,外部美丽和假的东西。



    编辑:对比度 lastElt 用这个python音译:

      def last_elt(xs):
    断言xs,空单!!
    lst = xs
    ret = xshead
    而lst:
    ret = lst.head
    lst = lst.tail
    return ret

    相同的行数,但每行都有相当少的噪音。




    编辑2



    值得一提的是, 纯粹的在Haskell中的替换看起来像:

      lastElt = return。最后

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

      lastElt [] =失败不安全的lastElt在空列表上调用
    lastElt [x] = return x
    lastElt(_:xs)= lastElt xs

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

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

    lastElt ::(Foldable t)=> t a - >也许
    lastElt = getLast。 foldMap(Last。Just)

    with Map ,例如:

     λ➔让示例= fromList [(10,spam),(50,eggs) ,(20,ham)] :: Map Int String 
    λlastElt示例
    只是eggs

    (。)运算符是功能组合


    (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天全站免登陆