The Artima Developer Community
Sponsored Link

Weblogs Forum
Getting Dynamic Productivity in a Static Language

78 replies on 6 pages. Most recent reply: Apr 5, 2009 3:19 PM by Cedric Beust

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 78 replies on 6 pages [ « | 1 2 3 4 5 6 | » ]
Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 3:24 PM
Reply to this message Reply
Advertisement
> this only works if you can modify the classes to implement
> your new interface. if you want to implement a new method
> that is suppose to accept as a parameter one of 2 classes,
> that share a common method signature but have no common
> subclass or subinterface, what alternative is there to
> structural typing?

I guess... But how often do you find yourself in the situation where there are two existing classes for which you don't have the source, which don't share a common ancestor or interface but which both happen to have a method of the same signature, and you want to invoke that particular method?

I know it hasn't happened to me very often (if ever :-)).

Note that it's still far from being a dead-end in Java: you just create two methods that invoke that same method on the two different objects.

And like I emphasized in the blog post I linked above, it's still much more type safe since your code will stop compiling if any of these methods gets renamed or its signature is modified, while Structural Typing won't notice anything until you actually try to run it.

--
Cedric

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 3:27 PM
Reply to this message Reply
Bill writes:

"If the thing you pull out has the method you call, it will work. Else it will throw an exception at runtime. That's about all you need to explain it."

Given a choice between more or less understanding types, vs. knowing nothing about them and fighting with intermittent runtime exceptions, I prefer the former. YMMV.

When the Java generics get too messy, I often just hack around them, with comments, instead of fighting all day with ? extends crap.

see some earlier Artima discussions such as

http://www.artima.com/forums/flat.jsp?forum=270&thread=234090

Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 3:28 PM
Reply to this message Reply
> Same thing, 1/100th the boilerplate

Less boilerplate but also impossible to refactor automatically.

It's the typical trade-off of statically versus dynamically typed languages. After about twenty years in this industry, I have a strong preference for an IDE or a compiler letting me know I screwed up as soon as I'm done typing, but your mileage may vary.

--
Cedric

James Iry

Posts: 85
Nickname: jiry
Registered: Nov, 2007

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 3:33 PM
Reply to this message Reply
> > Same thing, 1/100th the boilerplate
>
> Less boilerplate but also impossible to refactor
> automatically.

Say what? Why? If I say "refactor foo to take type {def shutdown} instead of {def close}" the compiler can find all calls to foo and check to see if it can rename methods and ask if I want it to. If not it can offer to automatically generate an adapter. It seems quite refactorable to me - are you confusing current state of the art with what's possible?

Or perhaps you'll ask something about code I don't have access to...e.g. if foo were in a library I wrote and others were using it. Well, then it doesn't matter if I use a nominative type or a structural type, I still have the same problem. If I modify the Closeable interface to have a shutdown method I'm breaking all code that implements that interface.

James Iry

Posts: 85
Nickname: jiry
Registered: Nov, 2007

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 5:06 PM
Reply to this message Reply
> And like I emphasized in the blog post I linked above,
> it's still much more type safe since your code will stop
> compiling if any of these methods gets renamed or its
> signature is modified, while Structural Typing won't
> notice anything until you actually try to run it.

Ah, I missed this paragraph. Now I understand why your blog post seems so confused.

Structural typing != dynamic typing. It is a form of static typing. Let me demonstrate with a little scala

// declare that foo takes anything with a close method
scala> def foo(closeable : {def close}) = closeable.close
foo: (AnyRef{def close: Unit})Unit

// declare that bar takes a String and will call foo on it
scala> def bar(x : String) = foo(x)
<console>:5: error: type mismatch;
found : String
required: AnyRef{def close: Unit}
def bar(x : String) = foo(x)
^


Type error. There's no close method on String. Note that this error hasn't happened at runtime - I haven't called bar yet, I've merely declared it.

Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 5:17 PM
Reply to this message Reply
> > this only works if you can modify the classes to
> implement
> > your new interface. if you want to implement a new
> method
> > that is suppose to accept as a parameter one of 2
> classes,
> > that share a common method signature but have no common
> > subclass or subinterface, what alternative is there to
> > structural typing?
>
> I guess... But how often do you find yourself in the
> situation where there are two existing classes for which
> you don't have the source, which don't share a common
> ancestor or interface but which both happen to have a
> method of the same signature, and you want to invoke that
> particular method?
>
> I know it hasn't happened to me very often (if ever :-)).
>
> Note that it's still far from being a dead-end in Java:
> you just create two methods that invoke that same method
> on the two different objects.
>
> And like I emphasized in the blog post I linked above,
> it's still much more type safe since your code will stop
> compiling if any of these methods gets renamed or its
> signature is modified, while Structural Typing won't
> notice anything until you actually try to run it.
>
I think you meant duck typing in the last sentence there, not "structural typing", right?

