18

Haskellでは、値が弱い頭の正規形に評価されているかどうかをテストすることは可能ですか? 関数が既に存在する場合、次のような署名があると予想されます

evaluated :: a -> IO Bool

同様の機能が存在する場所がいくつかあります。

以前の回答で ghci コマンドを紹介されました。:sprintこのコマンドは、すでに弱いヘッドの通常の形式に強制されている値の部分のみを出力します。:sprint値が評価されたかどうかを観察できます。

> let l = ['a'..]
> :sprint l
l = _
> head l
'a'
> :sprint l
l = 'a' : _

IOそうでなければ立ち入り禁止のプロパティを調べることができます。たとえば、IO2 つの値が同じ宣言に由来するかどうかを比較することができます。これは の によって提供され、StableNamedata -reifySystem.Mem.StableNameで観察可能な共有の問題を解決するために有名に使用されます。関連は、参照された値が弱いヘッドの正規形であるかどうかをチェックするメカニズムを提供しません。StablePtr

4

4 に答える 4

9

最終的にはghc-prim を使用してクロージャを検査するためのghci 実装。:sprintこれをヒープ オブジェクトunpackClosure#の形式の知識と組み合わせて、クロージャが弱いヘッドの正規形まで評価されたかどうかを判断できます。

の ghci 実装によって行われた検査を再現するには、いくつかの方法があります:sprint。GHC api は で公開さgetClosureData :: DynFlags -> a -> IO ClosureRtClosureInspectます。ghc- primのみに依存するバキューム パッケージは、からのコードを再現し . たとえば、これらの表現のいずれかを調べる方法は、すぐには明らかではありません。ghc-heap-viewパッケージはクロージャを検査し、と の詳細ビューの両方を公開します。ghc-heap-view は GHC api に依存します。RtClosureInspectgetClosure :: a -> IO ClosureClosuregetClosureData :: a -> IO ClosureClosure

from ghc-heap-view の観点からevaluated書くことができます。getBoxedClosureData

import GHC.HeapView

evaluated :: a -> IO Bool
evaluated = go . asBox
    where
        go box = do
            c <- getBoxedClosureData box
            case c of
                ThunkClosure     {} -> return False
                SelectorClosure  {} -> return False
                APClosure        {} -> return False
                APStackClosure   {} -> return False
                IndClosure       {indirectee = b'} -> go b'
                BlackholeClosure {indirectee = b'} -> go b'
                _ -> return True

このブラックホール クロージャの処理は、ブラックホールが評価されている間は正しくない可能性があります。セレクタ クロージャの処理が正しくない可能性があります。AP クロージャが弱い頭の正規形ではないという仮定は、正しくない可能性があります。他のすべてのクロージャーが WHNF にあるという仮定は、ほぼ確実に正しくありません。

この例では、一方のスレッドが式を評価していることを他方のスレッドで観察するために、2 つの同時スレッドが必要です。

import Data.Char
import Control.Concurrent

unsafe評価を選択的に強制することで、何も頼ることなく、関数から横方向に情報を伝達できます。以下は、ペアの一方または他方を強制することを選択できるサンクのペアのストリームを構築します。

mkBitStream :: Integer -> [(Integer, Integer)]
mkBitStream a = (a+2, a+3) : mkBitStream (a+1)

zero最初のものをone強制し、2番目のものを強制します。

zero :: [(x, y)] -> [(x, y)]
zero ((x, _):t) = x `seq` t

one :: [(x, y)] -> [(x, y)]
one ((_, y):t) = y `seq` t

copyは、データの検査に基づいてストリーム内のビットを強制するという副作用を持つ悪のアイデンティティ関数です。

copy :: (a -> Bool) -> [(x, y)] -> [a] -> [a]
copy f bs []     = []
copy f bs (x:xs) = let bs' = if f x then one bs else zero bs
                   in bs' `seq` (x:copy f bs' xs)

readBsペアの各サンクがevaluated.

readBs :: [(x, y)] -> IO ()
readBs bs@((f, t):bs') = do
    f' <- evaluated f
    if f'
    then putStrLn "0" >> readBs bs'
    else do
        t' <- evaluated t
        if t'
        then putStrLn "1" >> readBs bs'
        else readBs bs

印刷時に強制copyすると、読み取った文字列について観察された情報が印刷されるという副作用があります。

main = do
    let bs = mkBitStream 0
    forkIO (readBs bs)
    text <- getLine
    putStrLn (copy isAlpha bs text)
    getLine

プログラムを実行して入力abc123を提供すると、各文字がisAlpha

abc123
abc123
1
1
1
0
0
0
于 2015-02-24T16:55:38.797 に答える
7

記録のための否定的な答え: のメカニズムを再利用することは現実的ではないようです。これはsprint、プリミティブなランタイム構造とは対照的に、解釈されたインタラクティブな評価に密接に結びついているためです。私はGHCの内部を見たことがありません。

GitHub の GHC ソースで「sprint」を検索することから始めました。これは、「print」コマンドと実装を共有していることが判明しましたが、Boolフラグというフラグがあり、 RtClosureInspect.cvObtainTermに到達するforceまで定義に従いました。評価者。

于 2015-02-24T03:52:17.747 に答える
1

最近提案がありました。おそらくどこかで既に実装されています https://mail.haskell.org/pipermail/libraries/2015-February/024917.html

于 2015-02-25T16:45:12.213 に答える