2012-02-25 14 views
10

talk by Bret Victorのビデオを見た後、私はトークでデモンストレーションした開発環境とやや似ている簡単なハックを書くように奮起しました。Haskellで再コンパイルせずにデータ型を変更するにはどうすればよいですか?

基本的には、1つのウィンドウでアプリケーションが実行され、ソースファイルに変更が保存されるたびにプログラムが変更されます。

これは、アプリケーションをシャットダウンして再コンパイルすることなく、コード内の状態のタイプを変更できないという点を除いて、小さな変更に最適です。

式の問題を解決し、自分の状態のデータ型を再コンパイルせずに を変更できるようにするにはどうすればよいですか?

P.S. コードは次のとおりです。 私は元々は投稿したくありませんでした。なぜなら、それは本当に面倒で、すぐに一緒にハッキングしたからです。しかし、人々はそれを得るためにそれを望んでいました。

最初にディスプレイとアイドルモジュール(これはすばやくハックされたので、実際のモジュールとしてそれらを実行する方法は分かりませんでした)。

Idle.hs

\state -> do 
    counter <- readIORef state 
    writeIORef state ((counter + 1)`mod`3) 
    postRedisplay Nothing 

Display.hs

\state -> let 
cube w = do 
    renderPrimitive Quads $ do 
     vertex $ Vertex3 w w w 
     vertex $ Vertex3 w w (-w) 
     vertex $ Vertex3 w (-w) (-w) 
     vertex $ Vertex3 w (-w) w 
     vertex $ Vertex3 w w w 
     vertex $ Vertex3 w w (-w) 
     vertex $ Vertex3 (-w) w (-w) 
     vertex $ Vertex3 (-w) w w 
     vertex $ Vertex3 w w w 
     vertex $ Vertex3 w (-w) w 
     vertex $ Vertex3 (-w) (-w) w 
     vertex $ Vertex3 (-w) w w 
     vertex $ Vertex3 (-w) w w 
     vertex $ Vertex3 (-w) w (-w) 
     vertex $ Vertex3 (-w) (-w) (-w) 
     vertex $ Vertex3 (-w) (-w) w 
     vertex $ Vertex3 w (-w) w 
     vertex $ Vertex3 w (-w) (-w) 
     vertex $ Vertex3 (-w) (-w) (-w) 
     vertex $ Vertex3 (-w) (-w) w 
     vertex $ Vertex3 w w (-w) 
     vertex $ Vertex3 w (-w) (-w) 
     vertex $ Vertex3 (-w) (-w) (-w) 
     vertex $ Vertex3 (-w) w (-w) 

points :: Integer -> [(GLfloat,GLfloat,GLfloat)] 
points n' = let n = fromIntegral n' in map (\k -> let t = 2*pi*k/n in (sin(t),cos(t),0.0)) [1..n] 

in do 
    clear [ ColorBuffer ] 
    counter <- readIORef state 
    mapM_ (\(x,y,z) -> preservingMatrix $ do 
      color $ Color3 ((x+1.0)/2.0) ((y+1.0)/2.0) ((z+1.0)/2.0) 
      translate $ Vector3 x y z 
      cube (0.3::GLfloat) 
      ) $ points (9 + counter) 
    flush 

メインモジュール

module Main where 

import Control.Monad 
import Data.Typeable as Typeable 

import System.IO 

import Data.IORef 

import Graphics.Rendering.OpenGL 
import Graphics.UI.GLUT 

import Language.Haskell.Interpreter 

main :: IO() 
main = do 
    (_, _) <- getArgsAndInitialize 
    createWindow "Hello World" 

    action <- newIORef $ do 
    clear [ ColorBuffer ] 
    flush 

    let imports = ["Prelude", "Data.IORef", "Graphics.Rendering.OpenGL", "Graphics.UI.GLUT"] 
    let modules = ["State"] 

    runFile (undefined :: IORef Integer -> IO()) "Display.hs" imports $ \displayCode -> 
    runFile (undefined :: IORef Integer -> IO()) "Idle.hs" imports $ \idleCode -> do 

    state <- newIORef 12 

    displayCallback $= display displayCode state 
    idleCallback $= Just (idle displayCode idleCode state) 

    mainLoop 

display displayCode state = do 
    f <- execute displayCode 
    f state 

idle displayCode idleCode state = do 
    update displayCode 
    update idleCode 

    f <- execute idleCode 
    f state 

instance Eq GhcError where 
    GhcError s == GhcError t = s == t 

instance Eq InterpreterError where 
    UnknownError s == UnknownError t = s == t 
    WontCompile s == WontCompile t = s == t 
    NotAllowed s == NotAllowed t = s == t 
    GhcException s == GhcException t = s == t 

data V a = V { 
    update :: IO(), 
    execute :: IO a 
} 

runFile :: Typeable a => a -> String -> [String] -> (V a -> IO()) -> IO() 
runFile theType file imports f = do 
    currentError <- newIORef Nothing 
    currentAction <- newIORef Nothing 

    let v = V { 
     update = do 
      fileContents <- readFile file 

      result <- runInterpreter $ do 
       setImports imports 
       interpret fileContents theType 

       oldError <- readIORef currentError 

       case result of 
       Right newAction -> do 
        when (oldError /= Nothing) $ do 
         writeIORef currentError Nothing 
         putStrLn (file ++ " Ok!") 

         writeIORef currentAction (Just newAction) 

         Left newError -> do 

          when ((Just newError) /= oldError) $ do 
           writeIORef currentError (Just newError) 
           print newError 
           , execute = do 
            action <- readIORef currentAction 
            case action of 
            Nothing -> do 
             err <- readIORef currentError 
             return (error (show err)) 
             Just act -> return act 
             } 

    update v 

    f v 
+3

+1はBret Victorの話に従っています。あなたのコードをどこかに投稿することができれば幸いです。静的に型チェックされた言語は、このような環境にはあまり適していないと思います。静的型を主張する場合は、ランタイム(少なくともデバッグ開発ランタイム)は静的型を破棄し、動的型で処理する必要があります。私はこのようなランタイムがHaskellに存在するかどうかはわかりません。 –

+1

@ user990666あなたはトークへのリンクを投稿できますか? –

+2

@Matt Fenwick http://vimeo.com/36579366 –

答えて

2

私はそれがGHCでは不可能であるかなり確信しています。ハスケルがコンパイルされると、より高いレベルの言語がCoreに引き渡され、これもタイプされます。 GHCは、プログラムの型チェックを行うまで、Coreへの変換を開始しません。これには理由もあります:プログラムのタイプチェックが同時にそれ自身を証明するためです。 jberrymanが指摘したように、多形性を可能にする柔軟なタイプのStateを使用することが唯一の回避策であるため、タイプの変更は1つとして登録されない可能性があります。

関連する問題