{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module Bricks.Term where -- Bricks internal import Bricks.Internal.Monad import Bricks.Internal.Prelude import Bricks.Internal.Seq (Seq) import Bricks.Internal.Text (Text) -- Containers import Data.Map (Map) import Data.Set (Set) -- Base import Data.Dynamic (Dynamic) import Data.IORef data Bottom = Bottom Text displayBottom :: Bottom -> Text displayBottom (Bottom message) = "Error: " <> message class (Monad m, MonadIO m, MonadError Bottom m) => MonadEval m where reduce'term :: Term -> m Term reduce'dict'keys :: Term -> m (Map Text Term) type Function = forall m. MonadEval m => Term -> m Term data Term = Term'Data Text Dynamic -- ^ The name of the data type, and a value of that type. | Term'Function Function | Term'Lambda TermPattern Term -- ^ The head and body of a lambda expression. | Term'LetRec (Map Text Term) Term | Term'List (Seq Term) | Term'Dict (Seq (Term, Term)) | Term'Dict'ReducedKeys (Map Text Term) | Term'Var Text | Term'Apply Term Term | Term'Pointer TermPtr {- | Alias for 'Term'Apply'. The name is an allusion to the AST depictions in /The Implementation of Functional Programming Languages/, where "/f/ applied to /x/" is drawn as: > @ > ╱ ╲ > f x For a function of two parameters, see the corresponding '/@@\' operator. -} (/@\) :: Term -> Term -> Term (/@\) = Term'Apply infixl /@\ {- | Like '/@\', but for a function applied to two arguments. Depicted as an abstract syntax tree, @f /\@\@\\ (x, y)@ looks like this: > @ > ╱ ╲ > @ y > ╱ ╲ > f x -} (/@@\) :: Term -> (Term, Term) -> Term f /@@\ (x, y) = (f /@\ x) /@\ y infixl /@@\ {- | Alias for 'Term'Lambda'. -} (|->) :: TermPattern -> Term -> Term (|->) = Term'Lambda infixl |-> data TermPattern = TermPattern'Simple Text | TermPattern'Dict (Set Text) type TermPtr = IORef Term create'pointer :: MonadIO m => Term -> m Term create'pointer x = case x of Term'Pointer _ -> pure x -- The term is already a pointer, don't make another _ -> Term'Pointer <$> liftIO (newIORef x) dereference :: MonadIO m => Term -> m Term dereference = \case Term'Pointer p -> readTermPtr p >>= dereference x -> pure x newTermPtr :: MonadIO m => Term -> m Term newTermPtr x = case x of Term'Pointer _ -> pure x -- The term is already a pointer, nothing to do _ -> Term'Pointer <$> liftIO (newIORef x) readTermPtr :: MonadIO m => TermPtr -> m Term readTermPtr = liftIO . readIORef writeTermPtr :: MonadIO m => TermPtr -> Term -> m () writeTermPtr ptr val = do x <- dereference val liftIO $ writeIORef ptr x bottom :: MonadError Bottom m => Bottom -> m a bottom = throwError