Daniel Spiewak apparently just presented at Oscon records-of-lambdas
as a place where statically typed OO/FP could learn to love each
other.  PDF: http://bit.ly/pYFTbG Keynote: http://bit.ly/qIPBos

Charlie Nutter said he could see himself using it, which made me all
kinds of happy, because that's precisely the space I'm digging at with
my language, Ashlar.  Charlie asked via Twitter that I kick off a
conversation on this stuff here on the JVM Languages group, so that's
what I'm doing.

The gist in Ashlar is that every name refers to a function. Some
functions return records of named lambdas, which provides OO style
functionality. This is statically typed, however, in a structural way
(akin to OCaml's class system). This is precisely what Daniel was
talking about.  I'm also doing a few other things with Ashlar, but
they're icing on the cake on top of this core approach.

Although I could in theory try to generate all the possible
mix-matches of types (by basically implementing a system like Scala
Traits, but apply-able at *any time*), in practice this is very hard,
and generated an annoying amount of code. My earlier take on this
point, the language Cornerstone, was chasing this angle.

With Java 7, though, I shifted to a MethodHandle approach. Although
this is very statically typed in the user space, the resulting code is
So, take this Ashlar code:

let foo = { x, y -> x.plus(y) }

This is compiled into a class with a "foo(Object x, Object y)" method,
and a "MethodHandle foo" static final field (as well as, for the
moment, a redundant "MethodHandle foo" instance final field), whose
value is a method handle pointing to the foo method. The
implementation is to do an indy to retrieve the "plus" field off of
"x" (we'll return to this later), and then invoke that using x and y.
The type checking to ensure that x has a member "plus" holding a
lambda which can accept a y is done at compile time, and bound at
runtime. So, in this sense, Ashlar is a very statically typed language
to the user with a very dynamic feeling implementation.

Now, since I want to cling to decidable completeness, I'm sticking to
a variant type like approach for the base classes.  So you can declare
something like this:

type Foo = Bar(a:1) | Baz(a:2) with {
   let plus = { x -> a.plus(x) }
}

This compiles into three classes:
*) an abstract base class "Foo" with an abstract method "a" requiring
an integer to be returned, an instance field "a" that points to a
method handle returning an integer, as well as the "plus" method and
field, and
*) the two implementing classes "Foo$Bar" and "Foo$Baz" that implement
the "a" abstract method.

If you want to use an extended version of Foo, you can use the clone version:

type BigFoo(Foo) |= Bash(a:-1)

This generates a new abstract class extending Foo (Foo$BigFoo), as
well as a new concrete class (Foo$BigFoo$Bash).  Note that Bash cannot
be passed in for a Foo, although any Foo can be passed in for a
BigFoo.

Types are also assignable:

type Fab = Foo

For the moment, this is just handled at the compiler level, and
doesn't change code generation.

Types are also extensible:

type Fab = Foo with {
  let minus = { x -> a.minus(x) }
}

This generates a new abstract class with the new member.

And types are re-nameable:

type Foo = Foo with {
  let times = { x -> a.times(x) }
}

How this works by generate code to hack all instances of Foo within
scope (see hacking instances below). There's syntactic sugar which
allows you to leave out the "Foo =" bit right after "type", but it's
the same thing.  You can't extend Foo globally, but that's mostly my
conservatism coming through. It feels like it's stepping into a
hornets' nest, though.

You can also describe a "virtual type", which is akin to an interface,
except that anything matching its structure counts:

type Virt = with {
  let times:Integer->Integer
}

Once you have an instance of Foo, you can mangle it to your heart's content.

let hack_foo = { b:Foo -> b with { let times = { x -> a.times(x) } } }

(You could leave off the explicit typing of "b", but then hack_foo
would be badly named.  And I have to admit, I like Daniel's syntax
better at this point, and may well steal it, but there's an issue of
consistency I'd need to take into account.)

When you hack a type, the MethodHandle (with the instance curried in)
is added to the "members" construct, which is queried by the indy to
fetch methods. Currently, that's a hash table look-up followed by
checking all the hard-coded names of fields. I'm considering an
implementation which would take the name of the method as a string and
compare it to a known string, and then return the MethodHandle if they
match, null otherwise. When hacked a second time, this would then be
wrapped by another MethodHandle to do another comparison. This would
be done for some small "n" (3?), wherein we're still being nice to the
JVM.  But that's all optimization talk.

Type inference on all of this is pretty straightforward down the H-M
path, except that instead of failing to resolve types in ambiguous
circumstances, we generate "synthetic virtual types" (i.e. structural
descriptions of usage) and try to map onto them best we can.

There are two main difficulties I have encountered at this point:
pattern matching and error messaging. I had an issue with pattern
matching implementation, but that was solved last week (at least in
theory). The remaining issues both of them stem from the same
underlying open question: how do I actually *talk* about this stuff to
a human being?

Say we have this code:
let x = { a -> a.fetchSomething().doSomething(2) }

The type of x is:
'a -> 'b (borrowing the OCaml convention of 'a, 'b, ..., 'z being
"scratch"/unbound types)
The type of 'a is: { fetchSomething:Unit -> 'c  }
The type of 'c is { :doSomething:Integer -> 'b }

So, if we have this code, how should the error message read?

type Boring = DoNothing
let x = { a -> a.fetchSomething().doSomething(2) }
x(DoNothing())

Similarly, how do the patterns in the matches look to describe structures?

I haven't been working on Ashlar much for the last few weeks (studying
for my GREs and doing my masters program's field education placement),
but I've got an ASM 4.0 implementation that is about ready to be
pushed to GitHub.  I'm also gutting a bunch of the infrastructure
integration (OSGi, Ivy), which works, but really slows things down,
and it's confronting me with a few problems that probably shouldn't be
solved until I get to the point where there's actually a language to
speak of.

~~ Robert.
Love Your Enemy: A Campaign to Regain Human Dignity Through Nonviolence
http://www.mettacenter.org/mc/projects/love-your-enemy

-- 
You received this message because you are subscribed to the Google Groups "JVM 
Languages" group.
To post to this group, send email to jvm-languages@googlegroups.com.
To unsubscribe from this group, send email to 
jvm-languages+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/jvm-languages?hl=en.

Reply via email to