2020-02-13 17:58:06 +03:00
{- # LANGUAGE LambdaCase # -}
2020-12-02 18:33:37 +03:00
2017-12-01 12:36:14 +03:00
module Eval where
2017-06-26 12:15:03 +03:00
2020-12-02 18:33:37 +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
2020-10-08 00:25:20 +03:00
import Control.Applicative
2020-03-06 20:16:22 +03:00
import Control.Exception
2017-10-18 13:25:42 +03:00
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 )
2020-03-11 14:23:11 +03:00
import Data.Foldable ( foldlM , foldrM )
2021-06-08 08:39:06 +03:00
import Data.List ( foldl' , isSuffixOf )
2020-03-06 20:16:22 +03:00
import Data.List.Split ( splitOn , splitWhen )
2020-12-02 18:33:37 +03:00
import Data.Maybe ( fromJust , fromMaybe , isJust )
2017-12-01 12:36:14 +03:00
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
2021-03-09 21:24:02 +03:00
import EvalError
2017-12-15 18:37:03 +03:00
import Expand
2021-06-16 22:41:58 +03:00
import Forms
2020-12-02 18:33:37 +03:00
import Infer
import Info
2020-12-16 17:53:55 +03:00
import qualified Map
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
import Project
import Qualify
2021-01-25 23:18:01 +03:00
import qualified Set
2020-12-02 18:33:37 +03:00
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.
2020-12-02 18:33:37 +03:00
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 ]
2021-05-25 09:08:30 +03:00
deriving ( Show )
2020-10-12 01:43:15 +03:00
2020-12-22 17:53:55 +03:00
data Resolver
= ResolveGlobal
| ResolveLocal
2021-06-08 08:39:06 +03:00
type Evaluator = [ XObj ] -> IO ( Context , Either EvalError XObj )
Eval: Add a flag for preferring dynamic/static bindings
In certain contexts, naked identifiers (those without a qualifying path)
can be resolved to either global static bindings *or* dynamic bindings.
In the past, we've always preferred dynamic bindings first in the
evaluator, then, if we failed to find them, fell back to global
bindings.
However, for the s-expr command for getting the s-expressions of
bindings, dynamic bindings can conflict with global bindings for
interfaces functions etc.
This commit makes it possible for callers into the dynamic evaluator to
specify whether they want to prioritize dynamic or global lookups. All
internal calls into eval from eval itself use whatever flag was passed
in at the top level. For parity, all existing calls to eval prefer
Dynamic bindings *except* for invocations of the s-expr command. That
acknowledged, we should revisit calls to eval and double check that the
lookup priorities make sense (for instance, primitives should likely
prefer dynamic bindings while identifiers in static terms like `defn`
bodies should prefer global/static bindings by default.
2020-10-08 01:41:55 +03:00
-- Prefer dynamic bindings
2020-12-22 17:53:55 +03:00
evalDynamic :: Resolver -> Context -> XObj -> IO ( Context , Either EvalError XObj )
evalDynamic resolver ctx xobj = eval ctx xobj PreferDynamic resolver
Eval: Add a flag for preferring dynamic/static bindings
In certain contexts, naked identifiers (those without a qualifying path)
can be resolved to either global static bindings *or* dynamic bindings.
In the past, we've always preferred dynamic bindings first in the
evaluator, then, if we failed to find them, fell back to global
bindings.
However, for the s-expr command for getting the s-expressions of
bindings, dynamic bindings can conflict with global bindings for
interfaces functions etc.
This commit makes it possible for callers into the dynamic evaluator to
specify whether they want to prioritize dynamic or global lookups. All
internal calls into eval from eval itself use whatever flag was passed
in at the top level. For parity, all existing calls to eval prefer
Dynamic bindings *except* for invocations of the s-expr command. That
acknowledged, we should revisit calls to eval and double check that the
lookup priorities make sense (for instance, primitives should likely
prefer dynamic bindings while identifiers in static terms like `defn`
bodies should prefer global/static bindings by default.
2020-10-08 01:41:55 +03:00
-- Prefer global bindings
2020-12-22 17:53:55 +03:00
evalStatic :: Resolver -> Context -> XObj -> IO ( Context , Either EvalError XObj )
evalStatic resolver ctx xobj = eval ctx xobj PreferGlobal resolver
Eval: Add a flag for preferring dynamic/static bindings
In certain contexts, naked identifiers (those without a qualifying path)
can be resolved to either global static bindings *or* dynamic bindings.
In the past, we've always preferred dynamic bindings first in the
evaluator, then, if we failed to find them, fell back to global
bindings.
However, for the s-expr command for getting the s-expressions of
bindings, dynamic bindings can conflict with global bindings for
interfaces functions etc.
This commit makes it possible for callers into the dynamic evaluator to
specify whether they want to prioritize dynamic or global lookups. All
internal calls into eval from eval itself use whatever flag was passed
in at the top level. For parity, all existing calls to eval prefer
Dynamic bindings *except* for invocations of the s-expr command. That
acknowledged, we should revisit calls to eval and double check that the
lookup priorities make sense (for instance, primitives should likely
prefer dynamic bindings while identifiers in static terms like `defn`
bodies should prefer global/static bindings by default.
2020-10-08 01:41:55 +03:00
2017-12-15 18:22:16 +03:00
-- | Dynamic (REPL) evaluation of XObj:s (s-expressions)
2020-08-25 11:31:53 +03:00
-- Note: You might find a bunch of code of the following form both here and in
-- macroExpand:
--
2020-11-24 08:09:15 +03:00
-- pure (ctx, do res <- <something>
-- Right <something else with res>)
2020-08-25 11:31:53 +03:00
--
-- 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 compiler’ s 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.)
2020-12-22 17:53:55 +03:00
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
2020-12-02 18:33:37 +03:00
Lst body -> eval' body
2020-12-01 02:11:01 +03:00
Sym spath @ ( SymPath p n ) _ ->
2020-12-02 18:33:37 +03:00
pure $
2020-12-22 17:53:55 +03:00
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 )
2021-05-25 09:08:30 +03:00
ResolveLocal -> unwrapLookup ( tryAllLookups preference )
2020-12-22 17:53:55 +03:00
where
checkStatic v @ ( _ , Right ( XObj ( Lst ( ( XObj obj _ _ ) : _ ) ) _ _ ) ) =
if isResolvableStaticObj obj
2020-12-24 18:20:07 +03:00
then pure ( ctx , Left ( HasStaticCall xobj info ) )
else pure v
2020-12-22 17:53:55 +03:00
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.
2020-12-24 18:20:07 +03:00
unwrapLookup =
2020-12-22 17:53:55 +03:00
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 )
2021-05-25 09:08:30 +03:00
-- 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
2021-05-25 09:08:30 +03:00
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 =
2021-05-25 09:08:30 +03:00
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 =
2021-05-25 09:08:30 +03:00
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 )
2020-12-02 18:33:37 +03:00
tryDynamicLookup =
2021-05-25 09:08:30 +03:00
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 =
2021-05-25 09:08:30 +03:00
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 )
2020-12-02 18:33:37 +03:00
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)))
2020-12-02 18:33:37 +03:00
( 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
2020-12-02 18:33:37 +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
tryLookup :: SymPath -> Maybe ( Context , Either EvalError XObj )
2020-12-02 18:33:37 +03:00
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 )
2020-12-07 09:06:32 +03:00
>>= \ ( Binder meta found ) -> checkPrivate meta found
2020-12-02 18:33:37 +03:00
)
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 )
2020-12-07 09:06:32 +03:00
>>= \ ( Binder _ found ) -> pure ( ctx , Right ( resolveDef found ) )
2020-12-02 18:33:37 +03:00
)
2021-01-12 14:52:54 +03:00
<|> ( 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 ) )
2021-01-12 14:52:54 +03:00
>>= \ ( Binder meta found ) -> checkPrivate meta found
)
2021-01-25 23:18:01 +03:00
( Set . toList ( envUseModules ( contextGlobalEnv ctx ) ) )
2021-01-12 14:52:54 +03:00
)
)
2020-12-02 18:33:37 +03:00
checkPrivate meta found =
pure $
if metaIsTrue meta " private "
2021-03-09 21:24:02 +03:00
then throwErr ( PrivateBinding ( getPath found ) ) ctx info
2020-12-02 18:33:37 +03:00
else ( ctx , Right ( resolveDef found ) )
Arr objs -> do
2020-03-28 16:32:41 +03:00
( newCtx , evaled ) <- foldlM successiveEval ( ctx , Right [] ) objs
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
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
2021-01-25 23:16:53 +03:00
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 =
2021-06-08 08:39:06 +03:00
case validate form of
Left e -> pure ( evalError ctx ( format e ) ( xobjInfo xobj ) )
Right form' ->
case form' of
2021-06-16 22:41:58 +03:00
( IfPat _ _ _ _ ) -> evaluateIf form'
( DefnPat _ _ _ _ ) -> specialCommandDefine ctx xobj
( DefPat _ _ _ ) -> specialCommandDefine ctx xobj
2021-06-08 08:39:06 +03:00
( ThePat _ _ _ ) -> evaluateThe form'
2021-06-16 22:41:58 +03:00
( LetPat _ _ _ ) -> evaluateLet form'
2021-06-08 08:39:06 +03:00
( FnPat _ _ _ ) -> evaluateFn form'
( AppPat ( ClosurePat _ _ _ ) _ ) -> evaluateClosure form'
( AppPat ( DynamicFnPat _ _ _ ) _ ) -> evaluateDynamicFn form'
( AppPat ( MacroPat _ _ _ ) _ ) -> evaluateMacro form'
( AppPat ( CommandPat _ _ _ ) _ ) -> evaluateCommand form'
( AppPat ( PrimitivePat _ _ _ ) _ ) -> evaluatePrimitive form'
2021-08-05 08:36:29 +03:00
( WithPat _ sym @ ( SymPat path _ ) forms ) -> specialCommandWith ctx sym path forms
2021-06-16 22:41:58 +03:00
( DoPat _ forms ) -> evaluateSideEffects forms
( WhilePat _ cond body ) -> specialCommandWhile ctx cond body
( SetPat _ iden value ) -> specialCommandSet ctx ( iden : [ value ] )
2021-06-08 08:39:06 +03:00
-- 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.
2021-08-05 08:36:29 +03:00
( AppPat self @ ( ListPat ( x @ ( SymPat _ _ ) : _ ) ) args ) ->
2021-06-16 22:41:58 +03:00
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 )
2021-10-12 22:23:11 +03:00
Left er -> pure ( ctx , Left er )
2021-06-16 22:41:58 +03:00
( AppPat ( ListPat _ ) _ ) -> evaluateApp form'
2021-08-05 08:36:29 +03:00
( AppPat ( SymPat _ _ ) _ ) -> evaluateApp form'
2021-10-12 22:23:11 +03:00
( AppPat ( XObj other _ _ ) _ )
| isResolvableStaticObj other ->
pure ( ctx , ( Left ( HasStaticCall xobj info ) ) )
2021-06-08 08:39:06 +03:00
[] -> 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'
2020-12-01 02:11:01 +03:00
successiveEval ( ctx' , acc ) x =
2020-12-02 18:33:37 +03:00
case acc of
Left _ -> pure ( ctx' , acc )
Right l -> do
2021-01-11 15:22:05 +03:00
( newCtx , evald ) <- eval ctx' x preference resolver
2020-12-02 18:33:37 +03:00
pure $ case evald of
Right res -> ( newCtx , Right ( l ++ [ res ] ) )
Left err -> ( newCtx , Left err )
2021-06-08 08:39:06 +03:00
evaluateIf :: Evaluator
2021-06-16 22:41:58 +03:00
evaluateIf ( IfPat _ cond true false ) = do
2021-06-08 08:39:06 +03:00
( 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
2021-06-16 22:41:58 +03:00
( newCtx ,
do
okValue <- evaledValue
Right ( XObj ( Lst [ the , t , okValue ] ) info ty )
)
2021-06-08 08:39:06 +03:00
evaluateThe _ = pure ( evalError ctx ( format ( GenericMalformed xobj ) ) ( xobjInfo xobj ) )
evaluateLet :: Evaluator
2021-06-16 22:41:58 +03:00
evaluateLet ( LetPat _ ( ArrPat bindings ) body ) = do
2021-06-08 08:39:06 +03:00
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
2021-07-15 23:45:05 +03:00
parentEnv = envParent e
2021-06-08 08:39:06 +03:00
pure
2021-07-15 23:45:05 +03:00
( replaceInternalEnvMaybe finalCtx parentEnv ,
2021-06-08 08:39:06 +03:00
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 )
2021-07-15 23:45:05 +03:00
Right envWithSelf = if isFn x then E . insertX recFix ( SymPath [] n ) x else Right recFix
2021-06-08 08:39:06 +03:00
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
2021-06-16 22:41:58 +03:00
l @ ( ListPat _ ) -> go l ResolveLocal
2021-08-05 08:36:29 +03:00
sym @ ( SymPat _ _ ) -> go sym resolver
2021-06-08 08:39:06 +03:00
_ -> pure ( evalError ctx ( format ( GenericMalformed xobj ) ) ( xobjInfo xobj ) )
2021-06-16 22:41:58 +03:00
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' )
2021-06-08 08:39:06 +03:00
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
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
pure
( newCtx ,
do
ok <- expanded
Right ( XObj ( StaticArr ok ) i t )
)
2021-05-25 09:08:30 +03:00
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 )
2021-01-28 18:40:41 +03:00
XObj ( Lst [ XObj ( Sym ( SymPath [] " quote " ) _ ) _ _ , _ ] ) _ _ ->
pure ( ctx , Right xobj )
2020-12-22 17:53:55 +03:00
XObj ( Lst [ XObj ( Lst ( XObj Macro _ _ : _ ) ) _ _ ] ) _ _ -> evalDynamic ResolveLocal ctx xobj
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
Right m @ ( XObj ( Lst ( XObj Macro _ _ : _ ) ) _ _ ) -> do
2020-12-22 17:53:55 +03:00
( newCtx' , res ) <- evalDynamic ResolveLocal ctx ( XObj ( Lst ( m : args ) ) i t )
2020-11-24 08:09:15 +03:00
pure ( newCtx' , res )
2020-04-10 17:04:44 +03:00
_ -> do
( newCtx , expanded ) <- foldlM successiveExpand ( ctx , Right [] ) args
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
pure
( newCtx ,
do
ok <- expanded
Right ( XObj ( Lst ok ) i t )
)
2020-11-24 08:09:15 +03:00
_ -> pure ( ctx , Right xobj )
2020-12-02 18:33:37 +03:00
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 )
2020-12-02 18:33:37 +03:00
apply ctx @ Context { contextInternalEnv = internal } body params args =
2021-05-24 22:07:30 +03:00
let allParams = map getName params
2020-12-02 18:33:37 +03:00
in case splitWhen ( " :rest " == ) allParams of
2021-05-24 22:07:30 +03:00
[ a , b ] -> callWith a b
[ a ] -> callWith a []
2020-12-02 18:33:37 +03:00
_ ->
2021-03-09 21:24:02 +03:00
pure ( throwErr ( MacroBadArgumentSplit allParams ) ctx Nothing )
2020-12-02 18:33:37 +03:00
where
2021-05-24 22:07:30 +03:00
callWith proper rest = do
2020-12-02 18:33:37 +03:00
let n = length proper
2021-01-25 23:18:01 +03:00
insideEnv = Env Map . empty internal Nothing Set . empty InternalEnv 0
2020-12-02 18:33:37 +03:00
insideEnv' =
2021-01-31 16:54:13 +03:00
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 ) ) )
2020-12-02 18:33:37 +03:00
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 )
)
2021-05-24 22:07:30 +03:00
binds = if null rest then proper else proper ++ [ ( head rest ) ]
( c , r ) <- ( eval ( replaceInternalEnv ctx insideEnv'' ) body ( PreferLocal ( map ( \ x -> ( SymPath [] x ) ) binds ) ) ResolveLocal )
2020-12-02 18:33:37 +03:00
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
2021-07-23 22:24:59 +03:00
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
2020-12-02 18:33:37 +03:00
where
2021-07-23 22:24:59 +03:00
exec = case parseAtLine line input fileName of
2020-12-02 18:33:37 +03:00
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'
2020-12-24 18:20:07 +03:00
interactiveFolder ( _ , context ) =
executeCommand context
2020-12-02 18:33:37 +03:00
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-01 12:36:14 +03:00
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)
2017-12-01 12:36:14 +03:00
folder :: Context -> XObj -> IO Context
2020-03-09 18:27:50 +03:00
folder context xobj = do
2020-12-02 18:33:37 +03:00
( _ , ctx ) <- executeCommand context xobj
pure ctx
2017-12-01 12:36:14 +03:00
2020-03-11 14:23:11 +03:00
-- | Take a repl command and execute it.
2020-03-09 18:27:50 +03:00
executeCommand :: Context -> XObj -> IO ( XObj , Context )
2020-04-10 17:04:44 +03:00
executeCommand ctx @ ( Context env _ _ _ _ _ _ _ ) xobj =
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
case result of
2020-12-22 19:44:44 +03:00
Left e @ EvalError { } -> do
2020-12-02 18:33:37 +03:00
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 ) )
2020-12-02 18:33:37 +03:00
Left ( HasStaticCall _ _ ) ->
2020-06-07 01:14:46 +03:00
callFromRepl newCtx xobj
2020-12-02 18:33:37 +03:00
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'
2020-12-02 18:33:37 +03:00
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 )
2020-12-02 18:33:37 +03:00
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
2021-01-29 19:21:19 +03:00
( Lst [ XObj ( Sym ( SymPath [] " build " ) Symbol ) ( Just dummyInfo ) Nothing , trueXObj ] )
2020-12-02 18:33:37 +03:00
( Just dummyInfo )
Nothing ,
XObj
( Lst [ XObj ( Sym ( SymPath [] " run " ) Symbol ) ( Just dummyInfo ) Nothing ] )
( Just dummyInfo )
2021-01-29 19:21:19 +03:00
Nothing ,
( XObj ( Lst [] ) ( Just dummyInfo ) ( Just UnitTy ) )
2020-12-02 18:33:37 +03:00
]
)
( Just dummyInfo )
Nothing
xobjIsSexp ( XObj ( Lst ( XObj ( Sym ( SymPath [] " s-expr " ) Symbol ) _ _ : _ ) ) _ _ ) = True
xobjIsSexp _ = False
2017-12-01 12:36:14 +03:00
2020-03-07 02:26:32 +03:00
reportExecutionError :: Context -> String -> IO ()
2018-03-19 23:54:05 +03:00
reportExecutionError ctx errorMessage =
case contextExecMode ctx of
2020-03-07 02:26:32 +03:00
Check -> putStrLn errorMessage
2018-03-19 23:54:05 +03:00
_ ->
2020-12-02 18:33:37 +03:00
do
emitErrorBare errorMessage
throw CancelEvaluationException
2018-03-19 13:03:40 +03:00
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
2020-12-01 02:11:01 +03:00
( ShellOutException message rc ) -> emitErrorWithLabel " RUNTIME ERROR " message >> stop rc
CancelEvaluationException -> stop 1
EvalException err -> emitError ( show err ) >> stop 1
2020-12-02 18:33:37 +03:00
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
2021-01-25 23:18:01 +03:00
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 } )
2020-11-24 08:09:15 +03:00
pure ( ctxAfter' , dynamicNil )
2020-03-28 16:32:41 +03:00
specialCommandDefine :: Context -> XObj -> IO ( Context , Either EvalError XObj )
specialCommandDefine ctx xobj =
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
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 )
2020-12-02 18:33:37 +03:00
pure ( ctxWithDef , dynamicNil )
Left err ->
pure ( ctx , Left err )
2017-12-16 16:54:26 +03:00
2020-05-08 17:14:00 +03:00
specialCommandWhile :: Context -> XObj -> XObj -> IO ( Context , Either EvalError XObj )
specialCommandWhile ctx cond body = do
2020-12-22 17:53:55 +03:00
( newCtx , evd ) <- evalDynamic ResolveLocal ctx cond
2020-05-08 17:14:00 +03:00
case evd of
Right c ->
2020-12-01 02:11:01 +03:00
case xobjObj c of
2020-12-02 18:33:37 +03:00
Bol b ->
if b
then do
2020-12-22 17:53:55 +03:00
( newCtx' , _ ) <- evalDynamic ResolveLocal newCtx body
2020-12-02 18:33:37 +03:00
specialCommandWhile newCtx' cond body
else pure ( newCtx , dynamicNil )
2020-05-08 17:14:00 +03:00
_ ->
2021-03-09 21:24:02 +03:00
pure ( throwErr ( WhileContainsNonBool c ) ctx ( xobjInfo c ) )
2020-11-24 08:09:15 +03:00
Left e -> pure ( newCtx , Left e )
2020-05-08 17:14:00 +03:00
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 =
2020-05-21 01:09:10 +03:00
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 )
2020-12-07 09:06:32 +03:00
path = getPath xobj
2020-05-21 01:09:10 +03:00
fullPath = case path of
2020-12-02 18:33:37 +03:00
( 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 )
2020-12-02 18:33:37 +03:00
in case Meta . get " sig " metaData of
2020-02-21 13:51:49 +03:00
Just foundSignature ->
case xobjToTy foundSignature of
2020-12-02 18:33:37 +03:00
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 ) )
2020-12-01 02:11:01 +03:00
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
2020-02-21 13:51:49 +03:00
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
2020-12-02 18:33:37 +03:00
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 )
2020-12-02 18:33:37 +03:00
case sig of
Left err -> pure ( ctx , Left err )
Right okSig -> do
2020-12-22 17:53:55 +03:00
( _ , expansionResult ) <- expandAll ( evalDynamic ResolveLocal ) ctx xobj
2020-12-02 18:33:37 +03:00
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 )
2020-12-02 18:33:37 +03:00
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 )
2019-03-28 21:14:23 +03:00
2020-12-29 01:48:57 +03:00
primitiveDefmodule :: VariadicPrimitiveCallback
2021-07-03 14:00:14 +03:00
primitiveDefmodule xobj ctx @ ( Context env i tenv pathStrings _ _ _ _ ) ( XObj ( Sym ( SymPath [] moduleName ) _ ) si _ : innerExpressions ) =
2020-12-24 18:20:07 +03:00
-- 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 )
2020-12-02 18:33:37 +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
--------------------------------------------------------------------------------
-- 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 _ =
2021-03-09 21:24:02 +03:00
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 )
2021-07-03 14:00:14 +03:00
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 )
2020-12-22 17:53:55 +03:00
Right r -> evalDynamic ResolveLocal ctx'' r
2020-12-02 18:33:37 +03:00
primitiveDefmodule _ ctx ( x : _ ) =
2021-03-09 21:24:02 +03:00
pure ( throwErr ( DefmoduleContainsNonSymbol x ) ctx ( xobjInfo x ) )
primitiveDefmodule xobj ctx [] =
pure ( throwErr DefmoduleNoArgs ctx ( xobjInfo xobj ) )
2018-01-30 13:36:27 +03:00
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.
2020-12-15 09:58:33 +03:00
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 ] =
2021-03-09 21:24:02 +03:00
pure $ throwErr ( loadInvalidArgs [ x ] ) ctx ( xobjInfo x )
2020-12-15 09:58:33 +03:00
commandLoad ctx [ x , _ ] =
2021-03-09 21:24:02 +03:00
pure $ throwErr ( loadInvalidArgs [ x ] ) ctx ( xobjInfo x )
2020-12-15 09:58:33 +03:00
commandLoad ctx [ xobj @ ( XObj ( Str path ) i _ ) ] =
loadInternal ctx xobj path i Nothing DoesReload
2021-03-09 21:24:02 +03:00
commandLoad ctx x =
pure $ throwErr ( loadInvalidArgs x ) ctx Nothing
2020-05-05 16:00:57 +03:00
2020-12-15 09:58:33 +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 ] =
2021-03-09 21:24:02 +03:00
pure $ throwErr ( loadOnceInvalidArgs [ x ] ) ctx ( xobjInfo x )
2020-12-15 09:58:33 +03:00
commandLoadOnce ctx [ x , _ ] =
2021-03-09 21:24:02 +03:00
pure $ throwErr ( loadOnceInvalidArgs [ x ] ) ctx ( xobjInfo x )
2020-12-15 09:58:33 +03:00
commandLoadOnce ctx [ xobj @ ( XObj ( Str path ) i _ ) ] =
loadInternal ctx xobj path i Nothing Frozen
2021-03-09 21:24:02 +03:00
commandLoadOnce ctx x =
pure $ throwErr ( loadOnceInvalidArgs x ) ctx Nothing
2020-05-05 16:00:57 +03:00
2020-12-15 09:58:33 +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
2020-12-02 18:33:37 +03:00
Just ii ->
case infoFile ii of
" REPL " -> " . "
file -> takeDirectory file
Nothing -> " . "
2020-03-28 16:32:41 +03:00
carpDir = projectCarpDir proj
fullSearchPaths =
2020-12-03 14:02:58 +03:00
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)
2020-12-02 18:33:37 +03:00
++ [ carpDir </> " core " </> path ] -- user defined search paths
++ [ libDir </> path ]
2020-11-24 08:09:15 +03:00
firstM _ [] = pure Nothing
2020-12-02 18:33:37 +03:00
firstM p ( x : xs ) = do
2020-03-28 16:32:41 +03:00
q <- p x
if q
2020-11-24 08:09:15 +03:00
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 ->
2020-12-02 18:33:37 +03:00
if '@' ` elem ` path
then tryInstall path
else pure $ invalidPath ctx path
2020-03-28 16:32:41 +03:00
Just firstPathFound ->
2020-12-02 18:33:37 +03:00
do
canonicalPath <- liftIO ( canonicalizePath firstPathFound )
2020-12-22 19:44:44 +03:00
fileThatLoads <- liftIO ( canonicalizePath $ maybe " " infoFile i )
2020-12-02 18:33:37 +03:00
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
2020-12-02 18:33:37 +03:00
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 =
2020-05-06 11:27:10 +03:00
if projectForceReload proj
2020-12-02 18:33:37 +03:00
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
2020-12-01 02:11:01 +03:00
invalidPath ctx' path' =
2021-03-09 21:24:02 +03:00
throwErr ( LoadFileNotFound path' ) ctx' ( xobjInfo xobj )
2020-12-01 02:11:01 +03:00
invalidPathWith ctx' path' stderr cleanup cleanupPath = do
2019-10-19 18:09:08 +03:00
_ <- liftIO $ when cleanup ( removeDirectoryRecursive cleanupPath )
2020-12-02 18:33:37 +03:00
pure $
2021-03-09 21:24:02 +03:00
throwErr ( LoadGitFailure path' stderr ) ctx' ( xobjInfo xobj )
2020-11-26 00:12:57 +03:00
replaceC _ _ [] = []
2020-12-02 18:33:37 +03:00
replaceC c s ( a : b ) = if a == c then s ++ replaceC c s b else a : replaceC c s b
2020-12-01 02:11:01 +03:00
cantLoadSelf ctx' path' =
2021-03-09 21:24:02 +03:00
throwErr ( LoadRecursiveLoad path' ) ctx' ( xobjInfo xobj )
2020-12-01 02:11:01 +03:00
tryInstall path' =
let split = splitOn " @ " path'
2020-12-02 18:33:37 +03:00
in tryInstallWithCheckout ( joinWith " @ " ( init split ) ) ( last split )
2018-07-11 12:41:22 +03:00
fromURL url =
2019-10-02 20:58:00 +03:00
let split = splitOn " / " ( replaceC ':' " _COLON_ " url )
2020-12-01 02:11:01 +03:00
first = head split
2020-12-02 18:33:37 +03:00
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
2020-12-01 02:11:01 +03:00
tryInstallWithCheckout path' toCheckout = do
2018-06-25 15:30:12 +03:00
let proj = contextProj ctx
2020-12-01 02:11:01 +03:00
fpath <- liftIO $ cachePath $ projectLibDir proj </> fromURL path' </> toCheckout
2019-04-30 17:21:06 +03:00
cur <- liftIO getCurrentDirectory
2019-10-19 18:09:08 +03:00
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
2019-07-14 17:18:05 +03:00
( _ , txt , _ ) <- liftIO $ readProcessWithExitCode " git " [ " rev-parse " , " --abbrev-ref=loose " , " HEAD " ] " "
if txt == " HEAD \ n "
2020-12-02 18:33:37 +03:00
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
2020-12-01 02:11:01 +03:00
doGitLoad path' fpath =
2020-12-15 09:58:33 +03:00
case fileToLoad of
Just file -> commandLoad ctx [ XObj ( Str ( fpath </> file ) ) Nothing Nothing ]
Nothing ->
-- we’ re 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
2020-12-15 09:58:33 +03:00
loadFilesExt :: VariadicCommandCallback -> Context -> [ FilePath ] -> IO Context
2020-12-22 19:44:44 +03:00
loadFilesExt loadCmd = foldM load
2020-12-02 18:33:37 +03:00
where
load :: Context -> FilePath -> IO Context
load ctx file = do
2020-12-15 09:58:33 +03:00
( newCtx , ret ) <- loadCmd ctx [ XObj ( Str file ) Nothing Nothing ]
2020-12-02 18:33:37 +03:00
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).
2020-12-14 01:34:56 +03:00
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
2020-11-24 08:09:15 +03:00
f context ( _ , Frozen ) | not ( projectForceReload ( contextProj context ) ) = pure context
2020-05-06 11:38:55 +03:00
f context ( filepath , _ ) =
2020-12-02 18:33:37 +03:00
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 )
2020-11-24 08:09:15 +03:00
pure ( newCtx , dynamicNil )
2017-12-16 16:54:26 +03:00
-- | Command for expanding a form and its macros.
2020-12-14 01:34:56 +03:00
commandExpand :: UnaryCommandCallback
2020-12-22 19:44:44 +03:00
commandExpand = macroExpand
2017-12-23 08:30:42 +03:00
-- | This function will show the resulting C code from an expression.
-- | i.e. (Int.+ 2 3) => "_0 = 2 + 3"
2020-12-14 01:34:56 +03:00
commandC :: UnaryCommandCallback
commandC ctx xobj = do
2020-12-22 17:53:55 +03:00
( newCtx , result ) <- expandAll ( evalDynamic ResolveLocal ) ctx xobj
2020-03-28 16:32:41 +03:00
case result of
2020-11-24 08:09:15 +03:00
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
2020-12-01 02:11:01 +03:00
Left err -> pure $ evalError newCtx ( show err ) ( xobjInfo xobj )
2020-03-28 16:32:41 +03:00
Right ( annXObj , annDeps ) ->
2020-12-02 18:33:37 +03:00
do
let cXObj = printC annXObj
cDeps = concatMap printC annDeps
c = cDeps ++ cXObj
liftIO ( putStr c )
pure ( newCtx , dynamicNil )
2017-12-23 08:30:42 +03:00
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 )
2017-12-23 08:30:42 +03:00
-- | Helper function for commandC
2019-10-24 12:12:41 +03:00
printC :: XObj -> String
2017-12-23 08:30:42 +03:00
printC xobj =
case checkForUnresolvedSymbols xobj of
Left e ->
2019-10-24 12:12:41 +03:00
strWithColor Red ( show e ++ " , can't print resulting code. \ n " )
2017-12-23 08:30:42 +03:00
Right _ ->
2019-10-24 12:12:41 +03:00
strWithColor Green ( toC All ( Binder emptyMeta xobj ) )
2019-03-28 21:14:23 +03:00
2020-03-11 14:23:11 +03:00
buildMainFunction :: XObj -> XObj
buildMainFunction xobj =
2020-12-02 18:33:37 +03:00
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
2020-12-09 08:19:28 +03:00
Nothing -> error " buildmainfunction "
2020-12-02 18:33:37 +03:00
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
2020-12-29 01:48:57 +03:00
primitiveDefdynamic :: BinaryPrimitiveCallback
primitiveDefdynamic _ ctx ( XObj ( Sym ( SymPath [] name ) _ ) _ _ ) value = do
2020-12-22 17:53:55 +03:00
( newCtx , result ) <- evalDynamic ResolveLocal ctx value
2020-03-28 16:32:41 +03:00
case result of
2020-11-24 08:09:15 +03:00
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
2020-12-29 01:48:57 +03:00
primitiveDefdynamic _ ctx notName _ =
2021-03-09 21:24:02 +03:00
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 ] =
2020-12-24 18:20:07 +03:00
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 )
2020-12-24 18:20:07 +03:00
>>= \ 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 )
2020-12-24 18:20:07 +03:00
>>= \ 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 )
2020-12-24 18:20:07 +03:00
>>= \ 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 )
2020-12-24 18:20:07 +03:00
>>= \ 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 ) ) ) ) )
2020-12-24 18:20:07 +03:00
( \ ( 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 )
2020-12-02 18:33:37 +03:00
where
2020-12-24 18:20:07 +03:00
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
2021-05-23 00:44:04 +03:00
-- don't type check dynamic or untyped bindings
-- TODO: Figure out why untyped cases are sometimes coming into set!
Just DynamicTy -> handleUnTyped
Nothing -> handleUnTyped
2020-12-24 18:20:07 +03:00
_ ->
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 )
2021-05-25 09:08:30 +03:00
where
handleUnTyped :: IO ( Context , Either EvalError XObj )
handleUnTyped =
evalDynamic ResolveLocal ctx val
>>= \ ( newCtx , result ) -> setter newCtx env result binder
2020-12-24 18:20:07 +03:00
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 )
2020-12-24 18:20:07 +03:00
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 , _ ] =
2021-03-09 21:24:02 +03:00
pure ( throwErr ( SetInvalidVarName notName ) ctx ( xobjInfo notName ) )
2020-03-28 16:32:41 +03:00
specialCommandSet ctx args =
2021-03-09 21:24:02 +03:00
pure ( throwErr ( setInvalidArgs args ) ctx ( if null args then Nothing else xobjInfo ( head args ) ) )
2020-03-18 00:51:00 +03:00
2020-08-25 19:33:52 +03:00
-- | Convenience method for signifying failure in a given context.
2020-12-24 18:20:07 +03:00
failure :: Context -> XObj -> EvalError -> ( Context , Either EvalError a )
failure ctx orig err = evalError ctx ( show err ) ( xobjInfo orig )
2020-08-25 19:33:52 +03:00
-- | 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 )
2020-08-25 19:33:52 +03:00
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
2020-11-24 08:09:15 +03:00
pure $ case typedValue of
2020-11-26 00:12:57 +03:00
Right ( val' , _ ) -> go ctx' binderTy val'
2020-11-24 08:09:15 +03:00
Left err -> ( ctx' , Left err )
2020-12-02 18:33:37 +03:00
where
2020-12-22 19:44:44 +03:00
path = getPath ( binderXObj binder )
2020-12-02 18:33:37 +03:00
binderTy = xobjTy ( binderXObj binder )
2021-03-09 21:24:02 +03:00
typeErr x = throwErr ( SetTypeMismatch path ( fromJust ( xobjTy x ) ) ( fromJust binderTy ) ) ctx ( xobjInfo x )
2020-12-02 18:33:37 +03:00
go ctx'' ( Just DynamicTy ) x = ( ctx'' , Right x )
go ctx'' t x @ ( XObj _ _ t' ) = if t == t' then ( ctx'' , Right x ) else typeErr x
2020-08-25 19:33:52 +03:00
2020-08-22 09:08:14 +03:00
-- | Sets a variable, checking whether or not it is static or dynamic, and
-- assigns an appropriate type to the variable.
2020-08-25 19:33:52 +03:00
-- 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 =
2020-12-02 18:33:37 +03:00
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 ) ) )
2020-12-02 18:33:37 +03:00
( 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 ) ) ) )
2021-01-25 23:16:53 +03:00
( 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 ) ) )
2020-12-02 18:33:37 +03:00
-- shouldn't happen, errors are thrown at call sites.
-- TODO: Return an either here to propagate error.
_ -> env
2020-08-22 07:24:30 +03:00
2020-12-29 01:48:57 +03:00
primitiveEval :: UnaryPrimitiveCallback
primitiveEval _ ctx val = do
2020-03-09 17:50:52 +03:00
-- primitives don’ t evaluate their arguments, so this needs to double-evaluate
2020-12-22 17:53:55 +03:00
( newCtx , arg ) <- evalDynamic ResolveLocal ctx val
2020-03-09 17:50:52 +03:00
case arg of
2020-11-24 08:09:15 +03:00
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
2020-11-24 08:09:15 +03:00
Left err -> pure ( newCtx' , Left err )
2020-11-07 13:51:22 +03:00
Right ok -> do
2020-12-22 17:53:55 +03:00
( finalCtx , res ) <- evalDynamic ResolveLocal newCtx' ok
2020-11-24 08:09:15 +03:00
pure $ case res of
2021-03-09 21:24:02 +03:00
Left ( HasStaticCall x i ) -> throwErr ( StaticCall x ) ctx i
2020-11-24 08:09:15 +03:00
_ -> ( 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
2020-12-29 01:48:57 +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
2020-12-29 01:48:57 +03:00
primitiveDefndynamic _ ctx notName _ _ =
2020-04-10 18:31:51 +03:00
argumentErr ctx " defndynamic " " a name " " first " notName
2020-12-29 01:48:57 +03:00
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
2020-12-29 01:48:57 +03:00
primitiveDefmacro _ ctx notName _ _ =
2020-04-10 18:31:51 +03:00
argumentErr ctx " defmacro " " a name " " first " notName