let, where, let-in

https://haskell.e-bigmoon.com/posts/2019/08-24-let-where-letin.html

let を使うタイミング (do の中)

f :: Maybe Int
f = do
  x <- return 1
  let y = x + 1
      z = 2
  return (y+z)
  • do の中で束縛された変数 x を使って新しい変数 y を宣言したい時

  • letdo とリスト内包表記でしか使えない

    • do の変換: do {let decls; stmts} = let decls in do {stmts}

    • リスト内包表記の変換: [ e | let decls, Q ] = let decls in [ e | Q ]

  • where では x を宣言できない

  • let-in を使うとインデントがとても面倒

    • letin の先頭を合わせるとコンパイルエラーになる

  • 他の変数に依存していない z はどの書き方でも良いが、この場合、普通は let で書くことが多いと思う

    • do が巨大だと where で宣言されている変数までの距離が遠いため、可読性が落ちる

where を使うタイミング (ガード)

calcBmi :: Double -> Double -> String
calcBmi cm kg
  | bmi <= 18.5 = "痩せてるね"
  | bmi <= 25.0 = "普通だね"
  | bmi <= 30.0 = "ぽっちゃりだね"
  | otherwise = "太っているね"
  where
    bmi = kg / (m^2)
    m = cm / 100
    
-- *Main> putStrLn $ calcBmi 170 60
-- 普通だね
  • このように、ガードで共通する変数を宣言する場合に where を使うと見通しが良くなります。

  • もし let-in を使って同じように書く場合は MultiWayIf を使うことになるでしょう。

  • do の中ではないため、当然 let は使えません

let-in を使うタイミング

-- Programming in Haskell 2nd Edition より引用
type State = Int
newtype ST a = S (State -> (a, State))

app :: ST a -> State -> (a, State)
app (S st) x = st x

instance Functor ST where
  -- fmap :: (a -> b) -> ST a -> ST b
  fmap g st = S (\s -> let (x,s') = app st s in (g x, s'))
  • こんな感じの関数を書くときにはとても便利です

  • ただ、このようなケースというのはあまり遭遇しないので、個人的には使わないようにしています。無くてもほぼ困らないです

    • 理由1: レイアウトルールを気にしなければならない点がとても面倒 (1行なら問題無い)

    • 理由2: ポイントフリースタイルで書くのが好きなので

  • where で書くと、let-in で書いたときよりも可読性が落ちてしまったように思います

where と let-in の違い

let-inwhere の明確な違いは let-in が式なのに対して where が節だという点です。

式と節の違い
-- OK
f = if let x = True in x then 1 else 0

-- NG
g = if (x where x = True) then 1 else 0

構文上のどこに式が出現できるかという規則については Language Report に詳しく記載されています。

検討事項

  • wherelet-in の最適化

  • Strict 拡張

参考リソース

Last updated