2012-02-25 34 views
10

看了一段talk by Bret Victor的視頻之後,我的靈感來源於寫了一個與他在演講中展示的開發環境有點相似的快速入侵。如何在不導致Haskell重新編譯的情況下更改我的數據類型?

基本上這個想法是,有一個應用程序在一個窗口中運行,並且每當程序發生變化時在源文件中保存更改。

這對於小的更改非常適用,但我無法在不關閉應用程序和重新編譯的情況下更改代碼中的狀態類型。

如何解決表達式問題並讓我的狀態的數據類型能夠在不導致重新編譯的情況下更改 ?

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這樣的運行時存在。 – 2012-02-25 19:31:16

+1

@ user990666你可以發佈鏈接到談話嗎? – 2012-02-25 20:10:14

+2

@Matt Fenwick http://vimeo.com/36579366 – 2012-02-25 20:51:10

回答

2

我敢肯定它在GHC是不可能的。當Haskell被編譯時,更高級的語言被解析到Core中,這也是鍵入的。直到程序輸入檢查完成後,GHC纔會開始轉換爲Core。這也是有原因的:因爲程序類型同時檢查它證明了它自己。正如jberryman指出的那樣,唯一的解決方法是爲State設置一個靈活的類型,這將允許多態性,所以類型更改可能不會註冊爲一個類型。

相關問題