C++のテンプレートで。
Haskellのばあい
基本に立ち返って、まずはHaskellのばあい。
Haskellは基本的に遅延評価なので、不適切な式が含まれていても評価されないのであれば全体としては正しく評価してくれます。
ここでinfinity
を評価してしまうと無限再帰呼び出しになってしまいますが、if_
に与えられるconditionがFalse
であるかぎりinfinity
は評価さません。結果としてif_
は値を返すことができます。
if_ condition m n = if condition then m else n infinity = infinity + 1 main = print $ if_ False infinity 1
C++のテンプレートで失敗したばあい
先日、Haskellと同じ調子で次のように書いてしまいました。
template<bool F, int M, int N> struct If { static const int value = M; }; template<int M, int N> struct If<false, T, U> { static const int value = N; }; template<int N = 0> struct Infinity { static const int value = Infinity<N + 1>::value; }; int n = If<false, Infinity<>::value, 1>::value;
もう少しだけ複雑な式だったため、なぜエラーになるのかわからず半日ほど考えてしまう始末。
落ち着いて考えてみれば自明なのですが、If
に値を与えるばあい、M
やN
の値は確定していなければなりません。M
の値、N
の値を含めたIf<F, M, N>
全体が型なので当然の結果でした。
C++のテンプレートでやりなおし
数値を直接テンプレートに渡してしまったための過ちなので、テンプレートには型を与えて、その型から数値を取得するように書き換えます。
template<bool F, typename T, typename U> struct If { static const int value = T::value; }; template<typename T, typename U> struct If<false, T, U> { static const int value = U::value; }; template<int N = 0> struct Infinity { static const int value = Infinity<N + 1>::value; }; template<int N> struct Int { static const int value = N; }; int n = If<false, Infinity<>, Int<1> >::value;
このC++テンプレートを実行してみます。OS は OS X Mavericks 、実行環境は G++ 5.1 です。
$ g++ -S lazy_if.cpp $ cat lazy_if.s .section __TEXT,__text,regular,pure_instructions .section __DATA,__data .globl _n ## @n .align 2 _n: .long 1 ## 0x1
Infinity
テンプレートに惑わされず、n
に1が入ることが確認できました(爆)。