Carp/src/Eval.hs

1185 lines
53 KiB
Haskell
Raw Permalink Normal View History

2020-02-13 17:58:06 +03:00
{-# LANGUAGE LambdaCase #-}
module Eval where
2017-06-26 12:15:03 +03:00
import ColorText
import Commands
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
import Context
import Control.Applicative
2020-03-06 20:16:22 +03:00
import Control.Exception
import Control.Monad.State
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
import Data.Either (fromRight)
import Data.Foldable (foldlM, foldrM)
import Data.List (foldl', isSuffixOf)
2020-03-06 20:16:22 +03:00
import Data.List.Split (splitOn, splitWhen)
import Data.Maybe (fromJust, fromMaybe, isJust)
import Emit
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
import qualified Env as E
import EvalError
2017-12-15 18:37:03 +03:00
import Expand
import Forms
import Infer
import Info
import qualified Map
import qualified Meta
import Obj
import Parsing
2019-09-24 00:47:26 +03:00
import Path
2020-02-21 22:20:13 +03:00
import Primitives
import Project
import Qualify
import qualified Set
import System.Exit (ExitCode (..), exitSuccess, exitWith)
import System.Process (readProcessWithExitCode)
import qualified Text.Parsec as Parsec
import TypeError
import Types
import Util
import Prelude hiding (exp, mod)
2017-06-26 12:15:03 +03:00
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
-- TODO: Formalize "lookup order preference" a bit better and move into
-- the Context module.
data LookupPreference
= PreferDynamic
| PreferGlobal
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
| PreferLocal [SymPath]
deriving (Show)
data Resolver
= ResolveGlobal
| ResolveLocal
type Evaluator = [XObj] -> IO (Context, Either EvalError XObj)
-- Prefer dynamic bindings
evalDynamic :: Resolver -> Context -> XObj -> IO (Context, Either EvalError XObj)
evalDynamic resolver ctx xobj = eval ctx xobj PreferDynamic resolver
-- Prefer global bindings
evalStatic :: Resolver -> Context -> XObj -> IO (Context, Either EvalError XObj)
evalStatic resolver ctx xobj = eval ctx xobj PreferGlobal resolver
2017-12-15 18:22:16 +03:00
-- | Dynamic (REPL) evaluation of XObj:s (s-expressions)
-- Note: You might find a bunch of code of the following form both here and in
-- macroExpand:
--
-- pure (ctx, do res <- <something>
-- Right <something else with res>)
--
-- This might a little weird to you, and rightfully so. Through the nested do
-- we ensure that an evaluation is forced where it needs to be, since we depend
-- on the state here; eval is inherently stateful (because it carries around
-- the compilers context, which might change after each macro expansion), and
-- it gets real weird with laziness. (Note to the note: this code is mostly a
-- remnant of us using StateT, and might not be necessary anymore since we
-- switched to more explicit state-passing.)
eval :: Context -> XObj -> LookupPreference -> Resolver -> IO (Context, Either EvalError XObj)
eval ctx xobj@(XObj o info ty) preference resolver =
2020-02-21 22:20:13 +03:00
case o of
Lst body -> eval' body
Sym spath@(SymPath p n) _ ->
pure $
case resolver of
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
ResolveGlobal -> unwrapLookup ((tryAllLookups preference) >>= checkStatic)
ResolveLocal -> unwrapLookup (tryAllLookups preference)
where
checkStatic v@(_, Right (XObj (Lst ((XObj obj _ _) : _)) _ _)) =
if isResolvableStaticObj obj
then pure (ctx, Left (HasStaticCall xobj info))
else pure v
checkStatic v = pure v
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
-- all else failed, error.
unwrapLookup =
fromMaybe
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
(throwErr (SymbolNotFound spath) ctx info)
-- Try all lookups performs lookups for symbols based on a given
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
-- lookup preference.
tryAllLookups :: LookupPreference -> Maybe (Context, Either EvalError XObj)
tryAllLookups PreferDynamic = (getDynamic) <|> fullLookup
tryAllLookups PreferGlobal = (getGlobal spath) <|> fullLookup
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
tryAllLookups (PreferLocal shadows) = (if spath `elem` shadows then (getLocal n) else (getDynamic)) <|> fullLookup
fullLookup = (tryDynamicLookup <|> (if null p then tryInternalLookup spath <|> tryLookup spath else tryLookup spath))
getDynamic :: Maybe (Context, Either EvalError XObj)
getDynamic =
do
(Binder _ found) <- maybeId (E.findValueBinder (contextGlobalEnv ctx) (SymPath ("Dynamic" : p) n))
pure (ctx, Right (resolveDef found))
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
getGlobal :: SymPath -> Maybe (Context, Either EvalError XObj)
getGlobal path =
do
(Binder meta found) <- maybeId (E.findValueBinder (contextGlobalEnv ctx) path)
checkPrivate meta found
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
tryDynamicLookup :: Maybe (Context, Either EvalError XObj)
tryDynamicLookup =
do
(Binder meta found) <- maybeId (E.searchValueBinder (contextGlobalEnv ctx) (SymPath ("Dynamic" : p) n))
checkPrivate meta found
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
getLocal :: String -> Maybe (Context, Either EvalError XObj)
getLocal name =
do
internal <- contextInternalEnv ctx
(Binder _ found) <- maybeId (E.getValueBinder internal name)
pure (ctx, Right (resolveDef found))
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
-- TODO: Deprecate this function?
-- The behavior here is a bit nefarious since it relies on cached
-- environment parents (it calls `search` on the "internal" binder).
-- But for now, it seems to be needed for some cases.
tryInternalLookup :: SymPath -> Maybe (Context, Either EvalError XObj)
tryInternalLookup path =
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
--trace ("Looking for internally " ++ show path) -- ++ show (fmap (fmap E.binders . E.parent) (contextInternalEnv ctx)))
( contextInternalEnv ctx
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
>>= \e ->
maybeId (E.searchValueBinder e path)
fix: don't expand inner module macros on first pass; privacy (#1216) * fix: don't expand inner module macros on first pass; privacy This commit changes the behavior of expansions to avoid expanding module expressions until we're actually processing the module in question. Previously, the following form would be entirely expanded at the time of evaluating A: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- expand, current env is A, *NOT* B. so if this expands to (private f) (defn f ....) the f of the expansion is added to *A*, and we have a duplicate ghost binder. ) (defn foo [] B.f) <- expand, B.f does not exist yet, any meta on the binding will be ignored, permitting privacy errors since expansion ignores undefined bindings, instead, we'll look this up at eval time, and not check privacy as doing so would cause problems for legitimate cases. ) ``` This meant that if the macro happened to have side-effects, e.g. calling `meta-set!` we'd produce side-effects in the wrong environment, A, resulting in duplicate bindings, missing bindings at evaluation time, and other problems. Now, we instead process the form as follows: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- wait ) (defn foo [] B.f) ) ;; step 2 (defmodule A (foo-bar ) <- previously expanded macro (defmodule B <- current environment (some-macro f) <- expand ) .... ) ``` In general, this prevents the generation of a bunch of unintentional and incorrectly added bindings when calling `meta-set!` from various macros. Additionally, privacy constraints are now carried across nested modules: ``` (defmodule A (defmodule B (private f) (defn f [] 0) ) (defn g [] (B.f)) ;; Privacy error! ) ``` This change also fixed an issue whereby recursive functions with `sig` annotations could trick the compiler. Again, this had to do with the unintentionally added bindings stemming from expansion of nested module expressions via meta-set. Fixes #1213, Fixes #467 * fix: ensure we check privacy against the path of found binders
2021-05-24 22:04:10 +03:00
>>= \(Binder meta found) -> checkPrivate meta found
)
Fix: Allow shadows of global commands, allow recursion in let bindings. (#1214) * fix: don't shadow local bindings with dynamics This commit adds a new lookup preference to the evaluator, LookupLocal, and uses it to lookup bindings in the scope of let forms. This fixes an issue whereby our original bias toward dynamic bindings would prevent users from shadowing dynamic bindings with local bindings of the same name. Before the following code returned `command c`: ``` (defdynamic test-val (let [c (car (list 1 2 3))] c)) ``` It now returns `1`. I also fixed a small issue with top-level (as in, without a corresponding function environment) let forms (they'd cause a crash for lack of an environment parent). Fixes #659 * refactor: only prefer local lookups for shadows The prior commit introduced a local lookup preference into the evaluator in order allow for shadowing of global names in local scopes (like let bindings). However, this introduced prohibitive performance costs, especially for dynamic functions. To mitigate this, we only perform local-biased lookups for a set of known-shadows. Since we know the names of local bindings at form evaluation time, we can restrict our local lookup bias to these paths only. This greatly reduces the performance penalties initially incurred by the change. I've also refactored some of the lookup code for clarity. * fix: support recursive let bindings Previously, the bodies of anonymous functions bound to a let name did not have access to their names, making recursion such as: ``` (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10)) ``` impossible. We now equip evaluation of let bindings with an additional recursion environment making this possible. The example above will now resolve to `1`. Fixes #1133
2021-05-24 09:58:16 +03:00
tryLookup :: SymPath -> Maybe (Context, Either EvalError XObj)
tryLookup path =
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
( maybeId (E.searchValueBinder (contextGlobalEnv ctx) path)
>>= \(Binder meta found) -> checkPrivate meta found
)
fix: don't expand inner module macros on first pass; privacy (#1216) * fix: don't expand inner module macros on first pass; privacy This commit changes the behavior of expansions to avoid expanding module expressions until we're actually processing the module in question. Previously, the following form would be entirely expanded at the time of evaluating A: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- expand, current env is A, *NOT* B. so if this expands to (private f) (defn f ....) the f of the expansion is added to *A*, and we have a duplicate ghost binder. ) (defn foo [] B.f) <- expand, B.f does not exist yet, any meta on the binding will be ignored, permitting privacy errors since expansion ignores undefined bindings, instead, we'll look this up at eval time, and not check privacy as doing so would cause problems for legitimate cases. ) ``` This meant that if the macro happened to have side-effects, e.g. calling `meta-set!` we'd produce side-effects in the wrong environment, A, resulting in duplicate bindings, missing bindings at evaluation time, and other problems. Now, we instead process the form as follows: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- wait ) (defn foo [] B.f) ) ;; step 2 (defmodule A (foo-bar ) <- previously expanded macro (defmodule B <- current environment (some-macro f) <- expand ) .... ) ``` In general, this prevents the generation of a bunch of unintentional and incorrectly added bindings when calling `meta-set!` from various macros. Additionally, privacy constraints are now carried across nested modules: ``` (defmodule A (defmodule B (private f) (defn f [] 0) ) (defn g [] (B.f)) ;; Privacy error! ) ``` This change also fixed an issue whereby recursive functions with `sig` annotations could trick the compiler. Again, this had to do with the unintentionally added bindings stemming from expansion of nested module expressions via meta-set. Fixes #1213, Fixes #467 * fix: ensure we check privacy against the path of found binders
2021-05-24 22:04:10 +03:00
<|> ( (maybeId (E.searchValueBinder (contextGlobalEnv ctx) (SymPath ((contextPath ctx) ++ p) n)))
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
>>= \(Binder meta found) -> checkPrivate meta found
)
<|> ( maybeId (lookupBinderInTypeEnv ctx path)
>>= \(Binder _ found) -> pure (ctx, Right (resolveDef found))
)
<|> ( foldl
(<|>)
Nothing
( map
( \(SymPath p' n') ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
maybeId (E.searchValueBinder (contextGlobalEnv ctx) (SymPath (p' ++ (n' : p)) n))
>>= \(Binder meta found) -> checkPrivate meta found
)
(Set.toList (envUseModules (contextGlobalEnv ctx)))
)
)
checkPrivate meta found =
pure $
if metaIsTrue meta "private"
then throwErr (PrivateBinding (getPath found)) ctx info
else (ctx, Right (resolveDef found))
Arr objs -> do
2020-03-28 16:32:41 +03:00
(newCtx, evaled) <- foldlM successiveEval (ctx, Right []) objs
pure
( newCtx,
do
ok <- evaled
Right (XObj (Arr ok) info ty)
)
StaticArr objs -> do
2020-08-24 12:05:47 +03:00
(newCtx, evaled) <- foldlM successiveEval (ctx, Right []) objs
pure
( newCtx,
do
ok <- evaled
Right (XObj (StaticArr ok) info ty)
)
_ -> do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
(nctx, res) <- annotateWithinContext ctx xobj
pure $ case res of
Left e -> (nctx, Left e)
Right (val, _) -> (nctx, Right val)
2020-02-21 22:20:13 +03:00
where
resolveDef (XObj (Lst [XObj DefDynamic _ _, _, value]) _ _) = value
resolveDef (XObj (Lst [XObj LocalDef _ _, _, value]) _ _) = value
2020-02-21 22:20:13 +03:00
resolveDef x = x
2020-03-28 16:32:41 +03:00
eval' form =
case validate form of
Left e -> pure (evalError ctx (format e) (xobjInfo xobj))
Right form' ->
case form' of
(IfPat _ _ _ _) -> evaluateIf form'
(DefnPat _ _ _ _) -> specialCommandDefine ctx xobj
(DefPat _ _ _) -> specialCommandDefine ctx xobj
(ThePat _ _ _) -> evaluateThe form'
(LetPat _ _ _) -> evaluateLet form'
(FnPat _ _ _) -> evaluateFn form'
(AppPat (ClosurePat _ _ _) _) -> evaluateClosure form'
(AppPat (DynamicFnPat _ _ _) _) -> evaluateDynamicFn form'
(AppPat (MacroPat _ _ _) _) -> evaluateMacro form'
(AppPat (CommandPat _ _ _) _) -> evaluateCommand form'
(AppPat (PrimitivePat _ _ _) _) -> evaluatePrimitive form'
(WithPat _ sym@(SymPat path _) forms) -> specialCommandWith ctx sym path forms
(DoPat _ forms) -> evaluateSideEffects forms
(WhilePat _ cond body) -> specialCommandWhile ctx cond body
(SetPat _ iden value) -> specialCommandSet ctx (iden : [value])
-- This next match is a bit redundant looking at first glance, but
-- it is necessary to prevent hangs on input such as: `((def foo 2)
-- 4)`. Ideally, we could perform only *one* static check (the one
-- we do in eval). But the timing is wrong.
-- The `def` in the example above initially comes into the
-- evaluator as a *Sym*, **not** as a `Def` xobj. So, we need to
-- discriminate on the result of evaluating the symbol to eagerly
-- break the evaluation loop, otherwise we will proceed to evaluate
-- the def form, yielding Unit, and attempt to reevaluate unit
-- indefinitely on subsequent eval loops.
-- Importantly, the loop *is only broken on literal nested lists*.
-- That is, passing a *symbol* that, e.g. resolves to a defn list, won't
-- break our normal loop.
(AppPat self@(ListPat (x@(SymPat _ _) : _)) args) ->
do
(_, evald) <- eval ctx x preference ResolveGlobal
case evald of
Left err -> pure (evalError ctx (show err) (xobjInfo xobj))
Right x' -> case checkStatic' x' of
Right _ -> evaluateApp (self : args)
Left er -> pure (ctx, Left er)
(AppPat (ListPat _) _) -> evaluateApp form'
(AppPat (SymPat _ _) _) -> evaluateApp form'
(AppPat (XObj other _ _) _)
| isResolvableStaticObj other ->
pure (ctx, (Left (HasStaticCall xobj info)))
[] -> pure (ctx, dynamicNil)
_ -> pure (throwErr (UnknownForm xobj) ctx (xobjInfo xobj))
checkStatic' (XObj Def _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Defn _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Interface _ _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Instantiate _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Deftemplate _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (External _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Match _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj Ref _ _) = Left (HasStaticCall xobj info)
checkStatic' x' = Right x'
successiveEval (ctx', acc) x =
case acc of
Left _ -> pure (ctx', acc)
Right l -> do
(newCtx, evald) <- eval ctx' x preference resolver
pure $ case evald of
Right res -> (newCtx, Right (l ++ [res]))
Left err -> (newCtx, Left err)
evaluateIf :: Evaluator
evaluateIf (IfPat _ cond true false) = do
(newCtx, evd) <- eval ctx cond preference ResolveLocal
case evd of
Right cond' ->
case xobjObj cond' of
Bol b -> eval newCtx (if b then true else false) preference ResolveLocal
_ ->
pure (throwErr (IfContainsNonBool cond) ctx (xobjInfo cond))
Left e -> pure (newCtx, Left e)
evaluateIf _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateThe :: Evaluator
evaluateThe (ThePat the t value) = do
(newCtx, evaledValue) <- expandAll (evalDynamic ResolveLocal) ctx value -- TODO: Why expand all here?
pure
( newCtx,
do
okValue <- evaledValue
Right (XObj (Lst [the, t, okValue]) info ty)
)
evaluateThe _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateLet :: Evaluator
evaluateLet (LetPat _ (ArrPat bindings) body) = do
let binds = unwrapVar (pairwise bindings) []
ni = Env Map.empty (contextInternalEnv ctx) Nothing Set.empty InternalEnv 0
eitherCtx <- foldrM successiveEval' (Right (replaceInternalEnv ctx ni)) binds
case eitherCtx of
Left err -> pure (ctx, Left err)
Right newCtx -> do
(finalCtx, evaledBody) <- eval newCtx body (PreferLocal (map (\(name, _) -> (SymPath [] name)) binds)) ResolveLocal
let Just e = contextInternalEnv finalCtx
parentEnv = envParent e
pure
( replaceInternalEnvMaybe finalCtx parentEnv,
do
okBody <- evaledBody
Right okBody
)
where
unwrapVar [] acc = acc
unwrapVar ((XObj (Sym (SymPath [] x) _) _ _, y) : xs) acc = unwrapVar xs ((x, y) : acc)
unwrapVar _ _ = error "unwrapvar"
successiveEval' (n, x) =
\case
err@(Left _) -> pure err
Right ctx' -> do
-- Bind a reference to the let bind in a recursive
-- environment. This permits recursion in anonymous functions
-- in let binds such as:
-- (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10))
let origin = (contextInternalEnv ctx')
recFix = (E.recursive origin (Just "let-rec-env") 0)
Right envWithSelf = if isFn x then E.insertX recFix (SymPath [] n) x else Right recFix
ctx'' = replaceInternalEnv ctx' envWithSelf
(newCtx, res) <- eval ctx'' x preference resolver
case res of
Right okX ->
pure $ Right (fromRight (error "Failed to eval let binding!!") (bindLetDeclaration (newCtx {contextInternalEnv = origin}) n okX))
Left err -> pure $ Left err
evaluateLet _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateFn :: Evaluator
evaluateFn (FnPat self args body) = do
(newCtx, expanded) <- macroExpand ctx body
pure $
case expanded of
Right b ->
(newCtx, Right (XObj (Closure (XObj (Lst [self, args, b]) info ty) (CCtx newCtx)) info ty))
Left err -> (ctx, Left err)
evaluateFn _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateClosure :: Evaluator
evaluateClosure (AppPat (ClosurePat params body c) args) = do
(newCtx, evaledArgs) <- foldlM successiveEval (ctx, Right []) args
case evaledArgs of
Right okArgs -> do
let newGlobals = (contextGlobalEnv newCtx) <> (contextGlobalEnv c)
newTypes = TypeEnv $ (getTypeEnv (contextTypeEnv newCtx)) <> (getTypeEnv (contextTypeEnv c))
updater = replaceHistory' (contextHistory ctx) . replaceGlobalEnv' newGlobals . replaceTypeEnv' newTypes
(ctx', res) <- apply (updater c) body params okArgs
pure (replaceGlobalEnv newCtx (contextGlobalEnv ctx'), res)
Left err -> pure (newCtx, Left err)
evaluateClosure _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateDynamicFn :: Evaluator
evaluateDynamicFn (AppPat (DynamicFnPat _ params body) args) = do
(newCtx, evaledArgs) <- foldlM successiveEval (ctx, Right []) args
case evaledArgs of
Right okArgs -> apply newCtx body params okArgs
Left err -> pure (newCtx, Left err)
evaluateDynamicFn _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateMacro :: Evaluator
evaluateMacro (AppPat (MacroPat _ params body) args) = do
(ctx', res) <- apply ctx body params args
case res of
Right xobj' -> macroExpand ctx' xobj'
Left _ -> pure (ctx, res)
evaluateMacro _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateCommand :: Evaluator
evaluateCommand (AppPat (CommandPat (NullaryCommandFunction nullary) _ _) []) =
nullary ctx
evaluateCommand (AppPat (CommandPat (UnaryCommandFunction unary) _ _) [x]) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) [x]
case evaledArgs of
Right args -> let [x'] = take 1 args in unary c x'
Left err -> pure (ctx, Left err)
evaluateCommand (AppPat (CommandPat (BinaryCommandFunction binary) _ _) [x, y]) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) [x, y]
case evaledArgs of
Right args -> let [x', y'] = take 2 args in binary c x' y'
Left err -> pure (ctx, Left err)
evaluateCommand (AppPat (CommandPat (TernaryCommandFunction ternary) _ _) [x, y, z]) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) [x, y, z]
case evaledArgs of
Right args' -> let [x', y', z'] = take 3 args' in ternary c x' y' z'
Left err -> pure (ctx, Left err)
evaluateCommand (AppPat (CommandPat (VariadicCommandFunction variadic) _ _) args) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) args
case evaledArgs of
Right args' -> variadic c args'
Left err -> pure (ctx, Left err)
-- Should be caught during validation
evaluateCommand (AppPat (CommandPat _ _ _) _) =
pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateCommand _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluatePrimitive :: Evaluator
evaluatePrimitive (AppPat p@(PrimitivePat (NullaryPrimitive nullary) _ _) []) =
nullary p ctx
evaluatePrimitive (AppPat p@(PrimitivePat (UnaryPrimitive unary) _ _) [x]) = do
unary p ctx x
evaluatePrimitive (AppPat p@(PrimitivePat (BinaryPrimitive binary) _ _) [x, y]) = do
binary p ctx x y
evaluatePrimitive (AppPat p@(PrimitivePat (TernaryPrimitive ternary) _ _) [x, y, z]) = do
ternary p ctx x y z
evaluatePrimitive (AppPat p@(PrimitivePat (QuaternaryPrimitive quaternary) _ _) [x, y, z, w]) = do
quaternary p ctx x y z w
evaluatePrimitive (AppPat p@(PrimitivePat (VariadicPrimitive variadic) _ _) args) = do
variadic p ctx args
-- Should be caught during validation
evaluatePrimitive (AppPat (PrimitivePat _ _ _) _) =
pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluatePrimitive _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateApp :: Evaluator
evaluateApp (AppPat f' args) =
case f' of
l@(ListPat _) -> go l ResolveLocal
sym@(SymPat _ _) -> go sym resolver
_ -> pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
where
go x resolve =
do
(newCtx, f) <- eval ctx x preference resolve
case f of
Right fun -> do
(newCtx', res) <- eval (pushFrame newCtx xobj) (XObj (Lst (fun : args)) (xobjInfo x) (xobjTy x)) preference ResolveLocal
pure (popFrame newCtx', res)
x' -> pure (newCtx, x')
evaluateApp _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateSideEffects :: Evaluator
evaluateSideEffects forms = do
foldlM successiveEval' (ctx, dynamicNil) forms
where
successiveEval' (ctx', acc) x =
case acc of
err@(Left _) -> pure (ctx', err)
Right _ -> eval ctx' x preference resolver
2020-04-10 13:37:55 +03:00
macroExpand :: Context -> XObj -> IO (Context, Either EvalError XObj)
macroExpand ctx xobj =
case xobj of
XObj (Arr objs) i t -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) objs
pure
( newCtx,
do
ok <- expanded
Right (XObj (Arr ok) i t)
)
2020-08-24 12:05:47 +03:00
XObj (StaticArr objs) i t -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) objs
pure
( newCtx,
do
ok <- expanded
Right (XObj (StaticArr ok) i t)
)
XObj (Lst (XObj (Sym (SymPath [] "defmodule") _) _ _ : _)) _ _ ->
fix: don't expand inner module macros on first pass; privacy (#1216) * fix: don't expand inner module macros on first pass; privacy This commit changes the behavior of expansions to avoid expanding module expressions until we're actually processing the module in question. Previously, the following form would be entirely expanded at the time of evaluating A: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- expand, current env is A, *NOT* B. so if this expands to (private f) (defn f ....) the f of the expansion is added to *A*, and we have a duplicate ghost binder. ) (defn foo [] B.f) <- expand, B.f does not exist yet, any meta on the binding will be ignored, permitting privacy errors since expansion ignores undefined bindings, instead, we'll look this up at eval time, and not check privacy as doing so would cause problems for legitimate cases. ) ``` This meant that if the macro happened to have side-effects, e.g. calling `meta-set!` we'd produce side-effects in the wrong environment, A, resulting in duplicate bindings, missing bindings at evaluation time, and other problems. Now, we instead process the form as follows: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- wait ) (defn foo [] B.f) ) ;; step 2 (defmodule A (foo-bar ) <- previously expanded macro (defmodule B <- current environment (some-macro f) <- expand ) .... ) ``` In general, this prevents the generation of a bunch of unintentional and incorrectly added bindings when calling `meta-set!` from various macros. Additionally, privacy constraints are now carried across nested modules: ``` (defmodule A (defmodule B (private f) (defn f [] 0) ) (defn g [] (B.f)) ;; Privacy error! ) ``` This change also fixed an issue whereby recursive functions with `sig` annotations could trick the compiler. Again, this had to do with the unintentionally added bindings stemming from expansion of nested module expressions via meta-set. Fixes #1213, Fixes #467 * fix: ensure we check privacy against the path of found binders
2021-05-24 22:04:10 +03:00
pure (ctx, Right xobj)
XObj (Lst [XObj (Sym (SymPath [] "quote") _) _ _, _]) _ _ ->
pure (ctx, Right xobj)
XObj (Lst [XObj (Lst (XObj Macro _ _ : _)) _ _]) _ _ -> evalDynamic ResolveLocal ctx xobj
XObj (Lst (x@(XObj (Sym _ _) _ _) : args)) i t -> do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
(_, f) <- evalDynamic ResolveLocal ctx x
2020-04-10 13:37:55 +03:00
case f of
Right m@(XObj (Lst (XObj Macro _ _ : _)) _ _) -> do
(newCtx', res) <- evalDynamic ResolveLocal ctx (XObj (Lst (m : args)) i t)
pure (newCtx', res)
2020-04-10 17:04:44 +03:00
_ -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) args
pure
( newCtx,
do
ok <- expanded
Right (XObj (Lst (x : ok)) i t)
)
2020-04-10 13:37:55 +03:00
XObj (Lst objs) i t -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) objs
pure
( newCtx,
do
ok <- expanded
Right (XObj (Lst ok) i t)
)
_ -> pure (ctx, Right xobj)
where
successiveExpand (ctx', acc) x =
case acc of
Left _ -> pure (ctx', acc)
Right l -> do
(newCtx, expanded) <- macroExpand ctx' x
pure $ case expanded of
Right res -> (newCtx, Right (l ++ [res]))
Left err -> (newCtx, Left err)
2020-04-10 13:37:55 +03:00
2020-03-28 16:32:41 +03:00
apply :: Context -> XObj -> [XObj] -> [XObj] -> IO (Context, Either EvalError XObj)
apply ctx@Context {contextInternalEnv = internal} body params args =
let allParams = map getName params
in case splitWhen (":rest" ==) allParams of
[a, b] -> callWith a b
[a] -> callWith a []
_ ->
pure (throwErr (MacroBadArgumentSplit allParams) ctx Nothing)
where
callWith proper rest = do
let n = length proper
insideEnv = Env Map.empty internal Nothing Set.empty InternalEnv 0
insideEnv' =
foldl'
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
(\e (p, x) -> fromRight (error "Couldn't add local def ") (E.insertX e (SymPath [] p) (toLocalDef p x)))
insideEnv
(zip proper (take n args))
insideEnv'' =
if null rest
then insideEnv'
else
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
fromRight
(error "couldn't insert into inside env")
( E.insertX
insideEnv'
(SymPath [] (head rest))
(XObj (Lst (drop n args)) Nothing Nothing)
)
binds = if null rest then proper else proper ++ [(head rest)]
(c, r) <- (eval (replaceInternalEnv ctx insideEnv'') body (PreferLocal (map (\x -> (SymPath [] x)) binds)) ResolveLocal)
pure (c {contextInternalEnv = internal}, r)
2017-12-16 16:54:26 +03:00
-- | Parses a string and then converts the resulting forms to commands, which are evaluated in order.
2020-03-07 02:26:32 +03:00
executeString :: Bool -> Bool -> Context -> String -> String -> IO Context
executeString = executeStringAtLine 1
executeStringAtLine :: Int -> Bool -> Bool -> Context -> String -> String -> IO Context
executeStringAtLine line doCatch printResult ctx input fileName =
2020-03-07 02:26:32 +03:00
if doCatch then catch exec (catcher ctx) else exec
where
exec = case parseAtLine line input fileName of
Left parseError ->
let sourcePos = Parsec.errorPos parseError
parseErrorXObj =
XObj
(Lst [])
( Just
dummyInfo
{ infoFile = fileName,
infoLine = Parsec.sourceLine sourcePos,
infoColumn = Parsec.sourceColumn sourcePos
}
)
Nothing
in do
_ <- liftIO $ treatErr ctx (replaceChars (Map.fromList [('\n', " ")]) (show parseError)) parseErrorXObj
pure ctx
Right xobjs -> do
(res, ctx') <-
foldM
interactiveFolder
(XObj (Lst []) (Just dummyInfo) (Just UnitTy), ctx)
xobjs
when
(printResult && xobjTy res /= Just UnitTy)
(putStrLnWithColor Yellow ("=> " ++ pretty res))
pure ctx'
interactiveFolder (_, context) =
executeCommand context
treatErr ctx' e xobj = do
let fppl = projectFilePathPrintLength (contextProj ctx')
case contextExecMode ctx' of
Check -> putStrLn (machineReadableInfoFromXObj fppl xobj ++ " " ++ e)
_ -> emitErrorWithLabel "PARSE ERROR" e
throw CancelEvaluationException
2017-12-16 16:54:26 +03:00
-- | Used by functions that has a series of forms to evaluate and need to fold over them (producing a new Context in the end)
folder :: Context -> XObj -> IO Context
folder context xobj = do
(_, ctx) <- executeCommand context xobj
pure ctx
-- | Take a repl command and execute it.
executeCommand :: Context -> XObj -> IO (XObj, Context)
2020-04-10 17:04:44 +03:00
executeCommand ctx@(Context env _ _ _ _ _ _ _) xobj =
do
when (isJust (envModuleName env)) $
error ("Global env module name is " ++ fromJust (envModuleName env) ++ " (should be Nothing).")
-- The s-expression command is a special case that prefers global/static bindings over dynamic bindings
-- when given a naked binding (no path) as an argument; (s-expr inc)
2020-12-22 19:44:44 +03:00
(newCtx, result) <- if xobjIsSexp xobj then evalStatic ResolveGlobal ctx xobj else evalDynamic ResolveGlobal ctx xobj
case result of
2020-12-22 19:44:44 +03:00
Left e@EvalError {} -> do
reportExecutionError newCtx (show e)
pure (xobj, newCtx)
-- special case: calling something static at the repl
Right (XObj (Lst (XObj (Lst (XObj (Defn _) _ _ : (XObj (Sym (SymPath [] "main") _) _ _) : _)) _ _ : _)) _ _) ->
2020-04-20 11:22:21 +03:00
executeCommand newCtx (withBuildAndRun (XObj (Lst []) (Just dummyInfo) Nothing))
Left (HasStaticCall _ _) ->
2020-06-07 01:14:46 +03:00
callFromRepl newCtx xobj
Right res -> pure (res, newCtx)
where
callFromRepl newCtx xobj' = do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
(nc, r) <- annotateWithinContext newCtx xobj'
case r of
Right (ann, deps) -> do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
ctxWithDeps <- liftIO $ foldM (define True) nc (map Qualified deps)
executeCommand ctxWithDeps (withBuildAndRun (buildMainFunction ann))
Left err -> do
reportExecutionError nc (show err)
pure (xobj', nc)
withBuildAndRun xobj' =
XObj
( Lst
[ XObj Do (Just dummyInfo) Nothing,
xobj',
XObj
(Lst [XObj (Sym (SymPath [] "build") Symbol) (Just dummyInfo) Nothing, trueXObj])
(Just dummyInfo)
Nothing,
XObj
(Lst [XObj (Sym (SymPath [] "run") Symbol) (Just dummyInfo) Nothing])
(Just dummyInfo)
Nothing,
(XObj (Lst []) (Just dummyInfo) (Just UnitTy))
]
)
(Just dummyInfo)
Nothing
xobjIsSexp (XObj (Lst (XObj (Sym (SymPath [] "s-expr") Symbol) _ _ : _)) _ _) = True
xobjIsSexp _ = False
2020-03-07 02:26:32 +03:00
reportExecutionError :: Context -> String -> IO ()
reportExecutionError ctx errorMessage =
case contextExecMode ctx of
2020-03-07 02:26:32 +03:00
Check -> putStrLn errorMessage
_ ->
do
emitErrorBare errorMessage
throw CancelEvaluationException
2017-12-16 16:54:26 +03:00
-- | Decides what to do when the evaluation fails for some reason.
2017-12-14 00:09:12 +03:00
catcher :: Context -> CarpException -> IO Context
catcher ctx exception =
case exception of
(ShellOutException message rc) -> emitErrorWithLabel "RUNTIME ERROR" message >> stop rc
CancelEvaluationException -> stop 1
EvalException err -> emitError (show err) >> stop 1
where
stop rc =
case contextExecMode ctx of
Repl -> pure ctx
Build -> exitWith (ExitFailure rc)
Install _ -> exitWith (ExitFailure rc)
BuildAndRun -> exitWith (ExitFailure rc)
Check -> exitSuccess
2017-12-15 18:55:47 +03:00
2020-03-28 16:32:41 +03:00
specialCommandWith :: Context -> XObj -> SymPath -> [XObj] -> IO (Context, Either EvalError XObj)
2020-11-26 00:12:57 +03:00
specialCommandWith ctx _ path forms = do
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
let Just env = contextInternalEnv ctx <|> maybeId (innermostModuleEnv ctx) <|> Just (contextGlobalEnv ctx)
2020-03-28 16:32:41 +03:00
useThese = envUseModules env
env' = env {envUseModules = Set.insert path useThese}
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
ctx' = replaceGlobalEnv ctx env'
2020-03-28 16:32:41 +03:00
ctxAfter <- liftIO $ foldM folder ctx' forms
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
let Just envAfter = contextInternalEnv ctxAfter <|> maybeId (innermostModuleEnv ctxAfter) <|> Just (contextGlobalEnv ctxAfter)
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
-- undo ALL use:s made inside the 'with'.
ctxAfter' = replaceGlobalEnv ctx (envAfter {envUseModules = useThese})
pure (ctxAfter', dynamicNil)
2020-03-28 16:32:41 +03:00
specialCommandDefine :: Context -> XObj -> IO (Context, Either EvalError XObj)
specialCommandDefine ctx xobj =
do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
(newCtx, result) <- annotateWithinContext ctx xobj
case result of
Right (annXObj, annDeps) ->
do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
ctxWithDeps <- liftIO $ foldM (define True) newCtx (map Qualified annDeps)
ctxWithDef <- liftIO $ define False ctxWithDeps (Qualified annXObj)
pure (ctxWithDef, dynamicNil)
Left err ->
pure (ctx, Left err)
2017-12-16 16:54:26 +03:00
specialCommandWhile :: Context -> XObj -> XObj -> IO (Context, Either EvalError XObj)
specialCommandWhile ctx cond body = do
(newCtx, evd) <- evalDynamic ResolveLocal ctx cond
case evd of
Right c ->
case xobjObj c of
Bol b ->
if b
then do
(newCtx', _) <- evalDynamic ResolveLocal newCtx body
specialCommandWhile newCtx' cond body
else pure (newCtx, dynamicNil)
_ ->
pure (throwErr (WhileContainsNonBool c) ctx (xobjInfo c))
Left e -> pure (newCtx, Left e)
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
getSigFromDefnOrDef :: Context -> XObj -> Either EvalError (Maybe (Ty, XObj))
getSigFromDefnOrDef ctx xobj =
let pathStrings = contextPath ctx
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
globalEnv = contextGlobalEnv ctx
fppl = projectFilePathPrintLength (contextProj ctx)
path = getPath xobj
fullPath = case path of
(SymPath [] _) -> consPath pathStrings path
(SymPath _ _) -> path
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
metaData = either (const emptyMeta) id (E.lookupMeta globalEnv fullPath)
in case Meta.get "sig" metaData of
Just foundSignature ->
case xobjToTy foundSignature of
Just t ->
let sigToken = XObj (Sym (SymPath [] "sig") Symbol) Nothing Nothing
nameToken = XObj (Sym (SymPath [] (getName xobj)) Symbol) Nothing Nothing
recreatedSigForm = XObj (Lst [sigToken, nameToken, foundSignature]) Nothing (Just MacroTy)
in Right (Just (t, recreatedSigForm))
Nothing -> Left (EvalError ("Can't use '" ++ pretty foundSignature ++ "' as a type signature") (contextHistory ctx) fppl (xobjInfo xobj))
2020-04-17 13:29:21 +03:00
Nothing -> Right Nothing
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
annotateWithinContext :: Context -> XObj -> IO (Context, Either EvalError (XObj, [XObj]))
annotateWithinContext ctx xobj = do
let globalEnv = contextGlobalEnv ctx
typeEnv = contextTypeEnv ctx
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
sig = getSigFromDefnOrDef ctx xobj
fppl = projectFilePathPrintLength (contextProj ctx)
case sig of
Left err -> pure (ctx, Left err)
Right okSig -> do
(_, expansionResult) <- expandAll (evalDynamic ResolveLocal) ctx xobj
case expansionResult of
fix: don't expand inner module macros on first pass; privacy (#1216) * fix: don't expand inner module macros on first pass; privacy This commit changes the behavior of expansions to avoid expanding module expressions until we're actually processing the module in question. Previously, the following form would be entirely expanded at the time of evaluating A: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- expand, current env is A, *NOT* B. so if this expands to (private f) (defn f ....) the f of the expansion is added to *A*, and we have a duplicate ghost binder. ) (defn foo [] B.f) <- expand, B.f does not exist yet, any meta on the binding will be ignored, permitting privacy errors since expansion ignores undefined bindings, instead, we'll look this up at eval time, and not check privacy as doing so would cause problems for legitimate cases. ) ``` This meant that if the macro happened to have side-effects, e.g. calling `meta-set!` we'd produce side-effects in the wrong environment, A, resulting in duplicate bindings, missing bindings at evaluation time, and other problems. Now, we instead process the form as follows: ```clojure (defmodule A <- current environment (some-macro) <- expand (defmodule B (some-macro f) <- wait ) (defn foo [] B.f) ) ;; step 2 (defmodule A (foo-bar ) <- previously expanded macro (defmodule B <- current environment (some-macro f) <- expand ) .... ) ``` In general, this prevents the generation of a bunch of unintentional and incorrectly added bindings when calling `meta-set!` from various macros. Additionally, privacy constraints are now carried across nested modules: ``` (defmodule A (defmodule B (private f) (defn f [] 0) ) (defn g [] (B.f)) ;; Privacy error! ) ``` This change also fixed an issue whereby recursive functions with `sig` annotations could trick the compiler. Again, this had to do with the unintentionally added bindings stemming from expansion of nested module expressions via meta-set. Fixes #1213, Fixes #467 * fix: ensure we check privacy against the path of found binders
2021-05-24 22:04:10 +03:00
Left err -> pure (ctx, Left err)
Right expanded ->
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
let xobjFullSymbols = qualify ctx expanded
in case xobjFullSymbols of
Left err -> pure (evalError ctx (show err) (xobjInfo xobj))
Right xs ->
case annotate typeEnv globalEnv xs okSig of
Left err ->
-- TODO: Replace this with a single call to evalError (which already checks the execution mode)
case contextExecMode ctx of
Check -> pure (evalError ctx (joinLines (machineReadableErrorStrings fppl err)) Nothing)
_ -> pure (evalError ctx (show err) (xobjInfo xobj))
Right ok -> pure (ctx, Right ok)
primitiveDefmodule :: VariadicPrimitiveCallback
primitiveDefmodule xobj ctx@(Context env i tenv pathStrings _ _ _ _) (XObj (Sym (SymPath [] moduleName) _) si _ : innerExpressions) =
-- N.B. The `envParent` rewrite at the end of this line is important!
-- lookups delve into parent envs by default, which is normally what we want, but in this case it leads to problems
-- when submodules happen to share a name with an existing module or type at the global level.
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
either (const (defineNewModule emptyMeta)) updateExistingModule (E.searchValueBinder ((fromRight env (E.getInnerEnv env pathStrings)) {envParent = Nothing}) (SymPath [] moduleName))
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
>>= defineModuleBindings
>>= \(newCtx, result) ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
let updater c = (c {contextInternalEnv = (E.parent =<< contextInternalEnv c)})
in case result of
Left err -> pure (newCtx, Left err)
Right _ -> pure (updater (popModulePath newCtx), dynamicNil)
where
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
--------------------------------------------------------------------------------
-- Update an existing module by modifying its environment parents and updating the current context path.
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
updateExistingModule :: Binder -> IO (Context, Either EvalError XObj)
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
updateExistingModule (Binder _ (XObj (Mod innerEnv _) _ _)) =
let updateContext =
replacePath' (contextPath ctx ++ [moduleName])
. replaceInternalEnv' (innerEnv {envParent = i})
in pure (updateContext ctx, dynamicNil)
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
updateExistingModule (Binder meta (XObj (Lst [XObj MetaStub _ _, _]) _ _)) =
defineNewModule meta
updateExistingModule _ =
pure (throwErr (ModuleRedefinition moduleName) ctx (xobjInfo xobj))
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
--------------------------------------------------------------------------------
-- Define a brand new module with a context's current environments as its parents.
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
defineNewModule :: MetaData -> IO (Context, Either EvalError XObj)
defineNewModule meta =
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
pure (fromRight ctx (updater ctx), dynamicNil)
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
where
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
moduleDefs = E.new (Just (fromRight env (E.getInnerEnv env pathStrings))) (Just moduleName)
moduleTypes = E.new (Just tenv) (Just moduleName)
newModule = XObj (Mod moduleDefs moduleTypes) si (Just ModuleTy)
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
updater = \c ->
insertInGlobalEnv' (markQualified (SymPath pathStrings moduleName)) (Binder meta newModule) c
>>= pure . replaceInternalEnv' (moduleDefs {envParent = i})
>>= pure . replacePath' (contextPath ctx ++ [moduleName])
--------------------------------------------------------------------------------
-- Define bindings for the module.
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
defineModuleBindings :: (Context, Either EvalError XObj) -> IO (Context, Either EvalError XObj)
defineModuleBindings (context, Left e) = pure (context, Left e)
defineModuleBindings (context, _) =
foldM step (context, dynamicNil) innerExpressions
step :: (Context, Either EvalError XObj) -> XObj -> IO (Context, Either EvalError XObj)
step (ctx', Left e) _ = pure (ctx', Left e)
step (ctx', Right _) expressions =
2020-12-22 19:44:44 +03:00
macroExpand ctx' expressions
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
>>= \(ctx'', res) -> case res of
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
Left err -> pure (ctx'', Left err)
Right r -> evalDynamic ResolveLocal ctx'' r
primitiveDefmodule _ ctx (x : _) =
pure (throwErr (DefmoduleContainsNonSymbol x) ctx (xobjInfo x))
primitiveDefmodule xobj ctx [] =
pure (throwErr DefmoduleNoArgs ctx (xobjInfo xobj))
2017-12-16 16:54:26 +03:00
-- | "NORMAL" COMMANDS (just like the ones in Command.hs, but these need access to 'eval', etc.)
-- | Command for loading a Carp file.
commandLoad :: VariadicCommandCallback
commandLoad ctx [xobj@(XObj (Str path) i _), XObj (Str toLoad) _ _] =
loadInternal ctx xobj path i (Just toLoad) DoesReload
commandLoad ctx [XObj (Str _) _ _, x] =
pure $ throwErr (loadInvalidArgs [x]) ctx (xobjInfo x)
commandLoad ctx [x, _] =
pure $ throwErr (loadInvalidArgs [x]) ctx (xobjInfo x)
commandLoad ctx [xobj@(XObj (Str path) i _)] =
loadInternal ctx xobj path i Nothing DoesReload
commandLoad ctx x =
pure $ throwErr (loadInvalidArgs x) ctx Nothing
2020-05-05 16:00:57 +03:00
commandLoadOnce :: VariadicCommandCallback
commandLoadOnce ctx [xobj@(XObj (Str path) i _), XObj (Str toLoad) _ _] =
loadInternal ctx xobj path i (Just toLoad) Frozen
commandLoadOnce ctx [XObj (Str _) _ _, x] =
pure $ throwErr (loadOnceInvalidArgs [x]) ctx (xobjInfo x)
commandLoadOnce ctx [x, _] =
pure $ throwErr (loadOnceInvalidArgs [x]) ctx (xobjInfo x)
commandLoadOnce ctx [xobj@(XObj (Str path) i _)] =
loadInternal ctx xobj path i Nothing Frozen
commandLoadOnce ctx x =
pure $ throwErr (loadOnceInvalidArgs x) ctx Nothing
2020-05-05 16:00:57 +03:00
loadInternal :: Context -> XObj -> String -> Maybe Info -> Maybe String -> ReloadMode -> IO (Context, Either EvalError XObj)
loadInternal ctx xobj path i fileToLoad reloadMode = do
2020-03-28 16:32:41 +03:00
let proj = contextProj ctx
libDir <- liftIO $ cachePath $ projectLibDir proj
let relativeTo = case i of
Just ii ->
case infoFile ii of
"REPL" -> "."
file -> takeDirectory file
Nothing -> "."
2020-03-28 16:32:41 +03:00
carpDir = projectCarpDir proj
fullSearchPaths =
path :
(relativeTo </> path) :
map (</> path) (projectCarpSearchPaths proj) -- the path from the file that contains the '(load)', or the current directory if not loading from a file (e.g. the repl)
++ [carpDir </> "core" </> path] -- user defined search paths
++ [libDir </> path]
firstM _ [] = pure Nothing
firstM p (x : xs) = do
2020-03-28 16:32:41 +03:00
q <- p x
if q
then pure $ Just x
2020-03-28 16:32:41 +03:00
else firstM p xs
existingPath <- liftIO $ firstM doesFileExist fullSearchPaths
case existingPath of
Nothing ->
if '@' `elem` path
then tryInstall path
else pure $ invalidPath ctx path
2020-03-28 16:32:41 +03:00
Just firstPathFound ->
do
canonicalPath <- liftIO (canonicalizePath firstPathFound)
2020-12-22 19:44:44 +03:00
fileThatLoads <- liftIO (canonicalizePath $ maybe "" infoFile i)
if canonicalPath == fileThatLoads
then pure $ cantLoadSelf ctx path
else do
let alreadyLoaded = projectAlreadyLoaded proj ++ frozenPaths proj
if canonicalPath `elem` alreadyLoaded
then pure (ctx, dynamicNil)
else do
contents <- liftIO $ slurp canonicalPath
let files = projectFiles proj
files' =
2020-12-22 19:44:44 +03:00
if canonicalPath `elem` map fst files
then files
else files ++ [(canonicalPath, reloadMode)]
prevStack = projectLoadStack proj
proj' =
proj
{ projectFiles = files',
projectAlreadyLoaded = canonicalPath : alreadyLoaded,
projectLoadStack = canonicalPath : prevStack
}
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
newCtx <- liftIO $ executeString True False (replaceProject ctx proj') contents canonicalPath
pure (replaceProject newCtx (contextProj newCtx) {projectLoadStack = prevStack}, dynamicNil)
2018-06-25 15:30:12 +03:00
where
2020-05-05 16:00:57 +03:00
frozenPaths proj =
if projectForceReload proj
then [] -- No paths are Frozen when the "force reload" project setting is true.
else map fst $ filter (isFrozen . snd) (projectFiles proj)
2020-05-05 16:00:57 +03:00
isFrozen Frozen = True
isFrozen _ = False
invalidPath ctx' path' =
throwErr (LoadFileNotFound path') ctx' (xobjInfo xobj)
invalidPathWith ctx' path' stderr cleanup cleanupPath = do
_ <- liftIO $ when cleanup (removeDirectoryRecursive cleanupPath)
pure $
throwErr (LoadGitFailure path' stderr) ctx' (xobjInfo xobj)
2020-11-26 00:12:57 +03:00
replaceC _ _ [] = []
replaceC c s (a : b) = if a == c then s ++ replaceC c s b else a : replaceC c s b
cantLoadSelf ctx' path' =
throwErr (LoadRecursiveLoad path') ctx' (xobjInfo xobj)
tryInstall path' =
let split = splitOn "@" path'
in tryInstallWithCheckout (joinWith "@" (init split)) (last split)
2018-07-11 12:41:22 +03:00
fromURL url =
let split = splitOn "/" (replaceC ':' "_COLON_" url)
first = head split
in if first `elem` ["https_COLON_", "http_COLON_"]
then joinWith "/" (tail (tail split))
else
if '@' `elem` first
then joinWith "/" (joinWith "@" (tail (splitOn "@" first)) : tail split)
else url
tryInstallWithCheckout path' toCheckout = do
2018-06-25 15:30:12 +03:00
let proj = contextProj ctx
fpath <- liftIO $ cachePath $ projectLibDir proj </> fromURL path' </> toCheckout
2019-04-30 17:21:06 +03:00
cur <- liftIO getCurrentDirectory
pathExists <- liftIO $ doesPathExist fpath
let cleanup = not pathExists
2019-10-30 14:16:14 +03:00
_ <- liftIO $ createDirectoryIfMissing True fpath
2018-06-25 15:30:12 +03:00
_ <- liftIO $ setCurrentDirectory fpath
(_, txt, _) <- liftIO $ readProcessWithExitCode "git" ["rev-parse", "--abbrev-ref=loose", "HEAD"] ""
if txt == "HEAD\n"
then do
_ <- liftIO $ setCurrentDirectory cur
doGitLoad path' fpath
else do
_ <- liftIO $ readProcessWithExitCode "git" ["init"] ""
_ <- liftIO $ readProcessWithExitCode "git" ["remote", "add", "origin", path'] ""
(x0, _, stderr0) <- liftIO $ readProcessWithExitCode "git" ["fetch", "--all", "--tags"] ""
case x0 of
ExitFailure _ -> do
_ <- liftIO $ setCurrentDirectory cur
invalidPathWith ctx path' stderr0 cleanup fpath
ExitSuccess -> do
(x1, _, stderr1) <- liftIO $ readProcessWithExitCode "git" ["checkout", toCheckout] ""
_ <- liftIO $ setCurrentDirectory cur
case x1 of
ExitSuccess -> doGitLoad path' fpath
ExitFailure _ -> invalidPathWith ctx path' stderr1 cleanup fpath
doGitLoad path' fpath =
case fileToLoad of
Just file -> commandLoad ctx [XObj (Str (fpath </> file)) Nothing Nothing]
Nothing ->
-- were guessing what file to use here
let fName = last (splitOn "/" path')
realName' =
if ".git" `isSuffixOf` fName
then take (length fName - 4) fName
else fName
realName =
if ".carp" `isSuffixOf` realName'
then realName'
else realName' ++ ".carp"
fileToLoad' = fpath </> realName
mainToLoad = fpath </> "main.carp"
in do
(newCtx, res) <- commandLoad ctx [XObj (Str fileToLoad') Nothing Nothing]
case res of
ret@(Right _) -> pure (newCtx, ret)
Left _ -> commandLoad ctx [XObj (Str mainToLoad) Nothing Nothing]
2018-06-25 15:30:12 +03:00
2017-12-16 16:54:26 +03:00
-- | Load several files in order.
loadFiles :: Context -> [FilePath] -> IO Context
2020-05-05 16:00:57 +03:00
loadFiles = loadFilesExt commandLoad
loadFilesOnce :: Context -> [FilePath] -> IO Context
loadFilesOnce = loadFilesExt commandLoadOnce
loadFilesExt :: VariadicCommandCallback -> Context -> [FilePath] -> IO Context
2020-12-22 19:44:44 +03:00
loadFilesExt loadCmd = foldM load
where
load :: Context -> FilePath -> IO Context
load ctx file = do
(newCtx, ret) <- loadCmd ctx [XObj (Str file) Nothing Nothing]
case ret of
Left err -> throw (EvalException err)
Right _ -> pure newCtx
2017-12-16 16:54:26 +03:00
-- | Command for reloading all files in the project (= the files that has been loaded before).
commandReload :: NullaryCommandCallback
commandReload ctx = do
2020-03-28 16:32:41 +03:00
let paths = projectFiles (contextProj ctx)
2020-05-05 16:00:57 +03:00
f :: Context -> (FilePath, ReloadMode) -> IO Context
f context (_, Frozen) | not (projectForceReload (contextProj context)) = pure context
f context (filepath, _) =
do
let proj = contextProj context
alreadyLoaded = projectAlreadyLoaded proj
if filepath `elem` alreadyLoaded
then pure context
else do
contents <- slurp filepath
let proj' = proj {projectAlreadyLoaded = filepath : alreadyLoaded}
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
executeString False False (replaceProject context proj') contents filepath
2020-03-28 16:32:41 +03:00
newCtx <- liftIO (foldM f ctx paths)
pure (newCtx, dynamicNil)
2017-12-16 16:54:26 +03:00
-- | Command for expanding a form and its macros.
commandExpand :: UnaryCommandCallback
2020-12-22 19:44:44 +03:00
commandExpand = macroExpand
-- | This function will show the resulting C code from an expression.
-- | i.e. (Int.+ 2 3) => "_0 = 2 + 3"
commandC :: UnaryCommandCallback
commandC ctx xobj = do
(newCtx, result) <- expandAll (evalDynamic ResolveLocal) ctx xobj
2020-03-28 16:32:41 +03:00
case result of
Left err -> pure (newCtx, Left err)
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
Right expanded -> do
(_, annotated) <- annotateWithinContext newCtx expanded
case annotated of
Left err -> pure $ evalError newCtx (show err) (xobjInfo xobj)
2020-03-28 16:32:41 +03:00
Right (annXObj, annDeps) ->
do
let cXObj = printC annXObj
cDeps = concatMap printC annDeps
c = cDeps ++ cXObj
liftIO (putStr c)
pure (newCtx, dynamicNil)
2021-09-17 06:57:50 +03:00
-- | This function will return the compiled AST.
commandExpandCompiled :: UnaryCommandCallback
commandExpandCompiled ctx xobj = do
(newCtx, result) <- expandAll (evalDynamic ResolveLocal) ctx xobj
case result of
Left err -> pure (newCtx, Left err)
Right expanded -> do
(_, annotated) <- annotateWithinContext newCtx expanded
case annotated of
Left err -> pure $ evalError newCtx (show err) (xobjInfo xobj)
Right (annXObj, _) -> pure (newCtx, Right annXObj)
-- | Helper function for commandC
printC :: XObj -> String
printC xobj =
case checkForUnresolvedSymbols xobj of
Left e ->
strWithColor Red (show e ++ ", can't print resulting code.\n")
Right _ ->
strWithColor Green (toC All (Binder emptyMeta xobj))
buildMainFunction :: XObj -> XObj
buildMainFunction xobj =
XObj
( Lst
[ XObj (Defn Nothing) di Nothing,
XObj (Sym (SymPath [] "main") Symbol) di Nothing,
XObj (Arr []) di Nothing,
XObj
( Lst
[ XObj Do di Nothing,
case xobjTy xobj of
Nothing -> error "buildmainfunction"
Just UnitTy -> xobj
Just (RefTy _ _) ->
XObj
(Lst [XObj (Sym (SymPath [] "println*") Symbol) di Nothing, xobj])
di
(Just UnitTy)
Just _ ->
XObj
( Lst
[ XObj (Sym (SymPath [] "println*") Symbol) di Nothing,
XObj
(Lst [XObj Ref di Nothing, xobj])
di
(Just UnitTy)
]
)
di
(Just UnitTy),
XObj (Num IntTy 0) di Nothing
]
)
di
Nothing
]
)
di
(Just (FuncTy [] UnitTy StaticLifetimeTy))
where
di = Just dummyInfo
2020-03-06 20:16:22 +03:00
primitiveDefdynamic :: BinaryPrimitiveCallback
primitiveDefdynamic _ ctx (XObj (Sym (SymPath [] name) _) _ _) value = do
(newCtx, result) <- evalDynamic ResolveLocal ctx value
2020-03-28 16:32:41 +03:00
case result of
Left err -> pure (newCtx, Left err)
2020-03-28 16:32:41 +03:00
Right evaledBody ->
dynamicOrMacroWith newCtx (\path -> [XObj DefDynamic Nothing Nothing, XObj (Sym path Symbol) Nothing Nothing, evaledBody]) DynamicTy name value
primitiveDefdynamic _ ctx notName _ =
pure (throwErr (DefnDynamicInvalidName notName) ctx (xobjInfo notName))
2020-03-06 20:16:22 +03:00
2020-03-28 16:32:41 +03:00
specialCommandSet :: Context -> [XObj] -> IO (Context, Either EvalError XObj)
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
specialCommandSet ctx [orig@(XObj (Sym path@(SymPath _ _) _) _ _), val] =
let lookupInternal =
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
maybe (Left "") Right (contextInternalEnv ctx)
>>= \e ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
unwrapErr (E.searchValueBinder e path)
>>= \binder -> pure (binder, setInternal, e)
lookupGlobal =
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
Right (contextGlobalEnv ctx)
>>= \e ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
unwrapErr (E.searchValueBinder e path)
>>= \binder -> pure (binder, setGlobal, e)
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
in either
((const (pure $ (throwErr (SetVarNotFound orig) ctx (xobjInfo orig)))))
(\(binder', setter', env') -> evalAndSet binder' setter' env')
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
(lookupInternal <> lookupGlobal)
where
evalAndSet :: Binder -> (Context -> Env -> Either EvalError XObj -> Binder -> IO (Context, Either EvalError XObj)) -> Env -> IO (Context, Either EvalError XObj)
evalAndSet binder setter env =
case xobjTy (binderXObj binder) of
-- don't type check dynamic or untyped bindings
-- TODO: Figure out why untyped cases are sometimes coming into set!
Just DynamicTy -> handleUnTyped
Nothing -> handleUnTyped
_ ->
evalDynamic ResolveLocal ctx val
>>= \(newCtx, result) ->
case result of
Right evald -> typeCheckValueAgainstBinder newCtx evald binder >>= \(nctx, typedVal) -> setter nctx env typedVal binder
left -> pure (newCtx, left)
where
handleUnTyped :: IO (Context, Either EvalError XObj)
handleUnTyped =
evalDynamic ResolveLocal ctx val
>>= \(newCtx, result) -> setter newCtx env result binder
setGlobal :: Context -> Env -> Either EvalError XObj -> Binder -> IO (Context, Either EvalError XObj)
setGlobal ctx' env value binder =
pure $ either (failure ctx' orig) (success ctx') value
where
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
success c xo = (replaceGlobalEnv c (setStaticOrDynamicVar path env binder xo), dynamicNil)
setInternal :: Context -> Env -> Either EvalError XObj -> Binder -> IO (Context, Either EvalError XObj)
setInternal ctx' env value binder =
pure $ either (failure ctx' orig) (success ctx') value
where
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
success c xo = (replaceInternalEnv c (setStaticOrDynamicVar path env binder xo), dynamicNil)
2020-11-26 00:12:57 +03:00
specialCommandSet ctx [notName, _] =
pure (throwErr (SetInvalidVarName notName) ctx (xobjInfo notName))
2020-03-28 16:32:41 +03:00
specialCommandSet ctx args =
pure (throwErr (setInvalidArgs args) ctx (if null args then Nothing else xobjInfo (head args)))
2020-03-18 00:51:00 +03:00
-- | Convenience method for signifying failure in a given context.
failure :: Context -> XObj -> EvalError -> (Context, Either EvalError a)
failure ctx orig err = evalError ctx (show err) (xobjInfo orig)
-- | Given a context, value XObj and an existing binder, check whether or not
-- the given value has a type matching the binder's in the given context.
2020-12-22 19:44:44 +03:00
typeCheckValueAgainstBinder :: Context -> XObj -> Binder -> IO (Context, Either EvalError XObj)
typeCheckValueAgainstBinder ctx val binder = do
refactor: Context and Qualify (#1170) * refactor: move Context updates into functions Previously, we had a lot of instances of updating Context records directly, replacing environments where needed. This commit replaces those hand-written record setting instances with functions, which should allow us to more gracefully abstract over any preprocessing we should have to do and help ensure we're updating contexts in the right way. * refactor: replace inline context manipulation in primitives Like the commit that altered Eval before it, this commit leverages Context functions to remove a bunch of inline record field setting code and direct env manipulation. * refactor: replace generic binder lookups with contextual ones * refactor: move true and false XObjs into Obj.hs Previously, trueXObj and falseXObj were defined in Commands.hs, but since they're just literal constructed XObj values, I feel Obj.hs is a more appropriate home for them and makes them more widely accessible to other modules without needing to import Commands. * refactor: model symbol qualification requirements at typelevel This commit refactors the Qualify module to express symbol qualification at type level. In the past, all functions operated on SymPaths. In some cases, the functions operated on paths that *were not yet qualified* and so the functions would perform qualification inline. Contrarily, other functions like define received XObjs (from another part of the codebase entirely!) that were already fully qualified, and so it would be a grave mistake to re-qualify them. In the general case, it's difficult or impossible to tell across modules whether or not a given SymPath is coming in qualified or unqualified, which can easily lead to mistakes of double-qualification, e.g. transforming `Foo.bar` into `Foo.Foo.bar`. Modelling qualification in the type system enables us to avoid the problem by distinguishing between unqualified and qualified paths. A function receiving an SymPath can safely qualify it, whereas a function receiving a QualifiedPath should not further qualify the path. This helps better express and ensure constraints across modules. In addition, this commit also refactors a few functions where there was opportunity to do so. * refactor: remove eval call from `doc` This can lead to problems where a doc call intended to be evaluated later (in a macro body) is evaluated *immediately* resulting in a binding being added to the wrong scope (see the function reverse in core). The reason this behavior crops up now is that a special case for evaluating module contexts was removed last commit--this special case caused problems of its own, and the real root of things stems from the unnecessary eval call. Generally, evaling a doc call provides no benefit other than making evaluation of the meta set immediate in the repl, which is easy enough for one to do on one's own by calling eval where needed. * refactor: use do notation to clarify case qualification * refactor: rename runQualified to unQualified @eriksvedang pointed out the `run` prefix typically denotes a monad. As `Qualified` is not monadic (no monad instance defined) we drop the `r` to ensure we don't mislead readers. * refactor: convert a few more binds to do notation Do notation is generally clearer in cases where we use mapM, etc. We can also leverage liftM frequently in the qualification functions to transform a Qualified xobj back into an xobj for further use. * refactor: temporarily restore special case in meta set Meta set disallows setting the meta of a prefixed, absolute path such as `Foo.bar`. It only allows relative, unqualified paths `bar` and uses the current context to determine the appropriate module. If we eventually throw and error from envInsertAt, we can remove this special case. I intend to do that later, but for now we'll keep the special case to make for a more pleasant user experience.
2021-02-14 23:53:42 +03:00
(ctx', typedValue) <- annotateWithinContext ctx val
pure $ case typedValue of
2020-11-26 00:12:57 +03:00
Right (val', _) -> go ctx' binderTy val'
Left err -> (ctx', Left err)
where
2020-12-22 19:44:44 +03:00
path = getPath (binderXObj binder)
binderTy = xobjTy (binderXObj binder)
typeErr x = throwErr (SetTypeMismatch path (fromJust (xobjTy x)) (fromJust binderTy)) ctx (xobjInfo x)
go ctx'' (Just DynamicTy) x = (ctx'', Right x)
go ctx'' t x@(XObj _ _ t') = if t == t' then (ctx'', Right x) else typeErr x
-- | Sets a variable, checking whether or not it is static or dynamic, and
-- assigns an appropriate type to the variable.
-- Returns a new environment containing the assignment.
setStaticOrDynamicVar :: SymPath -> Env -> Binder -> XObj -> Env
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
setStaticOrDynamicVar path@(SymPath _ name) env binder value =
case binder of
(Binder meta (XObj (Lst (def@(XObj Def _ _) : sym : _)) _ t)) ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
fromRight env (E.insert env path (Binder meta (XObj (Lst [def, sym, value]) (xobjInfo value) t)))
(Binder meta (XObj (Lst (defdy@(XObj DefDynamic _ _) : sym : _)) _ _)) ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
fromRight env (E.insert env path (Binder meta (XObj (Lst [defdy, sym, value]) (xobjInfo value) (Just DynamicTy))))
(Binder meta (XObj (Lst (lett@(XObj LocalDef _ _) : sym : _)) _ t)) ->
Refactor: clean up Env module, store type environments in modules (#1207) * refactor: major environment mgmt refactor This big refactor primarily changes two things in terms of behavior: 1. Stores a SymPath on concretely named (non-generic) struct types; before we stored a string. 2. The SymPath mentioned in (1.) designates where the struct is stored in the current environment chain. Modules now carry a local type environment in addition to their local value environments. Any types defined in the module are added to this environment rather than the global type environment. To resolve a type such as `Foo.Bar` we now do the following: - Search the *global value environment* for the Foo module. - Get the type environment stored in the Foo module. - Search for Bar in the Foo module's type environment. Additionally, this commit eliminates the Lookup module entirely and refactors the Env module to handle all aspects of environment management in hopefully a more reusable fashion. I also took the opportunity to refactor primitiveDeftype in Primitives and qualifySym in Qualify, both of which were hefty functions that I found difficult to grok and needed refactoring anyway as a result of lookup changes (lookups now return an Either instead of a Maybe). Subsequent commits will clean up and clarify this work further. This does include one minor regression. Namely, an implementation of `hash` in core/Color that was maximally generic now needs type casting. * refactor: clean up recent Env changes This commit removes some redundant functions, unifies some logic, and renames some routines across the Env module in efforts to make it cleaner. Call sites have been updated accordingly. * chore: format code with ormolu * fix: update lookup tests Changes references to renamed functions in the Env module. * refactor: style + additional improvements from eriksvedang@ - Rename arrayTy -> arrayTyA in ArrayTemplates.hs to disambiguate. - Add maybeId util function. - Remove commented code. - Refactor a few functions for readability. * fix: fix type inference regression Recent commits introduced one minor regression whereby an instance of type inference in core/Color.carp no longer worked and required explicit type annotation. The problem ultimately had to do with qualification: - Prior to the recent changes, type inference worked because the call in question was qualified to Color.Id.get-tag, fixing the type. - Failing to copy over a local envs Use modules to function envs resulted in finding more than just Color.Id.get-tag for this instance. We now copy use modules over to function envs generated during qualification to ensure we resolve to Use'd definitions before more general cases. Similarly, I made a small change to primitiveUse to support contextual use calls (e.g. the `(use Id)` in Color.carp, which really means `(use Color.Id)`) * chore: Update some clarificatory comments * chore: fix inline comment
2021-05-19 20:20:48 +03:00
fromRight (error "FAILED!") (E.replaceInPlace env name (Binder meta (XObj (Lst [lett, sym, value]) (xobjInfo value) t)))
-- shouldn't happen, errors are thrown at call sites.
-- TODO: Return an either here to propagate error.
_ -> env
primitiveEval :: UnaryPrimitiveCallback
primitiveEval _ ctx val = do
2020-03-09 17:50:52 +03:00
-- primitives dont evaluate their arguments, so this needs to double-evaluate
(newCtx, arg) <- evalDynamic ResolveLocal ctx val
2020-03-09 17:50:52 +03:00
case arg of
Left err -> pure (newCtx, Left err)
2020-04-10 20:52:01 +03:00
Right evald -> do
Various submodule fixes (#1078) * fix: don't set the inner env to globals in type mods Previously, we set the inner environment of a type generated module to the global env in cases where the overarching context didn't have an inner env. This leads to problems where by the recognition of modules is inconsistent, and one can't use the names of types as submodules in certain circumstances. This commit fixes that issue. * refactor: refactor primitiveDefmodule This refactor fixes a issues with meta information on submodules, for instance, sigs on submodule functions used to result in a compiler error about ambiguous identifiers. This fixes that. Unfortunately, I don't have a precise idea about what exactly was wrong with the original definition of this function. My suspicion is that the recursion originally altered submodule paths in the wrong way, but I'm not certain. In any case it's fixed. * fix: ensure macros are expanded in the correct module Previously, macro expansions folded over all forms after the top level form, without performing any context updates on encountered `defmodules`. This created an issue in which macro calls that produced new bindings, "meta stubs", were *hoisted* out of submodules and into the top-level module, creating duplicate definitions. This commit fixes that issue by adding a special case for defmodule in macroExpand. * fix: ensure submodules and globals don't conflict Previously, our module lookups during new module definition always eventually fell back to the global environment, which caused submodules that happen to share a name with a global module to be confused with the global module. This change fixes that, so now one can define both `Dynamic` (global) and `Foo.Dynamic` without issue. * fix: remove old prefixes from vector tests Commit 7b7cb5d1e replaced /= with a generic function. However, the vector tests still called the specific Vector variants of this function, which were removed when the generic was introduced. After recent changes, these calls are now (correctly) identified as erroneous. My guess is that they only worked in the past because of problems with our lookups. * chore: format code
2020-12-18 23:45:28 +03:00
(newCtx', expanded) <- macroExpand newCtx evald
2020-04-10 20:52:01 +03:00
case expanded of
Left err -> pure (newCtx', Left err)
2020-11-07 13:51:22 +03:00
Right ok -> do
(finalCtx, res) <- evalDynamic ResolveLocal newCtx' ok
pure $ case res of
Left (HasStaticCall x i) -> throwErr (StaticCall x) ctx i
_ -> (finalCtx, res)
2020-03-09 17:50:52 +03:00
2020-04-10 18:31:51 +03:00
dynamicOrMacro :: Context -> Obj -> Ty -> String -> XObj -> XObj -> IO (Context, Either EvalError XObj)
dynamicOrMacro ctx pat ty name params body = do
(ctx', exp) <- macroExpand ctx body
case exp of
Right expanded ->
dynamicOrMacroWith ctx' (\path -> [XObj pat Nothing Nothing, XObj (Sym path Symbol) Nothing Nothing, params, expanded]) ty name body
2020-11-26 00:12:57 +03:00
Left _ -> pure (ctx, exp)
2020-04-10 18:31:51 +03:00
primitiveDefndynamic :: TernaryPrimitiveCallback
primitiveDefndynamic _ ctx (XObj (Sym (SymPath [] name) _) _ _) params body =
2020-04-10 18:31:51 +03:00
dynamicOrMacro ctx Dynamic DynamicTy name params body
primitiveDefndynamic _ ctx notName _ _ =
2020-04-10 18:31:51 +03:00
argumentErr ctx "defndynamic" "a name" "first" notName
primitiveDefmacro :: TernaryPrimitiveCallback
primitiveDefmacro _ ctx (XObj (Sym (SymPath [] name) _) _ _) params body =
2020-04-10 18:31:51 +03:00
dynamicOrMacro ctx Macro MacroTy name params body
primitiveDefmacro _ ctx notName _ _ =
2020-04-10 18:31:51 +03:00
argumentErr ctx "defmacro" "a name" "first" notName