Runtimeless JVM language that tries as hard as possible to feel like writing Ruby without cheating.
- Should produce class files that are drop in replacements for Java.
- Should interop well with Java and Java tools
- It'd be nice if it interopped well with JRuby too, as well as other JVM langs, through compiler extensions.
- It should have an extensible compiler
- As a user, it should be easy to take advantage of library writer's macros, and it should be possible to write your own without shooting yourself in the foot
- User happiness over compiler writer happiness
-
no runtime. Compiler tricks have to happen at compile time. Any new features have to avoid core jar dependencies (libraries can make different choices, of course).
-
Ruby behavior over Java behavior Mirah should feel like a do what I mean, not what I say kind of language. Where possible, we should try our hardest to optimize for programmer happiness. Sometimes this won't be possible.
-
Good Java Interop Mirah should interact well with Java and javac, since it's a drop in replacement. This has implications for macro implementation, and method name generation.
-
Macros are Methods Mirah's internal DSL for macro creation forces macros to be method invocations inlined at compile time. Unless you "know what you are doing", macros shouldn't modify things parentward in the AST, with some exceptions.
Things that should be easy to do should have easy representations.
Features fit well in Mirah if
- they make users happy
- they don't cause runtime dependencies
- they are cool :)
What introduces new scopes, and how do they behave? Introducing a new scope means means that variables defined in that scope are not visible outside it.
- always introduce new scope
- shadows existing variables
current behavior macro blocks don't introduce new scope, but they should
i = "eye"
10.times { |i| puts i}
puts i
No new scope. This is tricky for cases like
if ARGV[0]
a = 1
end
puts a
a is 1: Integer, or null: Integer
if ARGV[0]
a = 1
else
a = "hello"
end
puts a
a is 1: Object, or "hello": Object
if ARGV[0]
a = 1
else
a = "hello"
end
puts a + 2
Is a compile error as Object doesn't know how to +
.
Specifically rescue clauses: What we want to do is support these usages
begin
if ARGV[0]
raise ErrorTypeOne
else
raise ErrorTypeTwo
end
rescue ErrorTypeOne => e
puts e.one
rescue ErrorTypeTwo => e
puts e.two
end
x = begin
1
rescue ErrorTypeOne => e
2
rescue ErrorTypeTwo => e
3
end
puts x
begin
x = 1
rescue ErrorTypeOne => e
x = 2
rescue ErrorTypeTwo => e
x = 3
end
puts x
begin
raise ErrorTypeOne
rescue ErrorTypeOne => e
puts e.one
end
begin
raise ErrorTypeTwo
rescue ErrorTypeTwo => e
puts e.two
end
Compiler Error?
begin
raise ErrorTypeOne
rescue ErrorTypeOne => e
end
puts e
How about?
e = nil
begin
raise ErrorTypeOne if ARGV[0]
rescue ErrorTypeOne => e
end
puts e
Working idea is that rescue clauses introduce a new cast scope
If there's a case like this:
Some earlier scoping notes: https://gist.github.com/baroquebobcat/8c608c58b2e1dad1ead2
Default method access control is public (current behavior). To change it, I(Nick), want Ruby style access control modifiers.
class Foo
def pub; 1; end
private
def pri1; 2; end
def pri2; 2; end
end
We could do Javaish
class Foo
def pub; 1; end
private def pri1; 2; end
private def pri2; 2; end
end
We could have the other Ruby access syntax
class Foo
def pub; 1; end
def pri1; 2; end
def pri2; 2; end
private :pri1, :pri2
end
-
blocks should always introduce new scope, whether the block is a macro or a closure. This isn't true right now.
-
hygene. Either document lack of hygene well, or maybe make macros hygenic. I think it should be possible. Essentially, you'd need to find all not unquoted var declarations and give change them and their references to temp names.
-
tree walking helper methods. make doing upstream modifications less difficult, and make common types of modifications easy.
-
make it harder to reach into the compiler directly from macros.
-
allow macros that need to know the types of their arguments and not just AST node types. Maybe those could be called something different like extension methods.
https://gist.github.com/baroquebobcat/10328625
- add some notion of an extension method, eg
100.millis
could live in it. Then you could as a user, import it somehow and make it scoped to that file.
It might be nice to allow entire projects to use a set of extension methods.
currently limited to inferring generic types of Collections
TODOS
- Syntax
- InterOp
Refs https://github.com/mirah/mirah/wiki/Generics https://gist.github.com/KeenS/97f63f83ed035499ea10
mutable bindings? lambdas vs blocks
Mirah Lambda Proposal: https://gist.github.com/baroquebobcat/8248405
TL;DR: 2 types of closures. One, the block, has non-local return. The other, the lambda, doesn't have it. Both should do include the scope they exist in in their scope, eg methods, fields, should be directly referenceable.
https://gist.github.com/baroquebobcat/6297238
https://groups.google.com/forum/#!searchin/mirah/generic$20syntax/mirah/WT7erIjcwHA/nZESKbLDOCgJ
Naive answer is: Just like Java's. We can do more though. We've got Java's std lib to play with so we could make assumptions about what primitives we have available, and create macros in the compiler for them.
Also, we need to figure out how to give access to things like volatile
.
Specifics
- synchronize intrinsic ala Java's
o = Object.new
o.synchronize do
puts "This. Is. Critical."
end
We could even have a method modifier
class Safe
synchronize def foo;end
synchronize def bar;end
end
Or ala Ruby
class Safe
def foo;end
def bar;end
synchronize :foo, :bar
end
It might be a good idea to have a core extension method for locks
@lock.sync do
# do stuff
end
To make things like.
@lock.lock
begin
ensure
@lock.unlock
end
harder to screw up
We could even combine Ruby meta style and get
class Safe
def foo;end
def bar;end
synchronize :foo, :bar, with: @lock
end
Other concurrency idea: Quasar Lib w/ macros / extension methods.
previously we had support for a dynamic
type. I think it'd be nice to bring that back, especially since dynamic is baked into Java 8.
- support netbeans work
- investigate plugging eval4j into things
- library-able
- mvn friendly, ie discoverable w/ mvn compiler plugin
- Make Compiler phases more explicit
- Easier to follow
- document them!
-
Auto gen equals/hashCode/toString
-
change == to -> !=nil && equals for refs. otherwise boxing'll fuck up numeric equality
-
goto: Mirah can have goto available which would allow neater construction of state machines. How to expose it would be the question One thought I had was to make it so that Goto exists as an artificial ASTNode, that can be injected via macros, making it possible to create state machine DSLs, but without making goto an userland language feature directly.
-
case expressions Thinking on this is: have an extension/macro method that case maps to, with intrinsic impls for switch-able types. This gets tricky for types, especially if we don't have arg-type based macro dispatch, but it'd be super cool.
-
write a JMH plugin
-
casting https://groups.google.com/forum/#!searchin/mirah/generics$20syntax/mirah/aA6vP9wo9Q0/aujwUEvPkFwJ
-
Constants
-
Symbols. We could translate symbols into enums + look up symbols with some compiler trickery. Might not be worth it, might be.
- Extension Methods on Java classes should be easy to make
- Mirah generated class files should not require mirahc on the classpath for javac to be happy
- generated operator method names should either be java friendly, or at least have java referenceable aliases. Macro annotations shouldn't exist on class files that javac might read.