let 式は where 節にとてもよく似ている。
where は関数の終わりで変数を束縛し、
その変数はガードを含む関数全体から見える。
それに対し、let式は、どこでも変数を束縛でき、そしてlet自身も式になる。
しかし let 式が作る束縛は局所的で、ガード間で共有されない。
束縛を行う Haskell の他の構文と同じく、let 式のパターンマッチも使える。
letは、
let bindings in expression という形を取る。
束縛を先に書いて、inのあとに式を追加するという感じ。
ただし、letは式だけどwhereは式じゃないことに留意すること。
cylinder :: Double -> Double -> Double cylinder r h = let sideArea = 2 * pi * r * h topArea = pi * r ^ 2 in sideArea + 2 * topArea
Main> cylinder 3.0 4.0 131.94689145077132 Main> 4 * (let a = 9 in a + 1) + 2 42 # ローカルスコープに関数を作ることも出来る Main> [let square x = x * x in (square 5, square 3, square 2)] [(25,9,4)] # 複数の変数を1行で束縛するときは ; を使う Main> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar) (6000000,"Hey there!") # タプルを分解するのにもletは使え、式全体が値 a + b + cを有する Main> (let (a, b, c) = (1, 2, 3) in a+b+c) * 100 600
--リスト内包表記でもletは使える。ここでは、フィルタ内で、名前を束縛している。 --ここでいう (w, h) <- xs はジェネレータと呼ばれ、変数bmiはジェネレータからは参照できない --理由はletの束縛よりも前に定義されているから calcBmis' :: [(Double, Double)] -> [Double] calcBmis' xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2] calcBmis'' :: [(Double, Double)] -> [Double] calcBmis'' xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi > 25.0]
Main> calcBmis' [(3, 2)] [0.75] Main> calcBmis'' [(3, 2)] [] Main> calcBmis'' [(100, 50)] [] Main> calcBmis'' [(100, 2)] [100.0] Main> calcBmis'' [(100, 1.25)] [64.0]
# なお、letのinは省略できる。こうすると、後のセッションでもletで束縛した変数を後で使える Main> let zoot x y z = x * y * z Main> zoot 3 9 2 54 Main> let boot x y z = x * y + z in boot 3 4 2 14 # in部分が省略されていないlet式は、それ自体が値を表す式である。 Main> boot <interactive>:131:1: Not in scope: `boot' Perhaps you meant `zoot' (line 128)