The composability of the records is the thing that's the new idea —
the idea that you can shove new behavior en passant while cloning the
record. Daniel wants to make this a core of the type system, whereas I
view it as a nice-to-have way of passing slight data structures:
that's a stunt I often pull in JavaScript and Groovy, and having to
define a new type or abuse tuples both struck me as a bit odd. And
structural typing is in general an under-appreciated way of
approaching object(ish) programming styles.

Now, everything that follows is not at all in response to Brian's
post, but it's another wrinkle that I was considering.

Daniel and I were talking over Twitter, and he seems OK with Scala's
decision to not have completeness checking for most match statements.
This always struck me as out of tune with functional programming, and
now that I'm thinking about FP and OO hybrids, I can finally figure
out why: encapsulation works differently in FP than in OO.

In the way I'm used to thinking about FP, encapsulation operates at
the verb level — give me a way to do X, and I'll do some funky stuff
and then do X, or I'll do a deviation on X. At least in Haskell and
OCaml, the provided verb needs to know all the gory, intimate details
of the data structure it operates upon, and keeping code like that in
sync with the changes to the data structure requires completeness
checking. In Java's style of OO, however, encapsulation operates at
the noun level — give me a Foo (which can do X, among other things),
and I'll do some operations on it and then ask it to do X. I
explicitly don't know about the data structure I'm operating over:
that's what encapsulation means in OO. So completeness is not only
unnecessary, but it's actually countervailing. Now, these aren't
necessarily mutually exclusive paradigms, but navigating the way that
they may behave together is tricky, to say the least.

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



On Thu, Jul 28, 2011 at 9:58 PM, Brian Hurt <[email protected]> wrote:
> No one else seems to have replied to this, so I might as well rant.
>
> On Wed, Jul 27, 2011 at 10:04 PM, Robert Fischer
> <[email protected]> wrote:
>> 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
>>
>
> tl;dr: no, this doesn't solve the problem.
>
> First of all, how Haskell type classes and Ocaml functors are
> implemented, under the hood, are records of closures.  This is not a
> new idea.  Hell, if you want old school, records of function pointers
> aren't uncommon in C either.  Now get off my lawn.
>
> The problem isn't implementation, the problem is typing subtypes.  If
> you're not doing subtyping, you're not doing OO.  "Duck types" are
> just implicitly generated supertypes- so even in duck typed languages
> like Ruby and Python you're still doing subtyping.  Changing objects
> to structures doesn't change the need for subtyping- and subtypes are
> just a royal frimping pest to type.
>
> Robert, you've heard this, but for those playing along at home- one of
> the big problems with subtyping is co/contra-variance.  Say you've got
> two types, A and B, and B is a subtype of A.  That means all B's are
> A's, but not all A's are B's.  Under what circumstances can an
> array/list/vector/container of A's be used as a container of B's, and
> vice versa?  The answer is- it depends upon what you're doing with the
> array.  If you're only taking things out of the container, a container
> of B's can be used as a container of A's- but a container of A's can
> not be used as a container of B's.  Likewise, if you're putting things
> into the container, a container of A's can be used as a container of
> B's, but not vice versa.
>
> Basically, any time you have an A and you want to cast it down the
> type hierarchy making it a B, you've either tossed the type system
> completely out the window and reverted to dynamic typing, or you
> require the type system to effectively have full theorem-proving
> capablities, like Coq, Twelf, or Agda.  Note that Ocaml doesn't allow
> downcasts (except via Obj.magic- which again is throwing the type
> system out the window without even any run time checking to back
> things up), so pretty much everything people think of as object
> oriented programming doesn't work.  And this is with a type system
> that was willing to deal with co- and contra-variance, it just didn't
> go full path-dependent.
>
> If you're willing to ditch subtyping, then yes, the problem becomes
> easy to solve.
>
>> 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 [email protected].
>> To unsubscribe from this group, send email to 
>> [email protected].
>> For more options, visit this group at 
>> http://groups.google.com/group/jvm-languages?hl=en.
>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups 
> "JVM Languages" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> For more options, visit this group at 
> http://groups.google.com/group/jvm-languages?hl=en.
>
>

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

Reply via email to