Skip to content

Instantly share code, notes, and snippets.

@baroquebobcat
Last active August 29, 2015 14:05
Show Gist options
  • Save baroquebobcat/d966e7ecfd034dd11f4e to your computer and use it in GitHub Desktop.
Save baroquebobcat/d966e7ecfd034dd11f4e to your computer and use it in GitHub Desktop.
Dreams of Mirah 1.0

Dreams of Mirah 1.0

What is Mirah?

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

Principles

  • 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 :)

Variable Scoping / Typing / Casting

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.

Blocks

  • 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

If / Conditional Expressions

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 +.

begin;rescue;ensure

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:

Methods

Class Bodies

Some earlier scoping notes: https://gist.github.com/baroquebobcat/8c608c58b2e1dad1ead2

Access Control

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

Macros

  • 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

File scoped extension methods

  • 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.

Compile Scoped Extension Methods

It might be nice to allow entire projects to use a set of extension methods.

Generics

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

Closures

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

Concurrency

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.

Dynamic Types

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.

Debugging / IDE support

  • support netbeans work
  • investigate plugging eval4j into things

Compiler Plugins

  • library-able
  • mvn friendly, ie discoverable w/ mvn compiler plugin

Compiler Architecture / Phases

  • Make Compiler phases more explicit
  • Easier to follow
  • document them!

Uncategorized

  • 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.

Java InterOp

  • 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment