Hatena::ブログ(Diary)

じゃばらの手記

2012-01-07

警告を消す・Warning: A do-notation statement discarded a result of type <type> ...

do記法の中での警告

Yesodを書いているとdo記法をたくさん使うのですが、ときどき以下のような警告に遭遇します。

Handler/Root.hs:36:7:
    Warning: A do-notation statement discarded a result of type <型情報>.
             Suppress this warning by saying "_ <- ($)
                                                     <コード片>,
             or by using the flag -fno-warn-unused-do-bind

「do記法の中の文が結果を戻してるのに変数で受けてませんよ」という意味のようですね。

文の結果がいらない、というのはよくあることだと思うのでこの警告は少々うるさく感じます。

警告を消すには、警告文にある通り

_ <- hogehoge...

という具合に _ で受けてあげればいいのですが、数が多くなってくると面倒ですねぇ。Haskellらしいと言えばらしいのですが。

警告文が示しているもう1つの解決方法は、コンパイルオプションを追加して警告を出さなくする、というものです。

Yesodの場合、これはcabalファイルにて指定します。

ghc-options:   -Wall -threaded -O0

というところに追記して

ghc-options:   -Wall -threaded -O0 -fno-warn-unused-do-bind

とします。

なお上記に該当する行は複数あるのですが、開発中はlibraryというセクション(という呼び名でいいのかな?)の方に追記する必要があります。

どちらの解消方法がいいのか

当面は _ で受ける方法で書くことにします。

この書き方には「この文の結果は使わない!」と強く表明する効果があるからです。

数が多くなってくると面倒ですが、個人で開発している分にはあまり大したことにはならないでしょう。

規模が大きい開発では、警告があまりたくさん出ると真に大事な警告が埋もれてしまう可能性が出てくるので、そんなときはコンパイルオプションを使うといいと思います。

「そもそもたくさん警告が出る状態がどうなんだ?」という議論はありますが、現実としてそれはよくあることですので・・・

Yesodにはまってると思いきやHaskellの文法にはまっていた

よくあるっちゃーよくあるんですが、Yesodを書いていてYesodの作法を理解していなくてはまっているのかと思いきや、実はHaskellの文法にやられていたという。

今回はこんなエラーが。

Handler/Bbs.hs:7:19:
    The first argument of ($) takes one argument,
    but its type `GGWidget master0 m0 ()' has none
    In the second argument of `($)', namely
      `do { setTitle "Bbs Index" } $ (widgetFile "bbs")'
    In the expression:
        defaultLayout $ do { setTitle "Bbs Index" } $ (widgetFile "bbs")
    In the expression:
      do { defaultLayout
         $   do { setTitle "Bbs Index" } $ (widgetFile "bbs") }
Starting development server...

dist/devel.hs:3:1:
    Failed to load interface for `Application':
      it is not a module in the current program, or in any known package.
Exit code: ExitFailure 1

エラーが出ている関数がこちら。

getBbsR :: Handler RepHtml
getBbsR = do
  defaultLayout $ do
    setTitle "Bbs Index"
    $ (widgetFile "bbs")

この関数の修正版がこちら。

getBbsR :: Handler RepHtml
getBbsR = do
  defaultLayout $ do
    setTitle "Bbs Index"
    $(widgetFile "bbs")

ちがいが分かりますか?

最終行の $ の後ろに空白があるかないかのちがいなんです。

なんでこれでエラーになるのか、理解できません。Haskellで、インデントの付け方がまずくてエラーになるのはよくあるのですが、空白の有無で意味が変わるなんてのは初めて。ましてや $ の引数を () で囲んで明示しているのに。

でも理解できなくても先に進むことはできます。当面、理解は置いておいて、前進!

その後・・・

その後、このエントリの一つ前のエントリでリンクした「できる!Template Haskell (完)」にきちんと書いてありました。

合成した構文木を埋め込むには、 $( ) で囲めばよいのです。

http://haskell.g.hatena.ne.jp/mr_konn/20111218/1324220725

そうだったのか!$っていう関数を適用してるのかと思ってたから理解できなかったんだな。なっとく。

ちなみに$()ってのは関数ではなくてTemplateHaskellにおける記法のようです。