私はHaskellの外部関数インタフェースを試しています。私は、相互再帰を行うことができるかどうかを確認するための簡単なテストを実装したかったのです。だから、私は以下のHaskellのコードを作成しました:CとHaskellでの相互再帰でのテールコール最適化のコンパイル
module MutualRecursion where
import Data.Int
foreign import ccall countdownC::Int32->IO()
foreign export ccall countdownHaskell::Int32->IO()
countdownHaskell::Int32->IO()
countdownHaskell n = print n >> if n > 0 then countdownC (pred n) else return()
注再帰の場合はcountdownCへの呼び出しであるので、これは末尾再帰でなければならないこと。私のCコードで
、私は同様に、末尾再帰である
#include <stdio.h>
#include "MutualRecursionHaskell_stub.h"
void countdownC(int count)
{
printf("%d\n", count);
if(count > 0)
return countdownHaskell(count-1);
}
int main(int argc, char* argv[])
{
hs_init(&argc, &argv);
countdownHaskell(10000);
hs_exit();
return 0;
}
を持っています。だから、私は
MutualRecursion: MutualRecursionHaskell_stub
ghc -O2 -no-hs-main MutualRecursionC.c MutualRecursionHaskell.o -o MutualRecursion
MutualRecursionHaskell_stub:
ghc -O2 -c MutualRecursionHaskell.hs
とコンパイルmake MutualRecursion
とコンパイルします。
実行中は、8991
の印刷後にセグメンテーションが行われます。 ちょうどGCC自体が相互再帰でTCOを処理できることを確認するための試験として、私は
void countdownC2(int);
void countdownC(int count)
{
printf("%d\n", count);
if(count > 0)
return countdownC2(count-1);
}
void countdownC2(int count)
{
printf("%d\n", count);
if(count > 0)
return countdownC(count-1);
}
を行なったし、それは非常にうまく働きました。これはC言語の単帰再帰の場合とHaskellの場合の両方でも機能します。
私の質問は、外部C関数の呼び出しが末尾再帰的であることをGHCに示す方法ですか?私はスタックフレームがHaskellからCへの呼び出しから来ていると仮定していますが、Cコードは関数呼び出しの戻り値であるため、別の方法ではありません。
ダブルリターンの場所を防ぐ方法はありますか?たとえば、パターンマッチング(ベースケースの場合は0)を使用して別のルーチンを書きましたが、それは役に立ちませんでした。一般的に、GHCに、境界を越えて末尾再帰を許すようにコンパイルするように指示する方法はありますか? – Crazycolorz5
@ Crazycolorz5ランタイムの適応は非常に難しい作業です。ランタイムをC標準に適合させるべきGHCだと思われるようです。それがどれほど難しいかを理解するには、GCCを複数のリターンやガーベジコレクションなどを可能にするように修正します。それは不可能です。あなたが尋ねることは、現行のGHC、あるいは私が見る限り遠い将来に達成される可能性は非常に低いです。 – chi