Skip to content

silky/HasCal

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

80 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HasCal

HasCal embeds PlusCal in Haskell as an ordinary Haskell package. Everything is implemented entirely in Haskell, including the model checker.

For example, this PlusCal code from the Learn TLA+ book:

---- MODULE Transfer ----
EXTENDS Naturals, TLC

(* --algorithm transfer
variables alice_account = 10, bob_account = 10,
          account_total = alice_account + bob_account;

process Transfer \in 1..2
  variable money \in 1..20;
begin
Transfer:
  if alice_account >= money then
    A: alice_account := alice_account - money;
       bob_account := bob_account + money;
end if;
C: assert alice_account >= 0;
end process

end algorithm *)

MoneyInvariant == alice_account + bob_account = account_total

… corresponds to this Haskell code:

{-# LANGUAGE BlockArguments  #-}
{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric   #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

import Control.Monad (when)
import Prelude hiding ((.))
import HasCal

data Global = Global
    { _alice_account :: Int
    , _bob_account   :: Int
    , _account_total :: Int
    } deriving (Eq, Generic, Hashable, Show, ToJSON)

data Local = Local { _money :: Int }
    deriving (Eq, Generic, Hashable, Show, ToJSON)

data Label = Transfer | A | C deriving (Eq, Generic, Hashable, Show, ToJSON)

makeLenses ''Global
makeLenses ''Local

main :: IO ()
main = do
    let transfer _ = Coroutine
        { startingLabel = Transfer

        , startingLocals = do
            _money <- [ 1 .. 20 ]
            return Local{..}

        , process = do
            _money <- use (local.money)

            alice_old <- use (global.alice_account)

            when (alice_old >= _money) do
                yield A
                global.alice_account -= _money
                global.bob_account   += _money

            yield C
            alice_new <- use (global.alice_account)
            assert (alice_new >= 0)
        }

    model defaultModel
        { startingGlobals = do
            let _alice_account = 10
            let _bob_account   = 10
            let _account_total = _alice_account + _bob_account
            return Global{..}

        , coroutine = traverse transfer [ 1 .. 2 ]

        , property =
            let predicate (Global{..}, _) =
                    _alice_account + _bob_account == _account_total
            in  always . arr predicate
        }

You can find more example code in the test suite

About

Haskell embedding of PlusCal

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Haskell 98.8%
  • Nix 1.2%