如何在不导致Haskell重新编译的情况下更改数据类型?

时间:2012-02-25 19:09:31

标签: haskell

在观看了talk by Bret Victor的视频后,我受到启发,写了一个与他在演讲中演示的开发环境有点相似的快速黑客。

基本上这个想法是,一个人在一个窗口中运行应用程序,每当一个人在源文件中保存更改时程序就会改变。

这适用于小的更改,除非我无法在不关闭应用程序和重新编译的情况下更改代码中的状态类型。

如何解决表达式问题并使我的状态的数据类型能够更改 没有导致重新编译?

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

1 个答案:

答案 0 :(得分:2)

我很确定在GHC中是不可能的。编译Haskell时,更高级别的语言被篡改为Core,也是键入的。在对程序进行类型检查之前,GHC不会启动向Core的转换。这也是有原因的:因为程序类型检查它同时证明了自己。正如jberryman指出的那样,唯一的解决方法是为State设置一个允许多态的灵活类型,因此类型更改可能不会注册为一个。