Let me see if I understand what you're getting at.
In traditional OOP languages like C++ or C#, the action of a method depends on the runtime type of the receiver:
Window w1 = new GlassWindow();
Window w2 = new PlexiglassWindow();
w1.Break(new Brick());
w2.Break(new Brick());
Which method is actually invoked depends on the runtime type, not the compile time type, of the receiver, provided that Break is "virtual".
Languages which have this property are called "single virtual dispatch" languages.
Your proposal is that the action of Break depend also upon the runtime type of the argument. Suppose Break takes an Object:
w1.Break(new Hammer());
w1.Break(new Pillow());
w2.Break(new Hammer());
w2.Break(new Pillow());
and now perhaps four different things happen depending on the runtime types.
This sort of double dispatch operation is very common in things like games, where you want to have different logic for every possible kind of collision: player with laser, laser with laser, laser with player, player with wall, laser with wall...
Languages that have this property are called "double dispatch" languages; languages that can do more than double dispatch are said to be "multiple dispatch".
This is a pretty well-studied area; do a web search on "visitor pattern" to learn more about simulating double dispatch in a single dispatch language.
When I think of minimialism, I think of Lisp and Go. With Lisp, all you have are functions and lists, which is about as simple as you can get (well, there's a little more, but whatever). However, I think the Go case is more interesting.
Go was designed to be simple (it's a decent read). The term they use is "feature orthogonality", which means that any feature should only be added if it provides something truly unique. This seems to stem with the authors' (Russ Cox and Rob Pike come to mind) involvement with Plan9, which was a reimagination of UNIX with simplicity in mind. (If you're interested in minimal design, Rob Pike's paper on a simple windowing system is a good read.)
Here's some simple examples of the syntax:
Only one looping construct
A loop can look like one of the following:
Infinite loop
for {
}
While loop
for <conditional> {
}
Traditional for loop
for i := 0; i < 10; i++ {
}
Foreach loop
// works for maps or arrays
for k, v := range arr {
}
Multi-purpose switch
switch {
// cases must be evaluate to a boolean
}
switch <value> {
}
switch t := <value>; t {
// can use t inside
}
Multiple return
return val1, val2, ...
- Removes the need for throw (pass the error as last return value)
- Removes the need for out parameters
- Removes the need for tuples
Interfaces
type X interface {
DoSomething()
String() string
}
- Solves similar problems as generics
- Allows abstraction
Embedding
type A struct {
Thing string
}
type B struct {
A // embeds A in B, so B.Thing refers to A.Thing
}
- Solves same problem as inheritance
- Obviates need for classes
Channels
Can be used to implement semaphores
var c = make(chan bool, 1)
c<-true // semaphore lock
<-c // semaphore free
Used for message passing between threads
func produce(c chan<- bool) {
for {
c <- true
}
}
func consume(c <-chan bool) {
for {
<-c
}
}
var c = make(chan bool)
go produce(c)
go consume(c)
Can be used to handle asynchronous events
func async() chan bool {
var c = make(chan bool)
go doSomethingAsync(c)
return c
}
// wait for long async process to finish
c := async()
select {
case _ = <-c:
}
Conclusion
I'm not going to go into every part of the syntax, but hopefully you can see what minimalism can do. The language is intriguing not because it adds a ton of new features, but because it uses the best features from other languages without anything extra.
There's usually one "best" way of solving a problem. For example, on the mailing list, a lot of users complain about not having generics. After discussion, they realize that everything they want to do can be done with interfaces. Read up on effective go for examples on idiomatic syntax.
The benefit of KISS languages is it's possible to write idiomatic code, because code style is restricted by the language. For example, in Go, you cannot write something like this:
if <condition>
statement;
You have to use curly-braces:
if <condition> {
statement;
}
There are many other examples of this in the syntax, which makes reading other peoples code easier.
Benefits of KISS language over featureful languages:
- easier to understand others' code
- easier to grok the entire language (C++ is notorious for being hard to understand)
- focus on algorithm, not the syntax
Best Answer
You may want to look at Julia.
It's a modern, dynamically typed language with Lisp inspired meta programming. It also JIT compiles using LLVM. The standard library is written in Julia too and is compiled and cached on first use I.e. A form of install time AOT compilation. As the LLVM IR is statically typed, I think this has most of the features you were looking for although, as with many dynamic languages, REPL usage was an important consideration hence the JIT compilation.
One interesting paper I saw (section 5.2). The standard library has around 135k variables of which about 80k had a fixed, static type. The rest had to stay represented as the variant type Any.
Depending on why you're interested in dynamic to static, this may be of value when you look at how dynamic you make your language. Obviously, this has some bearing on performance although, in practice, Julia seems quite quick.