分かりやすい例としてファイルへのごく簡単なアクセスを使って質問しますが、
本質的にはファイルアクセスに限らないテクニック(パターン)に関する質問だと思います。

f n = do
 (a, b) <- withFile "example" ReadMode $ \h -> do
  x <- getLine
  y <- getLine
  return (x, y)
 case n of
  1 -> a のみを使って何かする
  2 -> b のみを使って何かする
  3 -> a と b の両方を使って何かする

このような関数 f を 3 に適用した時は 2 度の getLine には意味があるので問題ありません。
しかし 1 や 2 に適用した時、本来はそれぞれ 1 度の getLine で済むはずが、
どちらも 2 度 getLine を行ってしまいます。
この無駄を省き、1 度しか getLine を行わないようにしたいです。

ここで、下記のような解決方法は取りたくありません。

f n = withFile "example" ReadMode $ \h -> do case n of
  1 -> アクセスX して何かする
  2 -> アクセスY して何かする
  3 -> アクセス(X,Y) して何かする

初めの例はファイルアクセスと結果の利用が明確に分かれており、スッキリしていますが、
後の例はファイルアクセスと結果の利用が交互に書かれていて、ごちゃごちゃしているからです。

また、初めの例で a と b の両方を使って何かする際に、ファイルの開閉は一度で済ませたいです。

このような2つの条件を満たすような解決策を探しています。
何かアイデアはないでしょうか。