13

このバグのあるコードを実行すると...

data Person = Adult { pName :: String}
            | Kid   { pName :: String
                    , pAge  :: Int
                    } deriving Show

getAge :: Person -> Int
getAge p = pAge p

getName :: Person -> String
getName p = pName p

main :: IO ()
main = do

  let p1 = Kid "fred" 5
      p2 = Adult "john"
      ps = [p1, p2]

  names = map getName ps
  ages = map getAge ps

  putStrLn $ "names: " ++ show names
  putStrLn $ "ages: " ++ show ages

...これをghciで取得します:

names: ["fred","john"]

ages: [5,* * * Exception: No match in record selector pAge

このエラーを回避する方法は知っていますが、「ghc -Wall」を使用してコンパイルしても、この問題について警告が表示されなかったのはなぜでしょうか。この種のエラーを防ぐのに役立つ別のツールはありますか?

4

3 に答える 3

8

この種のエラーを防ぐのに役立つ [a] ツールはありますか?

いいえ、しかしあるかもしれません。

ご存知のように、レコード構文は、定義した属性と同じ名前の getter を自動的に生成します。したがって、コード

data Person = Adult { pName :: String}
            | Kid   { pName :: String
                    , pAge  :: Int
                    } deriving Show

pName :: Person -> String関数とを作成しますpAge :: Person -> Int。ここで、Haskellにサブタイプがあるとします。もしそうならKid、のサブタイプであるPerson可能pAge性があり、より適切なタイプを持つ可能性がありますKid -> String。ただし、HaskellにはサブタイプがないKidため、型はありません。

がに与えるPerson -> Stringことができる最も具体的な型であるとすると、コンパイル時に が部分関数であるとpAge警告しないのはなぜでしょうか? pAgeリストの例を参照して、質問をそらしましょう

data List a = Cons { head :: a, tail :: List a } | Empty

この例では、headtailは部分関数です。空でないリストの 2 つのコンポーネントですが、(Haskell にはサブタイプがないため) 空リストの意味のないアクセサーです。では、なぜデフォルトで警告が出ないのでしょうか? デフォルトでは、自分が書いたコードはわかっています。を使用しても、コンパイラは警告を表示しませんunsafePerformIO。これは、あなたがここのプログラマーであるため、責任を持ってそのようなものを使用することが期待されているためです。

だからtl;dr : ここで警告が必要な場合:

getAge :: Person -> Int
getAge p = pAge p

型システムには、これが問題であると推測するのに十分な情報がないためです。

ここで警告が必要な場合:

data Person = Adult | Kid { pAge :: Int }

特定のフィールドが一部のコンストラクターに存在し、他のコンストラクターには存在しないことを確認するだけです。しかし、この警告がすべての人にとって広く役立つとは考えていません。ただのノイズだと文句を言う人もいるかもしれません。

于 2012-05-01T07:50:52.927 に答える
2

http://community.haskell.org/~ndm/catch/がこれを拾わないとしたら、私は驚くでしょう。

于 2012-05-01T11:15:10.150 に答える