* fix: make set! work with dynamic args (thanks @hellerve)
Like `let` before it, we used to bind function arguments to their values
only, which wasn't accounted for in `set!` such that one could not
`set!` `i` in `defndynamic [i] ...`. To fix this for let bindings we
introduced a `LetDef` form for consistency with Def forms. This commit
renames `LetDef` to `LocalDef` and uses it as a value for function
arguments in addition to let bindings, ensuring `set!` works on function
arguments too. Big thanks to @hellerve for the suggestion!
* test: add regression test for set! on dynamic function args
* feat: quasiquotation
* test: add tests for quasiquotation
* fix: fix typo in call to doc
* fix: do not evaluate quasiquote too eagerly
* test: pull quasiquote test into macro
* docs: fix unquote example with better constant
* feat: add quasiquote literals
* refactor: simplify reader macros
* core: add derive
* fix: fix errors with set!
Notably, don't type check dynamic bindings (which can be set to
whatever) and eliminate a hang that resulted from not handling an error
at the end of the `set!` call. Also refactors some of the code in
efforts to make it a bit cleaner.
Also adds an error when `set!` can't find the variable one calls set!
on.
* feat: better derive
* test: add error test for derive
* document derive
* add derive to core documentation to generate
* core: add derive
* fix: fix errors with set!
Notably, don't type check dynamic bindings (which can be set to
whatever) and eliminate a hang that resulted from not handling an error
at the end of the `set!` call. Also refactors some of the code in
efforts to make it a bit cleaner.
Also adds an error when `set!` can't find the variable one calls set!
on.
* feat: better derive
* document derive
* feat: first completely working version of derive
* feat: make name of derivable customizable (thanks @scolsen)
* refactor: implement doc edits provided by @scolsen
* feat: change argument order for derive
* fix: change deriver error test
* test: add derive tests
* fix: change order of derive back
* docs: fix typo in derive document
Co-authored-by: scottolsen <scg.olsen@gmail.com>
* feat: add semigroup instance for Env and Context
Adds a semigroup instance for combining Envs and Contexts--this will be
necessary to ensure closure's are evaluated under the combination of the
context captured in the closure and the current global context during
evaluation.
The semigroup instances are left biased and will prefer bindings defined
in the left context/env argument in the case of conflicts (this is in
keeping with the implementation of `union` in Data.Map, the underlying
function powering this instance).
* fix: evaluate closures under the current context
Previously, closures were evaluated only under the context that was
stored during their creation. However, this can lead to issues where
closures do not resolve bindings to their latest definitions.
This commit leverages the semigroup instance of Context to evaluate
closures under the combination of the context captured during their
creation and the broader context during their evaluation/application,
preferring the context captured in the closure when bindings conflict.
This ensures that when we apply closures their local bindings still
resolve to definitions encapsulated in the closure, while other bindings
resolve to the definitions contained in the current overarching context
(instead of the old context captured by the closure).
* fix: fix bias for context env combinations in semigroup
Previously, the semigroup instance for Context was left-biased in all
the combinations of each context's environment. However, one usually
calls this function to combine some older context with a newer context,
intending to have the older context win *only* in the case of internal
environments.
This commit changes the behavior of the semigroup instance to better
reflect this use case. When one calls:
`c <> c'`
The envs in each context are combined as follows:
- internal: If conflicts occur, prefer the bindings of the context on
the LHS (the "older" context)
- global: If conflicts occur, prefer the bindings of the context on the
RHS ("newer" context)
- type: If conflicts occur, prefer the bindings of the context on the
RHS ("newer" context)
This ensures the resulting context uses the latest values in the chance
of conflicts in the global env/type env, and the older values in the
case of an internal env (a closure).
* test: add basic tests for closures
* refactor: rename test/closure -> test/dynamic-closure
Also updates the forms to test dynamic closures.
Previously, we couldn't replace qualified bindings in Envs. This change
updates envReplaceBindings to accept qualified paths and to replace the
binding denoted by the path, updating the module environments the binder
belongs to accordingly.
If the qualified path does not denote an existing module, it simply
returns the environment as is (just like the unqualified case).
* fix: account for interfaces in function lookups
An older version of Carp only allowed users to implement interfaces by
defining a function of the same name. Since PR 769 and roughly commit
040e9e4 however, this was no longer the case, since we added a primitive
for implementing interfaces, allowing users to implement interfaces
using functions with whatever name they want.
When this happened, the function lookups that happen during
concretization were never updated to reflect the fact that interface
implementations might have different names than their corresponding
interfaces. It's rare, but this can lead to errors for template
functions in the compiler.
For example, registered types automatically get an implementation of
`prn` with the same name. This function takes a ref to the registered
type as an argument. If a user wants to use such a type as an array
member however, they must provide a prn that takes a value argument
instead of a ref. If the user still wants to keep the ref variant handy,
however, they need to give this new prn implementation a different name,
otherwise Carp will think they are redefining the existing function.
However, using an arbitrary name would result in this second prn
instance not being found during the lookup fixed in this commit, since
it did not match the name of the interface exactly.
After this commit, this issue will no longer crop up, as the
concretize.hs function lookup now accounts for the fact that, if the
function it is looking for is an interface, it may need to find
functions with names that differ from the name it received as an
argument; e.g. a lookup for `prn` could result in finding a function
named `foo` that is a valid implementation of `prn`.
* fix: include interface name in concretize lookup
* refactor: rename allFunctionsWithNameAndSignature
This function now finds implementations of interfaces as well, which
might have a different name than the function name passed as an
argument. This commit renames the function to reflect this; I've also
added some explanatory notes.
A misplaced type env reference caused a bug in which types could not be
placed in modules more than a single level deep. Similarly, existing
modules in the type env were always overwritten when new types were
added to modules that were not disjoint. This commit fixes both issues.
In the future, we should likely make module management functions more
general and call them to manage modules in both the value and type envs.
Notably, don't type check dynamic bindings (which can be set to
whatever) and eliminate a hang that resulted from not handling an error
at the end of the `set!` call. Also refactors some of the code in
efforts to make it a bit cleaner.
Also adds an error when `set!` can't find the variable one calls set!
on.
* feat: overwrite existing interface implementations
This commit alters the behavior of interfaces so that implementations
with the same type signature will overwrite previous implementations
with that signature--before this was a runtime error.
Previously, if a user defined two distinctly named implementations of an
interface that shared a type, Carp would panic and error at runtime if
the interface was called and resolved to the type, since it couldn't
decide which implementation to use from the type alone. After this
commit, we instead issue a warning and overwrite existing
implementations of the same type, so that defining:
```
(defn foo [] 0)
(implements zero foo)
```
will replace `Int.zero` in the `zero` interface's implementation path
list and won't result in a runtime error--instead `foo` will be called
when `zero` is called in a context in which it returns an int:
```
[WARNING] An implementation of the interface zero with type (Fn [] Int)
already exists: Int.zero. It will be replaced by the implementation:
foo.
This may break a bunch of upstream code!
```
test/interface.carp also has a concrete illustration of this case.
* chore: address hlint suggestions
* fix: don't print overridden interface implementations in info
This commit updates our handling of interface overrides to remove
interfaces from the implements meta of a function that was overridden by
a new implementation.
Similarly, this refactors primitiveInfo to prevent printing binders that
do not actually implement an interface.
* refactor: incorporate @TimDeve's error message suggestion
* feat: evaluate defs and defns in repl
* fix: better handling of static symbol evaluation
* refactor: include feedback by @scolsen (thanks)
* refactor: incorporate feedback by @eriksvedang into resolver code
* refactor: rename shouldResolve to resolver
* 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
* feat!: support defining types in modules
This commit adds support for defining types (using deftype) in modules.
Previously, all types were hoisted to the top level of the type
environment. After this commit, the type environment supports defining
nested modules just like the value env, so, calling the following:
```
(defmodule Foo (deftype Bar Baz))
```
Adds the following to the type env:
```
Foo : Module = {
Bar : Type
}
```
and the following to the value env:
```
Foo : Module = {
Bar : Module = {
Baz : (Fn [] Foo.Bar)
copy : (Fn [(Ref Foo.Bar q)] Foo.Bar)
delete : (Fn [Foo.Bar] ())
get-tag : (Fn [(Ref Foo.Bar q)] Int)
prn : (Fn [(Ref Foo.Bar q)] String)
str : (Fn [(Ref Foo.Bar q)] String)
}
}
```
Such a type is *distinct* from any type defined at the top level that
happens to also have the name `Bar`.
This commit also updates info and tests to account for types in modules.
BREAKING CHANGE: This change is breaking since it alters the names of
types that were previously defined in modules. A good example of this is
the `Id` type in the `Color` module. Previously, one could refer to this
type by simply typing `Id` since it was hoisted to the top level. Now it
*must* be referred to by `Color.Id` since `Id` at the top level of the
type env and `Color.Id` (Id in the color module) are considered to be
distinct types.
* chore: format code
* refactor: use concat instead of intercalate
* chore: remove excess parentheses
* chore: Add todo to return IO () in printIfFound
* feat: 'delete' interface (deciding whether a type is managed or not)
* refactor: Move implements function to Interface module
* feat: Automatically implement 'delete' for types defined with `deftype`
* fix: Don't implement `delete` for Pointer
* refactor: Clarify `memberInfo` function
* fix: Also check if function types are managed
* fix: Implement 'delete' for String and StaticArray.
* fix: Manage String and Pattern. Tests run!
* feat: Add `managed?` primitive
* docs: Note about primitiveIsManaged
* test: Basic test cases for managed / nonmanaged external types
* test: Make sure `managed?` primitive works
* test: Inactivate sanitizer since we're creating leaks intentionally
* feat: Removed 'isExternalType' function
* refactor: Decide if struct member takes ref or not when printing
..based on blitable interface, and 'prn' implemntation
* refactor: Use 'blit' everywhere
* refactor: Implement `strTakesRefOrNot` in terms of `memberStrCallingConvention`
* refactor: Use `remove` over `filter not`