Is there a dream language that merges the benefits of dynamic and strong typing?
Asked Answered
P

5

6

I would be interested to learn a language that handles objects internally as hashtables (like JavaScript) but could wrap them with strong types to offer the benefits of code completion/intellisense in design time. Here is how I wish this dream language to work:

public class Lion
{
  public void Roar() { Console.WriteLine("Aaarrgghh");}
} 

public static Main(string[] args)
{
  object myCat = new object(); // just plain object, no type!
  // adding a Roar() method to the myCat instance 
  myCat.Roar += delegate() {Console.WriteLine("Miauew");}
  // At this point myCat should qualify to be a Lion.
  // So we should be able to successfully duck-type-cast 
  // her to a lion
  Lion myLion = myCat as Lion;
  // now the myLion reference is strongly typed, 
  // so I expect the Intellisense window pop up 
  // and offer me the Roar() method when I hit the dot after "myLion"
  myLion.Roar();
}

I wish this program to compile without error, run without exception and print "Miauew" on the Console. Is there a language out there that can do this? Maybe C#4.0?

Pages answered 28/6, 2009 at 13:29 Comment(1)
I have thought about something like this as Lua extension. I use Lua in an embedded environment, where every byte of memory is precious. Lua also uses hashtables internally, which is very inefficient in terms of memory for certain types of objects. It would be nice to have a "struct" like syntax to simply create tightly packed object in Lua, instead of having to rely on custom objects in C.Smilax
C
3

Maybe the new dynamic type in C# 4.0. look at this: http://blogs.msdn.com/cburrows/archive/2008/10/27/c-dynamic.aspx

Cloudberry answered 28/6, 2009 at 13:38 Comment(2)
After reading about C#4.0, it looks like that "dynamic" type is just allows you postpone type binding to runtime, but does not allow you to add new member the object instances in runtime like Python or JS does.Pages
It does. The new BCL type ExpandoObject does exactly that without any additional work on your part. You can also get a lot more sophisticated by essentially creating your own runtime binder.Pinsk
E
2

Every time I've tried to plan any such thing, the problem is the cast. Of course you can just take the programmer's word for it, but that combines the worst features of both typing systems. Maybe the cast could check at runtime whether an object implements the Lion interface, but that's basically impossible. In this case it looks plausible since all you need is the existence of a void (*)() function. But in general what if Lion has a getHabitat method that returns an object which is supposed to be of another interface? A strongly typed language needs to be able to say for sure that it really does, but without actually calling the method, you can't in general tell whether untyped code returns a Habitat, and hence in strong-typing terms you can't work out whether it's a Lion.

So I've never got past the problem that when mixing weak- and strong-typed sections of code, you end up with strongly-typed code that at any moment might call a method and get back an object that doesn't implement the interface it's supposed to. That's not strong typing, it's dynamic typing with misleading code comments.

Equities answered 28/6, 2009 at 13:29 Comment(3)
In Javascript, you may just call the method as you feel like. If it is not there, you get a runtime exception. Why cannot the compiler just take my word that it is a Lion and the method call just will work. If I am wrong, of course I get the runtime exception in my face just like in JSPages
If the compiler takes your word for it, then there is no runtime check - it puts in the call Lion.Roar() without further ado. But you want the runtime check, like in JS, and myCat.Roar() to do something else. Which is it?Selfrevealing
The runtime does not need to check when the casting is done. In fact, I expect the casting be a design-time syntactic sugar only. Underneath, I just expect it to work as JS. So do the check when the method is called. And check the existence of only that method, do not worry about the rest. So casting does not have any meaning in runtime since all objects are just JS hashtables, it only helps the programmer at design time.Pages
S
2

It rather depends on what you intended by having the Lion type implement a non-virtual method.

Extension typing ( the set of objects which are of a given type are those which have the properties of the type, which is close to being a static equivalent of duck-typing ) requires that the structure of an object implies its type, but your Lion type has no structure - it only says that anything which is a lion has that particular Roar method. So for your example, I can't see any way you can cast your cat to be a Lion, since it doesn't have the only structural property you have said that Lions have.

If on the other hand you intended by saying that anything you say is a Lion may have either its own Roar method or will use the method in Lion, it's not saying anything about the type of objects which are Lions, and AFAIK you could get the same behaviour with an extension method on object ( though I don't know enough C# to know whether self methods override extension methods in C# ).

