packages feed

atomo-0.1: src/Atomo/Kernel/Continuation.hs

{-# LANGUAGE QuasiQuotes #-}
module Atomo.Kernel.Continuation where

import Data.IORef

import Atomo.Environment
import Atomo.Haskell


load :: VM ()
load = do
    [$p|current-continuation|] =:::
        [$e|{ cc | cc } call/cc|]

    -- call/cc actually makes an object delegating to Continuation
    -- so just add the show definiton here
    [$p|Continuation show|] =:: string "<continuation>"

    [$p|(c: Continuation) yield: v|] =: do
        Continuation c <- here "c" >>= findContinuation
        v <- here "v"
        liftIO (readIORef c) >>= ($ v)

    -- this enables call/cc as well
    [$p|(c: Continuation) call: [v]|] =::: [$e|c yield: v|]

    -- effectively just "jumping" to a continuation
    [$p|(c: Continuation) yield|] =::: [$e|c yield: @ok|]
    [$p|(c: Continuation) call|] =::: [$e|c yield: @ok|]

    -- an object providing lower-level call/cc functionality
    -- only used in call/cc's definition
    callccObj <- newScope $ do
        ([$p|o|] =::) =<< eval [$e|Object clone|]
        [$p|(o) pass-to: b|] =: callCC $ \c -> do
            b <- here "b"
            cr <- liftIO (newIORef c)
            as <- list [Continuation cr]
            dispatch (keyword ["call"] [b, as])
        eval [$e|o|]

    -- define call/cc and dynamic-wind in a new scope for hiding
    -- helper methods (internal-call/cc, dynamic-winds, dynamic-unwind)
    newScope (dynamicWind callccObj >> return (particle "ok"))

    return ()

dynamicWind :: Value -> VM ()
dynamicWind callccObj = do
    [$p|internal-call/cc|] =:: callccObj
    ([$p|dynamic-winds|] =::) =<< eval [$e|Parameter new: []|]

    [$p|(o: Object) call/cc|] =::: [$e|internal-call/cc pass-to: { cont |
        winds = dynamic-winds _? copy

        new = cont clone do: {
            yield: v := {
                dynamic-unwind call: [
                    winds
                    dynamic-winds _? length - winds length
                ]

                cont yield: v
            } call
        }

        o call: [new]
    }|]

    [$p|(v: Block) before: (b: Block)|] =::: [$e|v before: b after: { @ok }|]
    [$p|(v: Block) after: (a: Block)|] =::: [$e|v before: { @ok } after: a|]
    [$p|(v: Block) before: (b: Block) after: (a: Block)|] =::: [$e|{
        b call

        dynamic-winds =! ((b -> a) . dynamic-winds _?)

        { v call } ensuring: {
            dynamic-winds =! dynamic-winds _? tail
            a call
        }
    } call|]

    ([$p|dynamic-unwind|] =::) =<< eval [$e|{ to d |
        condition: {
            (dynamic-winds _? == to) -> @ok

            (d < 0) -> {
                dynamic-unwind call: [to tail, d + 1]
                to head from call
                dynamic-winds =! to
                @ok
            } call

            otherwise -> {
                post = dynamic-winds _? head to
                dynamic-winds =! dynamic-winds _? tail
                post call
                dynamic-unwind call: [to, d - 1]
            } call
        }
    }|]