私はそれを半分真実だと考えています。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 クラスを使用して、許可された side-effects を特徴付ける(非常にユニークな) 機能もあります。たとえば、次の関数があるとします。
foo :: (MonadWriter [String] m) => m Int
これは「命令型」関数である可能性がありますが、次の 2 つのことしかできないことがわかっています。
コンソールに出力したり、ネットワーク接続を確立したりすることはできません。抽象化機能と組み合わせると、「ストリームを生成する任意の計算」などに作用する関数を作成できます。
Haskell が非常に優れた命令型言語になっているのは、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 は最高の命令型言語になる可能性があります。しかし、フェイスリフトの性質上、内面的に美しく本物のものを、外面的に美しく偽物に置き換えることになります.
編集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
ライン数は同じですが、各ラインのノイズはかなり少なくなっています。
編集2
価値のあるものとして、これはHaskellでの純粋な置き換えがどのように見えるかです:
lastElt = return . last
それでおしまい。または、次の使用を禁止する場合Prelude.last
:
lastElt [] = fail "Unsafe lastElt called on empty list"
lastElt [x] = return x
lastElt (_:xs) = lastElt xs
または、任意のデータ構造で動作させたいが、実際にはエラーを処理する必要Foldable
がないことを認識している場合: IO
import Data.Foldable (Foldable, foldMap)
import Data.Monoid (Monoid(..), Last(..))
lastElt :: (Foldable t) => t a -> Maybe a
lastElt = getLast . foldMap (Last . Just)
Map
たとえば、次のようにします。
λ➔ let example = fromList [(10, "spam"), (50, "eggs"), (20, "ham")] :: Map Int String
λ➔ lastElt example
Just "eggs"
(.)
演算子は関数合成です。