Pointer Types
Assignment vs. passing to function...
I think rather than having a bunch of sigils (*
, &
, @
, $
) for pointer
types, I've realized that really the difference is in move/copy semantics:
- Unique pointers:
- Copy (
&
): Invalid - Move (
*
): Transfers ownership
- Copy (
- Shared pointers:
- Copy (
&
): Increments resource count - Move (
*
): Decrements resource count (then increments it, all atomically)
- Copy (
- Weak pointers:
- Copy (
&
): Gets the shared pointer if valid, and increments resource count - Move (
*
): Invalid
- Copy (
- Values:
- Copy (
&
): memcpy - Move (
*
): Invalid
- Copy (
Struct Fields
How to specify either a pointer or the whole thing, i.e.:
typedef struct {
const char *name;
unsigned char age;
const char *species;
} Pet;
typedef struct {
const char *name;
unsigned char age;
Pet *pet;
Pet whole_pet;
} Child;
And then &
can stick around as the reference-to operator.
Probably exactly how C does it; using *
.
To Do
Define a range
inside a function.
Remove struct
, it's type
, component
, :fields
and :components
or bust
now.
Can you iterate over a container of unique pointers? No. What if you super
need to? Do you move
them all and then move
them back? Seriously?
Need to add private struct fields.
It's pretty inconvenient to build a completely new discriminated type whenever the discriminated field changes.
Returning value types is a thing. In C we do this with output parameters, and for safety the output parameter is only written to on success.
Varargs (...
)
There are three string types, in actuality:
dynstring
: string bufferstring
:char*
str
:const char[]
Empty return should not require parentheses
Defining a type based on a primitive (like dynarray
or string
) should work
like:
string ErrorMessage("Error!!!!")
dynarray Games(type: string)
These aren't bindings (they don't start with var
, and they only work in file
scope, so it's probably not that tricky. The partial params (in the Games
example) are maybe a little tricky but whatever.
Ehhh I don't know how useful this is. I think probably it's only useful to parameterize the types. Probably requires a little more clever compiler magic to check what type arguments a constructor takes and see that only those are specified, but that's fine.
To Decide
-
Probably get rid of special type identifiers altogether (
). They're identifiers just like anything else because they're first-class, so they can be looked up just like anything else. -
Rethink exceptions
-
Should add replacements in
array
anddynarray
formemcpy
memset
-
There's some weird overlap between property control (only define
get
for a property) and mutability. -
Docstrings
- Research popular documentation generators (Sphinx, Doxygen, etc.?)
-
Serialization
- Cyclical references are the main problem here.
- I think this is probably library functionality, but we can help it with introspection
-
Tools
- Statically linked binaries
- Formatter
- How to do testing?
- Some kind of devel mode so that:
- Dead code is allowed
- Dead imports are allowed
- Is it possible to continuously compile a project, and warn about (the many) potential problems during development? This would save a huge amount of frustration.
- Fetching packages
-
type (struct) packing
- define a separate
:pack
field?
- define a separate
Probably Not
- Parametric polymorphism
Allocation
Allocating a big chunk of data and then assigning fields to that data, instead of allocating several chunks of data.
- I think texels are the example? Allocate 20 texels (f32 x, y, z, GLuint texID) in 4 rows instead of 20 columns.
- This is something like "describing" the data right. Instead of creating a description then asking the OS to allocate that, you ask the OS to allocate a big slab and then describe or annotate the fields.
I'm feeling a separate kind of function here, like alloc
or something. Needs
more consideration.
To Write About
-
No type inference in struct fields
-
You can set static types/structs as type/struct fields
- This also goes for containers
-
Generics
- Use
@
instead of that shitty
syntax - Use
gtype SuperTable<@key_type, @value_type>
- Use
gtrait Hashable(@type thing)
- Use
gfn add(@type a, @type b)
- Use
-
Compiler directives:
- Overflow (
@overflow
) - Type sections (
@variants
) - Generic type placeholders (
type SuperTable<@K key, @V value>
) - Global namespace (
@globals
)
- Overflow (
-
const
- keyword for variables
- Single line
const
blocks for single constants
-
Operator overloading
@op:iter
,@op:getitem
,@op:setitem
(borrow heavily from Python here)- I feel like these should also be traits
- Context manager (
@op:enter
and@op:exit
)
-
It doesn't seem possible to implement fields in Traits any more efficiently than just calling a function if you're starting from an unknown type.
- From an LLVM point-of-view
-
Scopes
- Explicit
@global
scope; solves weird variable scope and shadowing problems too@global
is like a big struct; all its fields have to be declared ahead of time. It's not a magic JavaScript/Python blob of properties.
- Constants
- Const structs can't have heap members, nor can their members have heap members, etc. etc.
- Explicit
-
Introspection:
- Introspection is a compile-time thing
- Access a type/struct/whatever with
Person:
:Person:fields
Person:name
- At runtime, you can access an instance's type:
charlie:type:fields
charlie:type:name
- You can also access the current block at runtime:
:this:type
:this:arguments
(for functions):this:name
and:this:number
forconst
blocks (for example)
- You can access super:
:super:type:fields
- what stuff is available in what blocks
- We're essentially talking about the AST right?
-
Variants
- validation on assignment
- define a whole new set of fields, even if they're redundant
- Solves memory layout problem
- Not super happy with the syntax here
- Size of a variant type is always the max; like a tagged union
- But the implementation is such that each variant has its own struct type... internally
- And the size of an instantiated variant type is the size of its variant variant
- variants can change if all fields are updated
-
FFI
-
Concurrency/Parallelism
- Basically what Rust does (Send/Sync)
-
Modules