我认为这是半真半假的。 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.