基本的な構文

7.7 定義と束縛

let式とwhere節

確かに似てます。違いの説明がなければ、理解出来ませんでした。
第一の違い、letは式で、whereは節。
式は値を持つけど、whereは値を持たない。


letは式なので、let式をそのまま関数の定義や値として扱うことが出来るけど、
where節はあくまで、他の式をサポートする立場。


使われ方もそんな感じで分類されている。
こんな感じ。

  • myLet.hs
main = do print $ myLet 10

myLet :: Int -> Int
myLet x = let xs = [0..x]
          in myWhere xs
              where
                myWhere []     = 0
                myWhere (x:xs) = x + myWhere xs


let式は関数内で変数を導入するために使われる。
C言語でいうと(constな)ローカル変数のような感じ。
where節は関数スコープでのみ有効な関数を定義するために使われる。
C言語では、関数スコープで定義されたマクロみたいなものかなぁ…。こんな感じ。

  • myLet.c
int myLet(int x) {
    int* xs;
    int  i, n;
    #define myWhere(nn, _xs, nm)        \
        do {                            \
            int ii;                     \
            nn = 0;                     \
            for(ii = 0; ii < nm; ii ++) \
                nn += _xs[ii];          \
        } while(0)

    xs = (int*)malloc(sinzeof(int) * (x + 1));
    if(0 == xs) {
        return 0;
    }

    for(i = 0; i < x + 1; i ++) {
        *(xs + i) = i;
    }

    myWhere(n, xs, x + 1);

    free(xs);

    return n;
}


何だかすごいことになってしまった(汗)。
ビルド出来なければすいません。ふいんきだけでも…。

変数のシャドウイングについて

なるほど、let式で導入する変数の編数名が同じだった時は後勝ちなんですね。
たとえばこんな感じ。

main = do print $ myShadow 10

myShadow :: Int -> Int
myShadow x = let cub = pow * x
                 pow = x * x
             in
               let cub = x
               in
                 cub * pow
% ghc -W -o myShadow myShadow.hs 

myShadow.hs:4:17: Warning: Defined but not used: `cub'
% ./myShadow 
1000


あれれ、シャドウイングが起こっている変数(cub)が使われていないと言われました。
結果を見ると意図通りですね。
でも同じ変数名を導入するのはあんまりよくないですよね。出来れば使わない方がよさげです。

定義中での相互参照

これ面白いですね。Haskellらしいというか。
C言語でもこんな感じでよくやるんですが、

int function(int x) {
    int xx = x * x;
    int xxx = x * xx;
    int xxxx = x * xxx;

    // …
}


C言語ではあんまりよい作法とは言えませんけどね。
でもHaskellでは順番は関係ない。さすが遅延評価!
もちろんC言語では順番が超重要です。

int function(int x) {
    int xxxx = x * xxx;
    int xxx = x * xx;
    int xx = x * x;

    // …
}


なんて書いたら即エラーです。でもHaskellでは

  • myFunc.hs
main = do print $ myFunc 10

myFunc :: Int -> Int
myFunc x = let xxxx = x * xxx
               xxx  = x * xx
               xx   = x * x
             in
               xxxx * xxx * xx * x


でも問題なし♪
遅延評価ってとても人間的ですよね。
人間なら上を参照してようが、下を参照してようが関係ないですから。