2012-02-25 26 views
10

Sau khi xem video có số talk by Bret Victor, tôi đã được truyền cảm hứng để viết một bản hack nhanh tương tự như một môi trường phát triển mà ông đã trình bày trong buổi nói chuyện.Làm cách nào để thay đổi loại dữ liệu của mình mà không làm biên dịch lại trong Haskell?

Về cơ bản, ý tưởng là một ứng dụng chạy trong một cửa sổ và bất cứ khi nào một ứng dụng lưu thay đổi trong tệp nguồn chương trình sẽ thay đổi.

Điều này phù hợp với những thay đổi nhỏ, ngoại trừ việc tôi không thể thay đổi loại trạng thái trong mã của mình mà không tắt ứng dụng và biên dịch lại.

Làm cách nào để giải quyết vấn đề biểu thức và có kiểu dữ liệu của tiểu bang của tôi có thể thay đổi mà không gây ra biên dịch lại?

P.S. Đây là mã. Ban đầu tôi không muốn đăng vì nó thực sự lộn xộn và nhanh chóng bị tấn công cùng nhau, nhưng mọi người muốn nó để họ có thể nhận được nó.

Đầu tiên màn hình và mô-đun nhàn rỗi, (đây là một bản hack nhanh vì vậy tôi không tìm ra cách làm chúng như mô-đun thực).

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 

Các module chính

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 cho diễn xuất trên trò chuyện của Bret Victor. Nó sẽ là tuyệt vời nếu bạn có thể đăng mã của bạn một nơi nào đó. Tôi nghĩ rằng một ngôn ngữ đánh máy tĩnh không phải là rất thích hợp cho một môi trường như thế này. Nếu bạn nhấn mạnh vào các kiểu tĩnh, thì thời gian chạy (thời gian chạy phát triển gỡ lỗi ít nhất) nên loại bỏ các kiểu tĩnh và làm việc với các kiểu động. Tôi không chắc chắn một thời gian chạy như thế này tồn tại cho Haskell. –

+1

@ user990666 bạn có thể đăng liên kết tới bài nói chuyện không? –

+2

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

Trả lời

2

Tôi khá chắc chắn rằng nó là không thể trong GHC. Khi Haskell được biên dịch, ngôn ngữ cấp cao hơn được desugared vào Core, cũng được đánh máy. GHC sẽ không bắt đầu chuyển đổi thành Core cho đến khi chương trình đã được đánh máy kiểm tra. Có một lý do cho điều này, quá: khi loại chương trình kiểm tra nó đồng thời chứng minh bản thân. Như jberryman lưu ý, công việc duy nhất xung quanh sẽ là có một loại linh hoạt cho State mà sẽ cho phép đa hình, do đó, một sự thay đổi loại có thể không đăng ký như một.

Các vấn đề liên quan