6

私は現在、テキストボックスからユーザー入力を受け取り、System.Pluginsライブラリを使用してコンパイルおよびロードし、画面に描画する画像を抽出するhaskellプログラムに取り組んでいます。ユーザーはテキストボックスのコードを編集し、コンパイルボタンをクリックして新しい画像を再読み込みできます。コンパイルボタンがクリックされたときに起動されるコードは次のとおりです。

compileText :: SourceView -> SOE.Window -> IO ()
compileText tview w = do 
    txtBuff <- textViewGetBuffer tview
    startIt <- textBufferGetStartIter txtBuff
    endIt <- textBufferGetEndIter txtBuff
    compTime <- getClockTime
    srcString <- textBufferGetByteString txtBuff startIt endIt False

    BS.writeFile "Test.hs" srcString
    mkStat <- make "Test.hs" []
    case mkStat of
        MakeSuccess cd fp -> print fp
        MakeFailure (er1:er2:errs) -> error er2

    loadResult <- getModule
    case loadResult of
        Right (md, pic) -> do
                runGraphics $ do
                    draw3 "gtk test" pic w
                unload md
        Left errors -> print errors
    return ()

getModule :: IO (Either [String] (Module, Picture))
getModule = do 
               mv <- load "Test.o" ["."] [] "pic"
               case mv of
                    LoadFailure messages -> return (Left messages)
                    LoadSuccess x y -> return (Right (x, y))

そして、ユーザーがテキストボックスに入力したコード例を次に示します。

module Test where
    import Picture

    r1,r2,r3,r4 :: Region
    r1 = Shape(Rectangle 2 1)
    r2 = Shape(Ellipse 2 1.5)
    r3 = Shape(RtTriangle 3 2)
    r4 = Shape(Polygon [(-2.5, 2.5), (-3.0,0), (-1.7,-1.0), (-1.1,0.2),(-1.5,2.0)])

    p1,p2,p3,p4 :: Picture
    p1 = Region Red r1
    p2 = Region Green r2
    p3 = Region Blue r3
    p4 = Region Yellow r4

    pics :: Picture
    pics = foldl Over EmptyPic [p1,p2,p3,p4]

ユーザーが毎回正しくコンパイルおよびロードするコードを記述していれば、これはすべて意図したとおりに機能します。ただし、ユーザーがロードに失敗するコードを記述した場合(私が遊んでいる例では、ロードするpic関数が見つからないように「pic」を「pics」に変更しています)意図された動作は、プログラムが出力することです。画面へのロードエラー。これにより、ユーザーはおそらくコードを修正して、コンパイルボタンをもう一度クリックしてみることができます。

ただし、実際に発生するのは、プログラムでLoadFailureが1回発生すると、コードが正しいかどうかに関係なく、コンパイルボタンをクリックしようとすると、ロード失敗メッセージが表示されることです。

ここで何が起こっているのかはよくわかりませんが、プログラムが評価から評価まで前の結果をある程度記憶しているように見えます。探している動作を取得するにはどうすればよいですか?

編集:私はgtkを使用せずに私が抱えている問題を説明する小さなテストケースを書くことによって問題を切り分けようとしました

import Control.Monad
import System.Time
import System.IO
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC

import System.Plugins.Make
import System.Plugins.Load
import System.Eval.Haskell

testCaseCorrect :: String
testCaseCorrect = "module Test where\n printGreeting :: String -> IO ()\n printGreeting greeting = print greeting"

-- This should cause load to fail as it will not be able to find the
-- printGreeting function
testCaseIncorrect :: String
testCaseIncorrect = "module Test where\n printGurting :: String -> IO ()\n printGurting greeting = print greeting"

main :: IO ()
main = do
    BS.writeFile "Test.hs" (BSC.pack testCaseCorrect)
    mkStat <- make "Test.hs" []

    case mkStat of
        MakeSuccess cd fp -> print fp
        MakeFailure (er1:er2:errs) -> error er2

    loadResult <- getModule
    case loadResult of
        Right (md, greeter) -> do
                greeter "Hi there"
                unload md
        Left errors -> print errors

    BS.writeFile "Test.hs" (BSC.pack testCaseIncorrect)

    mkStat2 <- make "Test.hs" []

    case mkStat2 of
        MakeSuccess cd fp -> print fp
        MakeFailure (er1:er2:errs) -> error er2

    loadResult2 <- getModule
    case loadResult2 of
        Right (md, greeter) -> do
                greeter "Hi there"
                unload md
        Left errors -> print errors


    BS.writeFile "Test.hs" (BSC.pack testCaseCorrect)
    mkStat3 <- make "Test.hs" []

    case mkStat3 of
        MakeSuccess cd fp -> print fp
        MakeFailure (er1:er2:errs) -> error er2

    loadResult3 <- getModule
    case loadResult3 of
        Right (md, greeter) -> do
                greeter "Hi there"
                unload md
        Left errors -> print errors

getModule :: IO (Either [String] (Module, String -> IO()))
getModule = do 
               mv <- load "Test.o" ["."] [] "printGreeting"
               case mv of
                    LoadFailure messages -> return (Left messages)
                    LoadSuccess x y -> return (Right (x, y))

このコードは結果を生成します:

"Test.o"
"Hi there"
"Test.o"
["load: couldn't find symbol <<printGreeting>>"]
"Test.o"
["load: couldn't find symbol <<printGreeting>>"]

つまり、エラーを再現することができます

編集2:これとまったく同じコードを実行すると、次のような出力も生成されます。

"Test.o"
"Hi there"
"Test.o"
"Hi there"
"Test.o"
"Hi there"

しかし、これは、連続するコンパイルが非常に高速に実行されるためである可能性があります。

4

1 に答える 1

1

プラグイン ライブラリの更新バージョンを使用して問題を再現し、3 つの原因を特定しました。

まず、モジュールを再コンパイルする必要があるかどうかを確認するために使用されるgetModificationTime 関数の精度 (秒) が不十分です。

第二に、GHC は同じ過ちを犯しているようです。

第三に、Don Stewart が言ったように、モジュールをアンロードする必要がありますが、これは API がモジュールへの直接参照を提供しないため、簡単には実行できません。

シンボル検索が失敗したときにモジュールを自動的にアンロードすることで、リポジトリの 3 番目の問題を修正しました。他の 2 つを修正する正しい方法は、おそらくアップストリームにパッチを当てることです。

于 2013-08-31T15:08:36.900 に答える