17

次の定義が与えられた場合:

import Control.Monad.ST
import Data.STRef

fourty_two = do
  x <- newSTRef (42::Int)
  readSTRef x

以下は GHC でコンパイルされます:

main = (print . runST) fourty_two -- (1)

しかし、これはしません:

main = (print . runST) $ fourty_two -- (2)

しかし、bdonlanがコメントで指摘しているように、これはコンパイルされます。

main = ((print . runST) $) fourty_two -- (3)

しかし、これはコンパイルされません

main = (($) (print . runST)) fourty_two -- (4)

(3) infix の特別な処理のためにのみコンパイルされることを示しているようですが$、(1) がコンパイルされる理由はまだ説明されていません。

質問:

1) 次の 2 つの質問 ( firstsecond )を読みましたが$、モノモーフィック型でのみインスタンス化できると信じるようになりました。しかし、同様.にモノモーフィック型でのみインスタンス化できると仮定し、その結果、同様に失敗します。最初のコードは成功するのに、2 番目のコードは成功しないのはなぜですか? (たとえば、GHC が最初のケースに対して持っていて、2 番目のケースには適用できない特別なルールはありますか?)

2) 2 番目のコードをコンパイルする現在の GHC 拡張機能はありますか? (おそらくImpredicativePolymorphismはある時点でこれを行いましたが、廃止されたようです。何かに取って代わりましたか?)

`my_dollar`3) GHC拡張機能を使用して何をするかを定義する方法はあります$が、ポリモーフィック型も処理できるため、(print . runST) `my_dollar` fourty_twoコンパイルできますか?

編集:提案された回答:

また、以下はコンパイルに失敗します。

main = ((.) print runST) fourty_two -- (5)

これは (1) と同じですが、 の中置バージョンを使用しない点が異なり.ます。

その結果、GHC は と の両方に特別なルールを持っているようです$.、それらの中置バージョンだけです。

4

2 に答える 2

6
  1. 2番目が機能しない理由がわかりません。の型を見て、print . runSTそれが十分にポリモーフィックであることを観察できるので、責任は にありません(.)。私は、GHC が infix に対して持っている特別な規則($)は、まったく十分ではないのではないかと考えています。SPJ とその友人は、トラッカーのバグとしてこのフラグメントを提案した場合、再検討する可能性があります。

    3 番目の例が機能する理由については、まあ、これも の型((print . runST) $)が十分に多態的だからです。実際、それは の型と同じですprint . runST

  2. に取って代わるものはImpredicativePolymorphismありません. GHC関係者は、コンパイラのバグの可能性よりもプログラマの利便性の方が勝るユースケースを見たことがないからです. (もちろん、私は権威ではありませんが、彼らもこれを説得力のあるものと見なすとは思いません。)
  3. 少しポリモーフィック性が低い を定義できます($$)

    {-# LANGUAGE RankNTypes #-}
    infixl 0 $$
    ($$) :: ((forall s. f s a) -> b) -> ((forall s. f s a) -> b)
    f $$ x = f x
    

    次に、あなたの例は、この新しい演算子で問題なく型チェックします:

    *Main> (print . runST) $$ fourty_two
    42
    
于 2012-04-27T18:32:05.500 に答える
0

私はこの主題についてあまり多くの権威を持って言うことはできませんが、これが起こっているかもしれないと私が思うことです:

これらの各ケースでタイプチェッカーが何をしなければならないかを考えてください。(print . runST)タイプがありShow b => (forall s. ST s t) -> IO ()ます。fourty_twoタイプがありST x Intます。

ここforallに存在型修飾子があります-ここでは、渡される引数がでユニバーサルでなければならないことを意味しsます。つまり、すべての値をサポートするポリモーフィック型を渡す必要がありますs。明示的に述べていない場合forall、Haskellはそれを型定義の最も外側のレベルに置きます。これはfourty_two :: forall x. ST x Int(print . runST) :: forall t. Show t => (forall s. ST s t) -> IO ()

これで、を許可することで一致forall x. ST x Intforall s. ST s tせることができt = Int, x = sます。したがって、直接呼び出しの場合は機能します。しかし、使用するとどうなり$ますか?

$タイプがあり($) :: forall a b. (a -> b) -> a -> bます。を解決するab、の型に$はこのような明示的な型スコープがないため、のx引数は-fourty_twoの型の最も外側のスコープに持ち上げられます。この時点で、とを一致させようとして失敗します。($)($) :: forall x t. (a = forall s. ST s t -> b = IO ()) -> (a = ST x t) -> IO ()ab

代わりにを書く場合((print . runST) $) fourty_two、コンパイラは最初にタイプを解決し((print . runST $)ます。($)のタイプをforall t. (a = forall s. ST s t -> b = IO ()) -> a -> b;に解決します。の2番目の出現にaは制約がないため、その厄介な型変数が最も外側のスコープにリークすることはありません。そして、一致が成功し、関数が部分的に適用され、式の全体的なタイプは、forall t. (forall s. ST s t) -> IO ()開始した場所に戻って成功します。

于 2012-04-27T06:46:40.967 に答える