In a dynamic IDE, there's no reason that you need to cast to a Lion before the IDE can work out that myCat has a Roar property - that information is statically deducible.

The reason IDE support is hard in languages with dynamically constructed types, such as JavaScript, is that you can do this:

let myCat = {}

if ( locale.name == 'en' )
    myCat.roar = function () { alert ( "Miauew" ); }
else
    mCat[ resources.getString( 'roar' ) ] = 
        function () { alert ( resources.getString ( 'Miauew' ) ); }


// the structure of the type of myCat depends what locale the code
// is running in, so you can't deduce the Intellisense
myCat.

Such situations of course mean that you can't predict whether the code will work either, without much deeper analysis ( for example, checking that your French cat is always asked to roar in French ).

If Intellisense can't deduce the structure of objects whose structure is statically deducible, then that's a weakness in Intellisense, not of dynamic typing.

What would you consider the benefit of strong typing in your example?

Selfrevealing answered 28/6, 2009 at 13:59 Comment(5)
In C#, extension methods are just syntax sugar for calling a static method on some class in the namespace. They only exist to turn the ugly ObjectExtensions.DoSomething(new Object()) into new Object().DoSomething()Phocomelia
Benefit of strong typing: I particularly fond of intellisense. It is unmissable on gigantic projects. Compiler errors are nice, but I can live without it.Pages
In the code you gave, structural type inference is sufficient for Intellisense. You don't need strong typing; in fact, in a strongly typed language you'd have to rely on the inference, as you cannot cast in strongly typed languages.Selfrevealing
And is there a language like that that allows me to specify classes that allows intellisense AND weakly typed like JS? Because classes in JS is just an aftertought.Pages
Smalltalk has both classes and dynamic typing, as do Ruby and Python. Smalltalk IDEs have had type aware autocompletion for ages, I believe that some Ruby, JavaScript and Python IDEs have type aware autocompletion. Intellisense is an IDE feature rather than a language feature.Selfrevealing
L
1

Its an interesting idea, and one that has been explored before. I'll link to some research and existing work, after I give you my take. I'll talk about scripting languages and static imperative languages, since I think this is what you're talking about.

The fundamental problem is that types don't mean the same thing in static and dynamic type systems. In a dynamic system, a type is a property of a value, and as the value is passed around, so is the type. In a static system, you restrict the types of values which may be held by variables (etc). So far so good.

Static types in dynamic languages

The problem occurs when you look at how objects are used in scripting languages - they are all duck-typed. This means the name of the type (nominal-typing) isn't useful. All modern imperative languages (Java, C#, C++, C) use nominal type systems. So in C++ you might say you expect a Duck, but in Python you really want something that goes quack().

So consider adding static typing to a duck-typed language. Since a function will expact a parameter to quack(), but it can't say that it expects a Duck, combining the two is difficult. You might define an interface called quacks which can quack(), and use that as the type. But this is a tad verbose really, which kills the benefit of the dynamic typing. Perhaps though, there might be something along these lines (some kind of structural type system) which can do it.

An alternative would be to just require the programmer to specify Duck, and damn this duck-typing business anyway - no one really uses it do they? But then you're just writing Java in Python, and as someone who tried that once, let me tell you its very counter-productive.

Dynamic types in static languages

So lets look at it the other way. How will C# benefit from the dynamic keyword? The simple answer is that it won't. I honestly don't see any of the beauty and freedom that you get from Python in C#'s dynamic. Now, the only thing I know about it comes from a talk by Jon Skeet, but the overwhelming impression I got is that it's verbose and inelegant. And I think that's not an implementation error from the C# folk. I think its because the problems which dynamic typing solves in Python are already solved in C# (albeit verbosely), and dynamic just brings nothing to the party.

Research on static/dynamic stuff

Look up Jeremy Siek's gradual typing stuff, its about the most advanced static/dynamic research out there. Its a bit hard reading though, and I've only given it a cursory look myself, so I can't summarize it. However, its interesting to flick through his related work alone, and the STOP conference will probably have good stuff in it.

Luana answered 2/9, 2009 at 14:4 Comment(0)
M
0

Not closely related to your example, but Scala's implicit conversion is a well thought-out (though sometimes hard to follow) mechanism to extend a functionality of a class in a completely controlled, statically checked way.

Also, there is structural typing which can sometimes be useful as a replacement for duck typing in a type-safe way.

Mignonne answered 12/8, 2011 at 23:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.