- dynamic scoping
- |> operator like F#
- CSP-like model
- haskell like combinator stuff
- restricting combinations of covariance and contravariance
- ML/Haskell style partial application
- active patterns
- pattern matching against open types
- really good, prescriptive, error messages
- anonymous types
- type aliases
Direct approach to purity
functions, methods, and modules are either:
nonblocking blocking throw x -- compiler support/inference nothrow -- compiler support/inference pure -- compiler support/inference total -- compiler support/inference
policies are attached to things as attributes. they are checked recursively at compile-time
there seem to be three operations that can be done with these:
- claim -- claim that a method meets a constraint
- assert -- assert a constraint. ignore evidence to the contrary
how do you make this stuff user-extensible
module ... let ... (define function) end type class ... let ... end
Compile time computation
alexandrescu loved it about d. I love it too, but we can do better.
compile time module imports cause code to run inside of the compiler!
caching compiler-server is the preferred way to build standalone compiler should be as fast as possible for other users
clear separation between identity types and structural types
don't tie the idea of identity and the idea of references together like clr
TODO: look at GADT's again and decide what to steal
feelings: scala's approach to this is too obtuse. GADTs are neat directly.
Maybe sums should use a GADT-like encoding always to emphasize their generality
What does a meta-type look like?
interface MetaType equals? : Type -> bool subtype_of? : Type -> bool end
Possible macro syntax (presumes statements)
macro stmt 'for' '(' (init:stmt) ';' (cond:expr) ';' (end:expr) ')' (body:stmt) ,init goto cond top: ,body if ,cond then goto top end cond: goto loop end
pain point: macro names vs identifiers can create ambiguity. Almost seems like they should be in separate namespaces but this is syntactically hard
Call sites with bidirectional data flow are evil.
Execution model simple enough for computation expressions, (but static?), (but composable?)
Think about hybridization of C++ const and "real" immutability.
- const_cast is bad. const should be unbreakable.
but do people really understand const...really?
The problem with object orientation is twofold:
- some program objects have no correspondence in the real world
- some real-world objects can-not be accurately represented in the program
that's it. We need good building blocks, not philosophy. Programs are not about modeling real world objects. They're about building virtual stuff.
trace is really important. example is incomplete matches:
match async_api foo bar with | Success(x) => | * => Debug.fail(tracecontext: true)
Canonical code formatting rules
tool to enforce
Banging out binding syntax...incomplete
// let bindings bind lexically scoped identifiers let x = 4 let x = 5 // ok to shadow let rec f = ... // defines lexically scoped function let rec map f Nil = Nil f (Cons hd tl) = Cons (f hd) (map f tl) where map : (a->b) -> List a -> List b let rec map :: (a->b) -> List a -> List b = | f Nil = Nil | f (Cons hd tl) = Cons (f hd) (map f tl) let rec map (f : a -> b) (l : List a) : List b = l match Nil -> Nil | Cons hd tl -> Cons (f hd) (map f tl) def map(f : a->b, l : List[a]) : List[b] = let rec map func list = list match Nil -> Nil | Cons hd tl -> Cons (f hd) (map f tl) let rec map func list = list match Nil -> Nil | Cons hd tl -> Cons (f hd) (map f tl) let map f l = match l with | Nil -> Nil | Cons hd tl -> Cons (f hd) (map f tl) let foo () = .. .. .. // var bindings bind lexically scoped variables var x <- 4 x <- 5