6

私は Haskell の初心者で、奇妙な質問があります。今までは順調で、プレリュードの読み込み機能も普通に使えていました。今突然、それを使用するために、その型を常に宣言する必要があります。

これを使用するには、常にこれまたは類似のものを宣言する必要があります。

let r = read::String-> Int

誤って読み取りをオーバーロードしたと思ってghciを再起動しようとしましたが、通常どおり使用しようとするたびに

read "456"

次のエラーが表示されます

No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
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
In the expression: read "456"
In an equation for `it': it = read "456"

これを引き起こしている可能性のあるものと、それを修正する方法を知っている人はいますか?

4

2 に答える 2

12

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に存在します。そのインスタンスは、どのように機能するかを定義します。残念ながら、インスタンスはかなり鈍感で、他の関数やその他のものに依存していますが、完全を期すために、次のようにします。readaIntRead Intread

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動作します !IntDouble

Haskell 言語は、通常、コンパイラが値の一意の型を推測しようとするか失敗することを保証します。これにはいくつかの奇妙な例外があり、GHC の言語にはいくつかの拡張機能があります。から期待する型を何らかの方法で指定しない場合、コンパイラは任意のread型を推測できますが、それは有効ではない可能性があります。以前は. _DoubleIntegerInt

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 つの問題は、モノモーフィズムの制限です。これは、率直に言って、奇妙で風変わりなものであり、ここで詳しく説明する価値はありません。

とにかく、readIntGHCi で上記を定義するための構文は単純ですが、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

于 2013-07-17T21:25:39.660 に答える
9

入力するとき

read "456"

プロンプトで、ghci には、必要なタイプを見つけるための情報がありません。Int、 、 、 、Bool()欲しいですかString?

あなたはそれを言う必要があります、

read "456" :: Int

それが分かるように。

実際のプログラムでは、通常、必要な型を決定するコンテキストがあり、それを推論することができ、手動で型を指定する必要はありません。プロンプトでは、推論に役立つコンテキストはありません。

于 2013-07-17T21:06:38.610 に答える