read
まず、問題を解決するために、いくつかの方法のいずれかで期待する結果を指定できます。
まず、計算全体の結果の型を指定します。
read "456" :: Int
または、読み取り関数のタイプを指定することによって:
(read :: String -> Int) "456"
これが発生する理由を説明するとread
、結果の型が多態的であるという問題があります。つまり、多くの異なるread
関数が定義されており、それらは typeclass の一部として定義されていますRead
。エラーは、その理由を説明します:
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Read () -- Defined in `GHC.Read'
instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
instance (Read a, Read b, Read c) => Read (a, b, c)
-- Defined in `GHC.Read'
...plus 25 others
Read
型クラスは非常に多くの型に対して定義されており、エラーには 28 個のインスタンスが定義されていると表示されています。これは、Int、Integer、Double、String など、無数に定義されています。そのため、コンパイラが型クラスのどのインスタンスを使用するかを知るためには、指定した型からそれを推測できる必要がありread
ます。を指定する:: String -> Int
と、コンパイラはそれを理解できます。
これがどのように機能するかを理解するために、 の型クラスの一部を見てみましょうRead
:
class Read a where
read :: String -> a
したがって、特定の typeについて、関数が type の値を返すためにa
は、 のインスタンスをRead a
定義する必要があります。したがって、 には のインスタンスの定義があり、実際にGHC-Readに存在します。そのインスタンスは、どのように機能するかを定義します。残念ながら、インスタンスはかなり鈍感で、他の関数やその他のものに依存していますが、完全を期すために、次のようにします。read
a
Int
Read Int
read
instance Read Int where
readPrec = readNumber convertInt
readListPrec = readListPrecDefault
readList = readListDefault
しかし、理解すべき重要なことは、このインスタンスは で機能することを意味するというread
ことですInt
。しかし、その直後に、次のインスタンスがあります。
instance Read Double where
readPrec = readNumber convertFrac
readListPrec = readListPrecDefault
readList = readListDefault
まあ。と のためにread
動作します !Int
Double
Haskell 言語は、通常、コンパイラが値の一意の型を推測しようとするか失敗することを保証します。これにはいくつかの奇妙な例外があり、GHC の言語にはいくつかの拡張機能があります。から期待する型を何らかの方法で指定しない場合、コンパイラは任意のread
型を推測できますが、それは有効ではない可能性があります。以前は. _Double
Integer
Int
Read
この場合、型を指定しないと、コンパイラーは断念するだけで、「どのインスタンスを使用しますか?」という質問に明確に答えることができませんでした。また、Haskell コンパイラは推測を好みません。推測を防ぐ 1 つの方法は、後で明確な方法で値を使用するか、明確に使用する関数を定義することread
です。たとえば、この関数をコードで使用すると、次のように入力する必要がなくなります:: String -> Int
。
readInt :: String -> Int
readInt = read
コンパイラはRead
、そこで使用する必要があるインスタンスを特定できます。
readInt "456"
コンパイラreadInt
は type を持っていることを認識しString -> Int
、したがってreadInt "456"
type を持っていInt
ます。Haskell が好むように、明確です。
補遺
GHCi の構文は定義に関して少し異なるため、GHCi をもっぱら Haskell をいじるために使用している場合は、.hs ファイルをロードし、:r
コマンドを使用してそれらをリロードするように切り替えることをお勧めします。GHCi の問題点の 1 つは、すべてを「let」で定義する必要があることです。プログラムを書き始めると、トップレベルの定義ではそれが不要な場合に奇妙に感じられます。もう 1 つの問題は、モノモーフィズムの制限です。これは、率直に言って、奇妙で風変わりなものであり、ここで詳しく説明する価値はありません。
とにかく、readInt
GHCi で上記を定義するための構文は単純ですが、2 つの方法 (同等) があります。あなたはすでに知っていますが、それは次のようにすることです:
let readInt = read :: String -> Int
もう 1 つの方法は、関数とその型を別々に定義することです。これは、通常の .hs ファイルで行う方法に似ています。
let readInt :: String -> Int; readInt = read
どちらも慣用的な Haskell ではありませんが、それは GHCi がコードの記述に風変わりな制限を課し、複数行の入力が奇妙であるためです。あなたはこれを行うことができます:
:{
let readInt :: String -> Int;
readInt = read
:}
これにより、慣用的な Haskell の定義に近づくことができます。:load
ただし、最初の例を .hs ファイルに入れて .hs ファイルを使用または指定すると、GHCi は正しくコンパイルしますghci ./somefile.hs
。