2012-02-25 13 views
10

talk by Bret Victor'un bir videosunu izledikten sonra, konuşmasında gösterdiği bir gelişim ortamına benzeyen hızlı bir hack yazmaktan ilham aldım.Veri türlerimi Haskell'de bir yeniden derlemeye neden olmadan nasıl değiştirebilirim?

Temel olarak fikir, bir uygulamanın bir pencerede çalıştığı ve bir kaynak dosyada bir değişiklik kaydedildiği zaman programın değişmesidir.

Bu, uygulamayı kapatmadan ve yeniden derlemeye gerek kalmadan kodumdaki durumun türünü değiştirememenin dışında küçük değişiklikler için harika çalışıyor.

İfade sorununu nasıl çözebilirim ve durumumun veri türü 'u yeniden derlenmeden değiştirebilir mi?

P.S. İşte kod. Aslen postalamak istemedim çünkü gerçekten dağınık ve çabuk bir şekilde hacklenmişti, ama insanlar istediler, böylece onu alabilirler.

İlk önce ekran ve boşta kalan modül, (bu, hızlı bir şekilde kesilmişti, bu yüzden bunları gerçek modüller olarak nasıl yapacağımı anlamadım).

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 

ana modül

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. Kodunuzu bir yere gönderebilirseniz harika olur. Statik olarak yazılan bir dilin böyle bir ortam için çok uygun olmadığını düşünüyorum. Statik türlerde ısrar ederseniz, çalışma zamanı (en azından hata ayıklama geliştirme çalışma zamanı) statik türleri atmalı ve dinamik türlerle çalışmalıdır. Haskell için böyle bir çalışma zamanı olduğundan emin değilim. –

+1

@ user990666 Bağlantıyı konuşmaya gönderebilir misiniz? –

+2

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

cevap

2

Ben ghc imkansız eminim. Haskell derlendiğinde, daha yüksek düzeyli dil, aynı zamanda yazılan Core'a da desugared edilir. GHC, program yazılıncaya kadar dönüştürmeyi Çekirdek'e başlatmayacaktır. Bunun da bir nedeni var: Program tipi kontroller aynı anda kendini kanıtlıyor. Jugman'ın belirttiği gibi, tek çalışma, polimorfizme olanak tanıyan State için esnek bir tipe sahip olmaktır, bu nedenle tip değişimi tek olarak kayıt olmayabilir. Bret Victor'un konuşması için

İlgili konular