1. ホーム
  2. haskell

[解決済み] 型チェッカーは非常に間違った型置換を許可しているが、プログラムはまだコンパイルできる

2023-01-01 01:39:45

質問

私のプログラムの問題 (半径が等しい 2 つの円が異なる大きさで描画される) をデバッグしようとしているときに、Gloss * を使用して、半径が等しい 2 つの円が異なるサイズで描画される) をデバッグしようとしているときに、奇妙な状況に出くわしました。オブジェクトを処理するファイルで、次のように Player :

type Coord = (Float,Float)
data Obj =  Player  { oPos :: Coord, oDims :: Coord }

で、Objects.hsをインポートした私のメインファイルでは、次のような定義をしています。

startPlayer :: Obj
startPlayer = Player (0,0) 10

これは、私がプレーヤーのフィールドを追加したり変更したりしたために、更新を忘れてしまったために起こりました。 startPlayer を更新し忘れたことが原因です (その寸法は半径を表す単一の数値で決定されましたが、私はそれを Coord に変更しました(プレイヤーオブジェクトを非円形にする場合に備えて、(width,height)を表すために)。

驚くべきことは、2番目のフィールドが間違った型であるにもかかわらず、上記のコードはコンパイルされ、実行されるということです。

私は最初、ファイルの異なるバージョンを開いていたのかもしれないと思いましたが、どのファイルへの変更もコンパイルされたプログラムに反映されました。

次に私は、もしかしたら startPlayer は何らかの理由で使われていないのではと思いました。コメントアウトすると startPlayer をコメントアウトするとコンパイラーエラーが発生しますし、さらに奇妙なことに 10startPlayer の開始サイズを変更する)適切なレスポンスを返します。 Player の開始サイズを変更します); また、間違ったタイプであるにもかかわらず。データ定義が正しく読み込まれていることを確認するために、ファイルにタイプミスを挿入すると、エラーが発生しました。

上の 2 つのスニペットをそれぞれのファイルに貼り付けてみたところ、2 番目のフィールドである PlayerstartPlayer は正しくありません。

何がこのようなことを可能にするのでしょうか?Haskellのタイプチェッカーがまさにこれを防ぐべきものだと思うでしょう。


* 私の最初の問題、すなわち半径が等しいはずの2つの円が異なる大きさに描かれているという問題の答えは、半径の1つが実際には負であるということでした。

どのように解決するのですか?

これがコンパイルできる可能性があるのは Num (Float,Float) インスタンスが存在する場合です。これは標準ライブラリでは提供されていませんが、あなたが使っているライブラリのいずれかが、何らかの非常識な理由でこれを追加している可能性があります。ghci でプロジェクトをロードして、もし 10 :: (Float,Float) が動作するかどうか確認し、次に :i Num を試してみて、そのインスタンスがどこから来ているのかを見つけて、それを定義した人を怒鳴りつけてください。

追記: インスタンスをオフにする方法はありません。また ではない モジュールからエクスポートしないようにする方法もありません。もしこれが可能であれば、さらに より のコードになります。ここでの唯一の本当の解決策は、そのようなインスタンスを定義しないことです。