Anyway, I'd agree with Cedric here. Structural typing isn't needed very often compared to nominal typing. When I first heard structural typing was going into Scala, my initial reaction was "why?" I asked Martin, and he said users were asking for it. Well since then I have found a total of only two occasions to use it, but for those occasions it solved my problem nicely. I show one of them in the video. I wanted to make it easy for people using the ScalaTest matchers DSL to invoke this syntax on any object that had any kind of length attribute:

object should have length (7)

The other case was the same thing, but for size instead of length. In both of these cases, there were lots of classes and interfaces in the Java API that had the right "structure", but didn't inherit from a common supertype on which you could get a length or size.

One thing I've observed about Scala's type system is it doesn't try to solve the 80/20 problem, give you the 20% of type system that will solve 80% of your problems. That would keep the type system simpler, but then those extra 20% problems would be more of a pain to deal with. Scala's type system has solved 100% of the problems I've encountered so far, but I've also encountered complaints that the type system is "too complicated" as a result. Including structural typing in Scala may have added a bit more complexity to the type system, but it is pretty easy to understand, and every now and then it is exactly the right tool to solve a problem.

Chee Seng Chua

Posts: 62
Nickname: quai83
Registered: Nov, 2006

Re: Getting Dynamic Productivity in a Static Language Posted: Mar 31, 2009 8:12 PM
Reply to this message Reply
Both static and dynamic typing have its own pros and cons, it depends on the problem that you are solving. I personally likes the optional typing in ActionScript 3.0, where you can optionally tell compiler to check/don't check typing for you.

// In normal class, type checking is enabled:-
public class MyStaticClass {...}

// If you declare it as dynamic class, compiler will not check typing for you:-
public dynamic class MyDynamicClass {...}

I personally prefer static typing most of the time, but there's time when I need dynamic typing as well. Having that kind of option is useful to me.

Martin Odersky

Posts: 84
Nickname: modersky
Registered: Sep, 2003

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 5:55 AM
Reply to this message Reply
Hi Bill,


> Anyway, I'd agree with Cedric here. Structural typing
> isn't needed very often compared to nominal typing. When I
> first heard structural typing was going into Scala, my
> initial reaction was "why?" I asked Martin, and he said
> users were asking for it.

That was not the most important reason. The most important reason was that we could get structural typing for free, in what concerns language complexity. Scala always had the concept that you could `refine' a type by putting constraints on its members. for instance like this:

class C {
type T
def x: Any
}

val x: C { type T = String; def x: Int }

The type of x is an instance of class C where we know that the T member type is String and the x member method has result type Int. Having this feature was very important for completeness -- without it we could not claim that member types can encode generics. Besides, it neatly solves the problem what should be the type of an anonymous class.

Given that type refinements were in Scala from the start we got structural typing for free, by simply dropping arestriction in the language. The restriction we dropped was: Each declaration of a refinement must override a member of the refined type.

By dropping this restriction, we allow members in a refinement which do not refer to a member in the parent.
The structural type

{ def close(): Unit }

is just an abbreviation of the refinement

Object { def close(): Unit }

It's not everyday that you get additional power by dropping some part of your spec. That's what caused us to do it.

This discussion reflects a general beef I have with the way Scala is presented. People say it has features A, B, C and so on, until readers feel that it's really a very large language. It's not. The point is that Scala's features are really very general. The same feature can be interpreted in many different ways. So people who don't know Scala come away with a wrong impression, that it has much more different features than really is the case.

Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 7:17 AM
Reply to this message Reply
> This discussion reflects a general beef I have with the
> way Scala is presented. People say it has features A, B, C
> and so on, until readers feel that it's really a very
> large language. It's not. The point is that Scala's
> features are really very general. The same feature can be
> interpreted in many different ways. So people who don't
> know Scala come away with a wrong impression, that it has
> much more different features than really is the case.
>
I don't know, Martin, I think from the user's perspective these things feel like separate features, even if they may be generalizations of each other. You mention that member types can encode generics, so is your feeling is that generics can be considered another representation for the one feature of member types? I think to users they feel like two features, because they have to learn two different syntaxes and sets of rules. They feel quite different.

James Iry

Posts: 85
Nickname: jiry
Registered: Nov, 2007

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 7:23 AM
Reply to this message Reply
> That was not the most important reason. The most important
> reason was that we could get structural typing for free,
> in what concerns language complexity. Scala always had the
> concept that you could `refine' a type by putting
> constraints on its members. for instance like this:
>
> class C {
> type T
> def x: Any
> }
>
> val x: C { type T = String; def x: Int }

I want to second what Martin said here and point out that refinement types themselves are a generalization, a removal of restrictions, from what Java does. Java has refinement types, they just aren't very useful.

Java normally allows covariant return types and adding methods to subclasses but essentially prevents you from using those mechanisms in anonymous classes.
interface C {
Object x();
}

// in some method somewhere this works
C z = new C() {
public String x() {return "hello";};
public String y() {return "goodbye";};
};

// but this does not - type error, cannot convert from Object to String
String aString = z.x();

// nor does this work - y isn't defined on type C
String bString = z.y();


