Haskell: 再帰とI/O

Haskellの勉強をはじめて1ヶ月ほど経ちました(実際勉強したのは1週間くらいです)。途中までは順調でしたが、モナドがやっぱり難しい。mapとかfilterのような高階関数は割とすんなり理解できたのですが。。

今日は久しぶりにHaskellをさわってみました。再帰とちょっとしたI/Oをするプログラムを作ってみました。

import System -- getArgs 

crazy :: String -> IO ()
crazy s =
    putStr s >> crazy s 

main = do
    [arg] <- getArgs 
    crazy arg

コンパイルと実行は以下のようにします。(コンパイルにはGlasgow Haskell Compilerを使っています)
実行すると、プログラムの引数に渡した文字列(foo)が標準出力に繰り返し出力されます。終了条件が書かれていないので、ずっと出力し続けます。

% ghc -o crazy crazy.hs
% ./crazy foo
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
[...]

では、Perlで同じようなことをやらせてみるとどうなるでしょうか?
試してみました。

use strict;
use warnings;

sub crazy
{
    my ($arg) = @_;
    print $arg;
    crazy($arg);
}

my $arg = shift;
crazy($arg);

実行すると、warningが出ました。再帰が深すぎて怒られてしまいました。

% perl crazy.pl foo
Deep recursion on subroutine "main::crazy" at test.pl line 8.
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
[...]

よく考えると、どのようにしてHaskellは今回のような終了条件が無い再帰をうまく処理しているのか気になります。
普通の感覚だと、関数呼び出しを管理しているスタックがオーバーフローして、まずい事になりそうな感じがするのですが。