Adds -c|--emitcname flag to headerparse to always emit the C identifier
in `register` definitions (useful when the register will be in a
module).
Fixes a bug where the kebab case flag would not output C identifiers
making the emitted C identifiers not match with the ones in the headers.
Adds docs entry about headerparse in CInterop doc.
Makes headerparse emit `CChar` instead of `Char` when encountering a
signature containing `char`.
This commit builds on the emit-c feature by permitting C typed values to
be used anywhere in Carp code.
For example, if one wants to use the literal C macro `EDOM`:
```clojure
(register EDOM C "EDOM")
(Int.+ 1 EDOM)
=> 34
```
when compiled, this will produce the call:
```c
Int__PLUS(1, EDOM)
```
So it provides a quite flexible means of using C macros directly. It is,
of course, also radically unsafe. Anyone registering and using values of
the C type better be cautious.
One can get pretty crazy with this feature:
```clojure
(register comment-it C "// commented out;")
(Int.+ 1 comment-it)
=> int _11 = Int__PLUS_(1, // commented out;)
int* _12 = &_11; // ref
String _13 = IntRef_str(_12);
```
* 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
* feat: a proper dynamic numeric tower
The following things were changed and/or added:
- `Dynamic.neg` was added
- `Dynamic.mod` was changed to work on float values
- `Dynamic.cxr` was changed to work with `0` instructions
- `Dynamic.=` was changed to ignore the type of the number
- `Dynamic.round` was added
- dynamic arithmetic was changed to respect the numeric type tower
- the instances of `Eq` and `Ord` for `Number` are no longer derived, so that they work across numeric types
- the instance of `Num` for `Number` was changed to work across numeric types
- `promoteNumber` was added as a type function to implement the numeric tower.
The numeric tower is as follows:
Byte -> Int -> Long -> Float -> Double
* test: add tests for cxr, neg, and =
* test: add tests for Dynamic.round
* 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
Previously, we didn't check that type variables had consistent kinds in
type definitions, which could lead to improper types. For example, one
could define:
(deftype (Foo f a b) [x (f a) y f])
without issues even though this type is invalid since the variable `f`
is assigned two distinct kinds (nullary: * and unary: *->*).
This commit adds a check to ensure all of the instances of a type
variable in a type definition have the same kind. It also fixes an issue
whereby only higher-kinded types of one argument were allowed as type
members; now higher-kinded types of any arity are permitted (again
assuming variable kinds are consistent).
When a user writes a type that has variables with inconsistent kinds,
they will be hit with an error:
(deftype (Foo (f a) b) [x (f a) y f])
Invalid type definition for 'Foo':
The type variable `f` is used inconsistently: (f a), f Type
variables must be applied to the same number of arguments.
Traceback:
(deftype (Foo (f a) b) [x (f a) y f]) at REPL:1:1.
* refactor: Move constraints test to own module, create TestLookup module
* refactor: Extracted 'Env' module
* refactor: Extracted 'TypePredicates' module
* test: First, very simple test
* refactor: Extracted 'Managed' module
* refactor: Add 'lookupBinder' function that doesn't return an Env (often what we want)
* refactor: Move out more stuff from Lookup
* refactor: Use new 'lookupBinder' in tons of places (avoids tuple)
* refactor: Got rid of monolithic 'recursiveLookupInternal'
* refactor: Avoid boolean blindness
* refactor: Better names for some lookup functions
* refactor: More logical order in Lookup.hs
* style: Use correct version of ormolu (0.1.4.1)
* refactor: Slightly more consistent naming
* refactor: Address @scolsen:s feedback
* Rename ty to xobjTy.
* Rename info to xobjInfo.
* Rename obj to xobjObj.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Address name shadowing.
* Remove some primes.
* Emit/Deftype: Add a few more special cases for Unit members
There were a few gaps remaining in our handling of Unit types as members
of user defined types, most notably, generic setters weren't handling
Units appropriately. This commit adds special checks for Unit types in
generic setters, and also accounts for a Unit refs:
- Specialize generic setter functions against Unit members
- Specialize calls to str/prn against Unit members in StructUtils
(rather than accessing the struct member, which doesn't exist in the
Unit case, we simple call Unit's prn function (added in the next
commit).
- Don't bind references to Unit values to variables. This fixes an error
whereby a reference to a Unit would generate invalid C such as:
`void* _9 = &;`
Likewise, we omit references to Units from function arguments just as
we omit Unit values.
* Unit: Add Unit type implementations for common interfaces.
Now that Unit can be used as a member type it is subject to several
interfaces, such as prn, that it previously hadn't implemented.
This commit adds Unit.carp to core which implements `prn`, `copy`, and
`zero` for the Unit type.
* Deftype: Return null pointers for Unit getters
This is *hopefully* one of the final updates needed to fully support
Unit's as member types. Getters for fields of such types have no struct
member to read, but are expected to return a void pointer; so we return
a NULL void pointer instead.
This commit also updates our emissions for function calls to prevent
assigning the results of functions with Unit and (Ref Unit) return types
to variables.
* Emit: Filter void args from lambda calls
Just as we filter void argument types from other function calls, we now
filter them from calls to lambdas.
This commit enables support for using values of type () (Unit) in
user-defined types such as product and sumtypes. After this commit,
types such as:
(deftype Units [action-one () action-two ()])
Are valid, and can be instantiated in the obvious way:
(Units.init (IO.println "foo") ())
Some important things to note about the implementation:
- The C structs emitted for types containing Unit members *completely
omit all unit members*. If a type in Carp has N members, the
corresponding C struct will have (N-U) members where U is the number of
members with the type `Unit`.
For example, this type:
(deftype (Foo [one Unit two Int]))
will produce the following typedef in C:
typedef struct {
int two;
} Foo;
As a special case, types that *only* have Unit's as members are represented and
initialized as completely empty structs:
(deftype Foo [empty Unit])
// emits
typedef struct {
} Foo;
Foo Foo_init() {
Foo instance = {};
return instance;
}
Such a type is merely a container for side effects.
- Side effects are not stored at all in the types that contain Unit
members. Instead, any side effects will be lifted out of the emitted C
struct and called prior to initialization.
For example, initializing `(deftype Foo [empty Unit])` with `(Foo.init
(IO.println "foo"))` will produce the following C:
main(...) {
//...
static String _10 = "foo";
String *_10_ref = &_10;
IO_println(_10_ref);
Foo _12 = Foo_init();
//...
}
- The typical operations on product fields are supported on Unit type
members, but they have slightly custom semantics. Since we don't
actually store any values of type Unit in custom types, most
updaters/getters/setters simply run a side effect.
This is mostly only supported to make the use of such members more
intuitive and allow programmers to chain side-effects within some
context, much like monadic IO in Haskell.
- Match forms also work on Unit types for parity, but again, there is no
meaningful behavior here, since Unit only has a single type
inhabitant.
As a bonus, this commit also makes it possible to use `Unit` and `()`
interchangeably in type signatures.
Extends Carp's support for type reflection by returning types for
values as well as bindings.
`type` now also returns a valid Carp expression/s-expression and so its
output can be used as input to dynamic functions and macros (prior to
this commit, `type` printed the type to the REPL but did not return a
meaningful expression in Carp).
Here are a few illustrations of the behavior:
```
(def x 1)
;; type now returns an s-expression/symbol
(type x)
=> Int
;; It also works on values
(type 1)
=> Int
(type 2b)
=> Byte
(type "foo")
=> (Ref String <StaticLifetime>)
;; It works on more complex values as well
(type Maybe)
=> Module
(type Maybe.Just)
(Fn [a] (Maybe a) <StaticLifetime>)
;; reports honestly about polymorphism
(type (Maybe.Nothing))
=> (Maybe a)
(type (Pair.init 1 2))
=> (Pair Int Int)
;; What about the type of types?
(type (type 2))
=> Type
;; Or the type of types of types?
(type (type (type 2)))
=> ()
;; One more time!
(type (type (type (type 2))))
=> ()
;; so, () is the fixpoint of type, and is reached after two applications
(type zero)
;; the type of an interface is all of its implementations
=> (((Fn [] (Array a) <StaticLifetime>) (Fn [] Bool <StaticLifetime>) (Fn
[] Byte <StaticLifetime>) (Fn [] Char <StaticLifetime>) (Fn [] Double
<StaticLifetime>) (Fn [] Float <StaticLifetime>) (Fn [] Int
<StaticLifetime>) (Fn [] Int16 <StaticLifetime>) (Fn [] Int32
<StaticLifetime>) (Fn [] Int64 <StaticLifetime>) (Fn [] Int8
<StaticLifetime>) (Fn [] Long <StaticLifetime>) (Fn [] (Maybe a)
<StaticLifetime>) (Fn [] (Pair a b) <StaticLifetime>) (Fn [] (Quadruple
a b c d) <StaticLifetime>) (Fn [] String <StaticLifetime>) (Fn []
(Triple a b c) <StaticLifetime>) (Fn [] Uint16 <StaticLifetime>) (Fn []
Uint32 <StaticLifetime>) (Fn [] Uint64 <StaticLifetime>) (Fn [] Uint8
<StaticLifetime>)))
```
As shown in the example above, this change also includes a cosmetic
update to the representation of lifetime variables, which are surrounded
in <> to distinguish them from type variables.
This commit also adds a new `kind` primitive that reports on the kind of
a binding or value:
```
(def x 3)
(kind x)
=> Base
(kind 2)
=> Base
(kind Maybe.Just)
=> Higher
(kind (Maybe.Just 2))
=> Higher
```
`kind` and `type` both support interactive development in the repl, for
example, a user can rely on `kind` to check the kind of a type they plan
on using in an interface that demands a higher-kinded argument.
Likewise, they both also support developing macros based on type
information.
This change makes refs "constructible" in the perspective of the type
system by allowing them to stand for polymorphic constructors such as
`(f a b)`. After this commit one can take an interface such as:
```
(definterface out (Fn [(f a b)] a))
```
and provide an implementation over references:
```
(defn copied [x] @x)
(implements out copied)
```
`copied` is a valid implementation of `out`, since it simply copies the
value of a reference, dropping its lifetime.
Essentially, this makes the type system slightly more flexible, allowing
one to view references as type constructors in their own right.
This commit makes function types unifiable to *only* polymorphic
constructors (StructTy with a VarTy name). This enables one to implement
interfaces defined against constructors against functions so long as:
- The number of function arguments match the number of type constructor
arguments.
Thus, one can define:
```
(definterface constructor (Fn [(f a b)] (f a b)))
(defmodule Test (defn constructor [f] (the (Fn ([a b] a) f))))
```
But how is this useful?
Here's one scenario this corrects in practice. In Haskell, the `some`
function is typed generically as `some :: f a -> f [a]`. `some` takes a
type and successively applies it until it returns an empty value, then
it returns a list of results of the applications. This is great for
types that actually have state, such as parsers, but for many values of
`f` it makes no sense. E.G. given a `Maybe` the function will never
terminate, since `Maybe.Just x` will never transform into the empty
value on its own. This problem is even worse when we don't have inherent
laziness to help us short-circuit application where possible.
In fact, using an obvious definition of `some`, a function is the only
(non-bottom) type in the `f a` position that may lead to eventual
termination without requiring rewriting `some` to explicitly match
against values. One could tuck a function away in a type constructor and
devise a clever enough instance of choice to make this work, but it's
simpler to define it against a function.
Another case: type equivalences. When types are unifiable with
constructors, it gives us an easy way to define concepts generically
across types and functions.
```
(definterface app (Fn [(f a) a] a))
(defmodule Func (defn app [f x] (f x)))
(defmodule Maybe (defn app [m x]
(match m (Maybe.Nothing) (Maybe.Nothing)
_ (Maybe.Just x))))
:i app
app : (Fn [(f a), a] a) = {
Func.app
Maybe.app
}
(definterface compose (Fn [(f a) (f b)] (f c)))
(defmodule Func (defn compose [f g] (fn [x] (f (g x)))))
:i Func.compose (Fn [(Fn [a] b c), (Fn [d] a c)] (Fn [d] b e))
;; In this case, we define composition as the explicit application of
;; the product
(defmodule Maybe (defn compose [ma mb]
(let [x (match ma (Maybe.Nothing) (zero)
(Maybe.Just a) a)
y (match mb (Maybe.Nothing) (zero)
(Maybe.Just b) b)]
(Maybe.Just (Pair x y)))))
:i compose
compose : (Fn [(f a), (f b)] (f c)) = {
Func.compose
Maybe.compose
}
```
In a more general sense, this would enable us to use functions as a
constructor type, analogous to the use of (->) in haskell. The gist is,
this commit will let us extend our higher-kinded generic functions to
functions.
This commit adds basic kinds checking on interfaces, such that an
interface that takes a type constructor (f a) can't be implemented by a
function using a concrete type like `Int` in that position.
In order to support polymorphic constructors (higher-kinds) we'll need
to resolve type variables in the constructor position, such as (a b).
This is a first step toward supporting polymorphic constructors. We add
a new type to represent concrete struct names, and have updated the
struct type resolving code to handle the presence of variables and
resolve to the new ConcreteName type.
If a C reserved word, for example, short or long, is used as an
identifier in carp, the resultant C code contains an invalid identifier
and fails to compile. This change adds a function for mangling reserved
words into valid C identifiers and includes two initial terms, short and
long.
We apply the string mangling after symbol/character mangling as the
string resulting from a successful character mangle is already a valid
identifier even if it contains a reserved word.