The above doesn't work because Java doesn't let you denote the distinct type of the the anonymous inner class. But what you may not realize is that that's not a fundamental limitation at the core of Java's type system, it's only a restriction in naming. Java actually knows about the refined type
// this works just fine
String aString = new C() {
public String x() {return "hello";};
public String y() {return "goodbye";};
}.x();

// as does this
String bString = new C() {
public String x() {return "hello";};
public String y() {return "goodbye";};
}.y();


So Java has refinements types! You just can't name them so they're practically useless.

The equivalent to the first problematic Java does work in Scala because you can denote the type of that anonymous class instance

scala> trait C { def x : AnyRef }
defined trait C

scala> val z : C { def x : String; def y : String} = new C {
| def x : String = "hello"
| def y : String = "goodbye"
| }


scala> val aString : String = z.x
aString: String = hello

scala> val bString : String = z.y


Of course, you're more likely to just let type inference deal with it
scala> val z = new C {
| def x : String = "hello"
| def y : String = "goodbye"
| }
z: java.lang.Object with C{def x: String; def y: String} = $anon$1@24c22b

scala> val aString = z.x
aString: String = hello

scala> val bString = z.y
bString: String = goodbye


As Martin says, if you stand back and look at Scala as a conceptual whole you'll find that Scala's type system doesn't just add features on top of Java's - much of what it's doing is really more like removing constraints that Java imposes. As my second Java example showed, refinement types are part of Java - they're just so restricted that they aren't useful. Scala removes that restriction and then makes it convenient to express things that are "purely" refinement types. We just sloppily call such "pure" refinement types structural types and use them as such.

Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 9:11 AM
Reply to this message Reply
> Say what? Why? If I say "refactor foo to take type {def
> shutdown} instead of {def close}" the compiler can find
> all calls to foo and check to see if it can rename methods
> and ask if I want it to.

Exactly: "ask if I want it to". Hence my claim that it can't be done *automatically*.

"Ask if I want it to" is better than manual replacing but it's still not automatic refactoring, and it doesn't scale to large code bases, which is why I recommend using structural typing very sparingly.

You simply cannot automatically refactor code that involves structural typing because the compiler cannot connect a type that contains the method "shutdown" with a structural type that contains a method by that name.

I covered this in the blog post I linked earlier, please read it.

--
Cedric

Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 9:13 AM
Reply to this message Reply
> Ah, I missed this paragraph. Now I understand why your
> blog post seems so confused.

Can you point out exactly what part is confused in the blog post?

I fully acknowledge the benefit of static typing offered by structural typing in that post but I also point out that code that uses structural typing cannot be automatically refactored.

--
Cedric

Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 9:22 AM
Reply to this message Reply
> I don't know, Martin, I think from the user's perspective
> these things feel like separate features, even if they may
> be generalizations of each other. You mention that member
> types can encode generics, so is your feeling is that
> generics can be considered another representation for the
> one feature of member types? I think to users they feel
> like two features, because they have to learn two
> different syntaxes and sets of rules. They feel quite
> different.

This is a very important point.

Martin, I think your intimate knowledge of both the design and implementation of Scala might cause you to miss a few aspects of how users perceive the language.

Whether you got structural typing for free or whether it made a lot of sense from a continuity or implementation standpoint, it's an additional syntax for users to learn, and in my experience, it adds little, is rarely needed and it should probably not have been added.

Just a personal opinion based on my experience.

--
Cedric

James Iry

Posts: 85
Nickname: jiry
Registered: Nov, 2007

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 9:24 AM
Reply to this message Reply
> Exactly: "ask if I want it to". Hence my claim that it
> can't be done *automatically*.

Wow, you have just jumped off the deep end. When I do rename refactoring in Eclipse it lets me preview changes and decide if I want to go ahead.

Okay, so if you want a very narrow definition of automatic let me rephrase - the refactorer could automatically write an adapter with no attempt to fix up renames. Ta da, the impossible just made easy! Thank you, good night, you've been a lovely audience.

Cedric Beust

Posts: 140
Nickname: cbeust
Registered: Feb, 2004

Re: Getting Dynamic Productivity in a Static Language Posted: Apr 1, 2009 9:28 AM
Reply to this message Reply
> So Java has refinements types! You just can't name them
> so they're practically useless.

I beg to differ. I use them quite often, typically to pass a listener:

window.addListener(new WindowListener() {
public void onClick() { ... }
})

I have dozens of such instances on the code base I'm working on as we speak and I have used this idiom for as far as I remember programming in Java.

I am pretty sure that the number of times where I thought that structural typing would have been useful can be counted on one hand.

I'll also point out that naming a structural type is optional in Scala as well, and if you are going to do it, you might as well create a fully fledged class/interface/trait.

--
Cedric

Flat View: This topic has 78 replies on 6 pages [ « | 1  2  3  4  5  6 | » ]
Topic: AntiSocial Networking Previous Topic   Next Topic Topic: Mixins considered harmful/3

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use