purescript-0.6.9: src/Language/PureScript/Sugar/BindingGroups.hs
-----------------------------------------------------------------------------
--
-- Module : Language.PureScript.BindingGroups
-- Copyright : (c) Phil Freeman 2013
-- License : MIT
--
-- Maintainer : Phil Freeman <paf31@cantab.net>
-- Stability : experimental
-- Portability :
--
-- |
-- This module implements the desugaring pass which creates binding groups from sets of
-- mutually-recursive value declarations and mutually-recursive type declarations.
--
-----------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
module Language.PureScript.Sugar.BindingGroups (
createBindingGroups,
createBindingGroupsModule,
collapseBindingGroups,
collapseBindingGroupsModule
) where
import Data.Graph
import Data.List (nub, intersect)
import Data.Maybe (isJust, mapMaybe)
import Control.Applicative
import Control.Monad ((<=<))
import Control.Monad.Error.Class
import qualified Data.Set as S
import Language.PureScript.AST
import Language.PureScript.Names
import Language.PureScript.Types
import Language.PureScript.Environment
import Language.PureScript.Errors
-- |
-- Replace all sets of mutually-recursive declarations in a module with binding groups
--
createBindingGroupsModule :: (Functor m, Applicative m, MonadError MultipleErrors m) => [Module] -> m [Module]
createBindingGroupsModule = mapM $ \(Module coms name ds exps) -> Module coms name <$> createBindingGroups name ds <*> pure exps
-- |
-- Collapse all binding groups in a module to individual declarations
--
collapseBindingGroupsModule :: [Module] -> [Module]
collapseBindingGroupsModule = map $ \(Module coms name ds exps) -> Module coms name (collapseBindingGroups ds) exps
createBindingGroups :: (Functor m, Applicative m, MonadError MultipleErrors m) => ModuleName -> [Declaration] -> m [Declaration]
createBindingGroups moduleName = mapM f <=< handleDecls
where
(f, _, _) = everywhereOnValuesTopDownM return handleExprs return
handleExprs :: (Functor m, MonadError MultipleErrors m) => Expr -> m Expr
handleExprs (Let ds val) = flip Let val <$> handleDecls ds
handleExprs other = return other
-- |
-- Replace all sets of mutually-recursive declarations with binding groups
--
handleDecls :: (Functor m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
handleDecls ds = do
let values = filter isValueDecl ds
dataDecls = filter isDataDecl ds
allProperNames = map getProperName dataDecls
dataVerts = map (\d -> (d, getProperName d, usedProperNames moduleName d `intersect` allProperNames)) dataDecls
dataBindingGroupDecls <- parU (stronglyConnComp dataVerts) toDataBindingGroup
let allIdents = map getIdent values
valueVerts = map (\d -> (d, getIdent d, usedIdents moduleName d `intersect` allIdents)) values
bindingGroupDecls <- parU (stronglyConnComp valueVerts) (toBindingGroup moduleName)
return $ filter isImportDecl ds ++
filter isExternDataDecl ds ++
filter isExternInstanceDecl ds ++
dataBindingGroupDecls ++
filter isTypeClassDeclaration ds ++
filter isTypeClassInstanceDeclaration ds ++
filter isFixityDecl ds ++
filter isExternDecl ds ++
bindingGroupDecls
-- |
-- Collapse all binding groups to individual declarations
--
collapseBindingGroups :: [Declaration] -> [Declaration]
collapseBindingGroups = let (f, _, _) = everywhereOnValues id collapseBindingGroupsForValue id in map f . concatMap go
where
go (DataBindingGroupDeclaration ds) = ds
go (BindingGroupDeclaration ds) = map (\(ident, nameKind, val) -> ValueDeclaration ident nameKind [] (Right val)) ds
go (PositionedDeclaration pos com d) = map (PositionedDeclaration pos com) $ go d
go other = [other]
collapseBindingGroupsForValue :: Expr -> Expr
collapseBindingGroupsForValue (Let ds val) = Let (collapseBindingGroups ds) val
collapseBindingGroupsForValue other = other
usedIdents :: ModuleName -> Declaration -> [Ident]
usedIdents moduleName =
let (f, _, _, _, _) = everythingWithContextOnValues S.empty [] (++) def usedNamesE usedNamesB def def
in nub . f
where
def s _ = (s, [])
usedNamesE :: S.Set Ident -> Expr -> (S.Set Ident, [Ident])
usedNamesE scope (Var (Qualified Nothing name)) | name `S.notMember` scope = (scope, [name])
usedNamesE scope (Var (Qualified (Just moduleName') name)) | moduleName == moduleName' && name `S.notMember` scope = (scope, [name])
usedNamesE scope (Abs (Left name) _) = (name `S.insert` scope, [])
usedNamesE scope _ = (scope, [])
usedNamesB :: S.Set Ident -> Binder -> (S.Set Ident, [Ident])
usedNamesB scope binder = (scope `S.union` S.fromList (binderNames binder), [])
usedImmediateIdents :: ModuleName -> Declaration -> [Ident]
usedImmediateIdents moduleName =
let (f, _, _, _, _) = everythingWithContextOnValues True [] (++) def usedNamesE def def def
in nub . f
where
def s _ = (s, [])
usedNamesE :: Bool -> Expr -> (Bool, [Ident])
usedNamesE True (Var (Qualified Nothing name)) = (True, [name])
usedNamesE True (Var (Qualified (Just moduleName') name)) | moduleName == moduleName' = (True, [name])
usedNamesE True (Abs _ _) = (False, [])
usedNamesE scope _ = (scope, [])
usedProperNames :: ModuleName -> Declaration -> [ProperName]
usedProperNames moduleName =
let (f, _, _, _, _) = accumTypes (everythingOnTypes (++) usedNames)
in nub . f
where
usedNames :: Type -> [ProperName]
usedNames (ConstrainedType constraints _) = flip mapMaybe constraints $ \qual ->
case qual of
(Qualified (Just moduleName') name, _) | moduleName == moduleName' -> Just name
_ -> Nothing
usedNames (TypeConstructor (Qualified (Just moduleName') name)) | moduleName == moduleName' = [name]
usedNames _ = []
getIdent :: Declaration -> Ident
getIdent (ValueDeclaration ident _ _ _) = ident
getIdent (PositionedDeclaration _ _ d) = getIdent d
getIdent _ = error "Expected ValueDeclaration"
getProperName :: Declaration -> ProperName
getProperName (DataDeclaration _ pn _ _) = pn
getProperName (TypeSynonymDeclaration pn _ _) = pn
getProperName (PositionedDeclaration _ _ d) = getProperName d
getProperName _ = error "Expected DataDeclaration"
-- |
-- Convert a group of mutually-recursive dependencies into a BindingGroupDeclaration (or simple ValueDeclaration).
--
--
toBindingGroup :: (Functor m, MonadError MultipleErrors m) => ModuleName -> SCC Declaration -> m Declaration
toBindingGroup _ (AcyclicSCC d) = return d
toBindingGroup _ (CyclicSCC [d]) = return d
toBindingGroup moduleName (CyclicSCC ds') =
-- Once we have a mutually-recursive group of declarations, we need to sort
-- them further by their immediate dependencies (those outside function
-- bodies). In particular, this is relevant for type instance dictionaries
-- whose members require other type instances (for example, functorEff
-- defines (<$>) = liftA1, which depends on applicativeEff). Note that
-- superclass references are still inside functions, so don't count here.
-- If we discover declarations that still contain mutually-recursive
-- immediate references, we're guaranteed to get an undefined reference at
-- runtime, so treat this as an error. See also github issue #365.
BindingGroupDeclaration <$> mapM toBinding (stronglyConnComp valueVerts)
where
idents :: [Ident]
idents = map (\(_, i, _) -> i) valueVerts
valueVerts :: [(Declaration, Ident, [Ident])]
valueVerts = map (\d -> (d, getIdent d, usedImmediateIdents moduleName d `intersect` idents)) ds'
toBinding :: (MonadError MultipleErrors m) => SCC Declaration -> m (Ident, NameKind, Expr)
toBinding (AcyclicSCC d) = return $ fromValueDecl d
toBinding (CyclicSCC ~(d:ds)) = cycleError d ds
cycleError :: (MonadError MultipleErrors m) => Declaration -> [Declaration] -> m a
cycleError (PositionedDeclaration p _ d) ds = rethrowWithPosition p $ cycleError d ds
cycleError (ValueDeclaration n _ _ (Right _)) [] = throwError . errorMessage $ CycleInDeclaration n
cycleError d ds@(_:_) = rethrow (onErrorMessages (NotYetDefined (map getIdent ds))) $ cycleError d []
cycleError _ _ = error "Expected ValueDeclaration"
toDataBindingGroup :: (MonadError MultipleErrors m) => SCC Declaration -> m Declaration
toDataBindingGroup (AcyclicSCC d) = return d
toDataBindingGroup (CyclicSCC [d]) = case isTypeSynonym d of
Just pn -> throwError . errorMessage $ CycleInTypeSynonym (Just pn)
_ -> return d
toDataBindingGroup (CyclicSCC ds')
| all (isJust . isTypeSynonym) ds' = throwError . errorMessage $ CycleInTypeSynonym Nothing
| otherwise = return $ DataBindingGroupDeclaration ds'
isTypeSynonym :: Declaration -> Maybe ProperName
isTypeSynonym (TypeSynonymDeclaration pn _ _) = Just pn
isTypeSynonym (PositionedDeclaration _ _ d) = isTypeSynonym d
isTypeSynonym _ = Nothing
fromValueDecl :: Declaration -> (Ident, NameKind, Expr)
fromValueDecl (ValueDeclaration ident nameKind [] (Right val)) = (ident, nameKind, val)
fromValueDecl ValueDeclaration{} = error "Binders should have been desugared"
fromValueDecl (PositionedDeclaration _ _ d) = fromValueDecl d
fromValueDecl _ = error "Expected ValueDeclaration"