Pandora Google Go On Pandora

How do you see its success?


  • Total voters
    52

Aethix said:
Crazy abuse of dot notation, for one thing. I think it uses forced OOP, like Java and VB.NET.
Who cares, if the OOP is fast?
 
Last edited by a moderator:
dflemstr said:
Aethix said:
Google go looks awfully Java-ish in syntax. Why would anybody want to program in it?
C, C++, C#, Javascript, Objective-C, GLSL, HLSL and Google Go look awfully Java-ish in syntax. Why would anyone want to program in those languages?
did you intentionally leave out B, D and Cg?

on another note, wtf at the abuse of curly brackets in all those languages. i mean, WTF!
 
Last edited by a moderator:
Its syntax is C like, that's neither here nor there. It's not a particularly good syntax, but it's one we're all used to.

It has some interesting things, but it doesn't seem more interesting than D (which is of about the same high-levelness). It's not even OOP, more like struct-based.
 
darkblu said:
did you intentionally leave out B, D and Cg?
Yep.

sindbad said:
It's not even OOP, more like struct-based.
Which is a Good Thing™. OOP is a horribly broken concept, and languages that try to fix that are in one of two categories:
1. Languages like e.g. Python where you try to circumvent the bad effects that OOP has (You have conflicts with multiple inheritance? Well, whatever, just throw a random error; the developer will fix it eventually. Don't like the problems with common safe OOP type systems? OK, let's just skip types all together).
2. Languages like e.g. Scala where you try to fix the issues with OOP (And it actually succeeds, but there are still some rough edges. From the core library linked list implementation: "sealed abstract class List[+A] extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]] { /* ... */ }")

Go enables you to use polymorphism, "inheritance" and interfaces without any of the OOP problems. It's a win IMO.
 
Last edited by a moderator:
dflemstr said:
darkblu said:
did you intentionally leave out B, D and Cg?
Yep.

sindbad said:
It's not even OOP, more like struct-based.
Which is a Good Thing™. OOP is a horribly broken concept, and languages that try to fix that are in one of two categories:
1. Languages like e.g. Python where you try to circumvent the bad effects that OOP has (You have conflicts with multiple inheritance? Well, whatever, just throw a random error; the developer will fix it eventually. Don't like the problems with common safe OOP type systems? OK, let's just skip types all together).
2. Languages like e.g. Scala where you try to fix the issues with OOP (And it actually succeeds, but there are still some rough edges. From the core library linked list implementation: "sealed abstract class List[+A] extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]] { /* ... */ }")

Go enables you to use polymorphism, "inheritance" and interfaces without any of the OOP problems. It's a win IMO.
I don't see how Go's system allows you to side-step the issues with OOP. It leaves the responsibility of getting it right to the individual developers, which I think is a bad idea.

for 1., you really haven't used Python much, have you? Conflicts with multiple inheritance are properly reported, but hard to fix. This is why the idiom of doing multiple inheritance in Python is mixins.

for 2., Scala has a highly over-engineered type system. I doubt regular developers understand enough of it to hack on it, which could lead to problems later on in the language's life. Still better than Java's half-done type system, though.
 
Last edited by a moderator:
Gary13579 said:
Lua has a bad name in the gaming scene, I think from the PSP days and LuaPlayer. But really, almost every application could be better with a lil lua inside.
I'm not sure where you're coming from with the "bad name", but in the commercial game development scene Lua is still very much the preferred and popular choice in scripting languages.
 
Last edited by a moderator:
(As a side note: when new programming languages come out, I will always try to argue *for* them instead of against them, mainly because I want to encourage people that are experienced with other languages to give me their point of view on the language that builds on *their* background, so that I can form a picture of the new language myself)
sindbad said:
I don't see how Go's system allows you to side-step the issues with OOP. It leaves the responsibility of getting it right to the individual developers, which I think is a bad idea.
It does? I don't see how.

For example, take
Polymorphistic compatibility
In other OOP languages, say that you have a "sort" function that accepts lists of stuff. Then, let's say that you want to use that function, without changing it, to also sort Strings. Then, let's say that that sort function uses some method on List called e.g. "elementAtIndex(x)", which Strings don't have, and you consequently have to "add" that method to String in order to sort it.

Python solves this using duck typing, which is a good enough solution, except that you have to actively add a method to instances of String, and you can't really say that "every time someone anywhere requests a call to 'elementAtIndex' on any 'String' anywhere, do this". Also, it isn't type safe, which is a problem for another discussion some other time.

Scala solves this using implicit type casts, so that you can create an implicit method that the compiler automatically uses to transform "mystring.elementAtIndex(0)" to "string2list(mystring).elementAtIndex(0)". This is called the "can be treated as" pattern (in contrast to the "is a" pattern that you use with inheritance). The already compiled "sort" function won't use that implicit however.
Another alternative in Scala is to mix in List into String, so you say "(new String("....") with List[Char]).elementAtIndex(0)", which works like duck-typing except that it's type safe. None of those solutions are relatively fast, however; both a wrapper and a mixin require a call equivalent to "malloc()" in C, and dynamic memory allocation is never a good thing.

In Go, however, you can simply add a method:
Code:
func (s string) ElementAtIndex(i int) Element {
  //...
}
...and *poof*, the string struct has a ElementAtIndex method all of a sudden, and it's as fast as if you had compiled in the function from the start when defining "sort()"

This does already eliminate the need for inheritance, "real" polymorphism, class definitions and so on.

sindbad said:
for 1., you really haven't used Python much, have you? Conflicts with multiple inheritance are properly reported, but hard to fix. This is why the idiom of doing multiple inheritance in Python is mixins.
Yes, exactly. How does this not match what I said?

sindbad said:
for 2., Scala has a highly over-engineered type system. I doubt regular developers understand enough of it to hack on it, which could lead to problems later on in the language's life. Still better than Java's half-done type system, though.
Yes, I agree, except about that it's hard to understand - it's not, really. You can describe it using around 600 words or less. The problem with it is rather that you, as a library designer, because it's so over-engineered, have to give the end-user extremely much freedom if you want him/her to be able to use your library in any way that he/she wishes (e.g. if you want the user to be able to add a sort method to integers, so that its bits can be sorted *efficiently* using qsort. You can do that in Scala).
 
Last edited by a moderator:
dflemstr said:
(As a side note: when new programming languages come out, I will always try to argue *for* them instead of against them, mainly because I want to encourage people that are experienced with other languages to give me their point of view on the language that builds on *their* background, so that I can form a picture of the new language myself)
sindbad said:
I don't see how Go's system allows you to side-step the issues with OOP. It leaves the responsibility of getting it right to the individual developers, which I think is a bad idea.
It does? I don't see how.

For example, take
Polymorphistic compatibility
In other OOP languages, say that you have a "sort" function that accepts lists of stuff. Then, let's say that you want to use that function, without changing it, to also sort Strings. Then, let's say that that sort function uses some method on List called e.g. "elementAtIndex(x)", which Strings don't have, and you consequently have to "add" that method to String in order to sort it.

Python solves this using duck typing, which is a good enough solution, except that you have to actively add a method to instances of String, and you can't really say that "every time someone anywhere requests a call to 'elementAtIndex' on any 'String' anywhere, do this". Also, it isn't type safe, which is a problem for another discussion some other time.

Scala solves this using implicit type casts, so that you can create an implicit method that the compiler automatically uses to transform "mystring.elementAtIndex(0)" to "string2list(mystring).elementAtIndex(0)". This is called the "can be treated as" pattern (in contrast to the "is a" pattern that you use with inheritance). The already compiled "sort" function won't use that implicit however.
Another alternative in Scala is to mix in List into String, so you say "(new String("....") with List[Char]).elementAtIndex(0)", which works like duck-typing except that it's type safe. None of those solutions are relatively fast, however; both a wrapper and a mixin require a call equivalent to "malloc()" in C, and dynamic memory allocation is never a good thing.

In Go, however, you can simply add a method:
Code:
func (s string) ElementAtIndex(i int) Element {
  //...
}
...and *poof*, the string struct has a ElementAtIndex method all of a sudden, and it's as fast as if you had compiled in the function from the start when defining "sort()"

This does already eliminate the need for inheritance, "real" polymorphism, class definitions and so on.

sindbad said:
for 1., you really haven't used Python much, have you? Conflicts with multiple inheritance are properly reported, but hard to fix. This is why the idiom of doing multiple inheritance in Python is mixins.
Yes, exactly. How does this not match what I said?

sindbad said:
for 2., Scala has a highly over-engineered type system. I doubt regular developers understand enough of it to hack on it, which could lead to problems later on in the language's life. Still better than Java's half-done type system, though.
Yes, I agree, except about that it's hard to understand - it's not, really. You can describe it using around 600 words or less. The problem with it is rather that you, as a library designer, because it's so over-engineered, have to give the end-user extremely much freedom if you want him/her to be able to use your library in any way that he/she wishes (e.g. if you want the user to be able to add a sort method to integers, so that its bits can be sorted *efficiently* using qsort. You can do that in Scala).

You can just say def f(i): ....; str.ElementAtIndex = f. It's not particularly idiomatic, but that's because there are much better solutions to that problem in Python. For Ruby it is somewhat idiomatic to monkey patch, so the syntax is nicer.

If you use multiple inheritance willy-nilly, you will have hard to debug problems. If you use mixins, however, it is very unlikely to get problems and if you do get them, they're easy to debug. Check out ABCs, as well.

You can add a sort method to integer in python as well, if you really want to. But it's bad manners to do so, why exactly are you so adverse to subclassing?

I think you've gone too out of touch with dynamic languages to remember just how much better they are.
 
Last edited by a moderator:
sindbad said:
You can just say def f(i): ....; str.ElementAtIndex = f. It's not particularly idiomatic, but that's because there are much better solutions to that problem in Python. For Ruby it is somewhat idiomatic to monkey patch, so the syntax is nicer.
Yeah yeah, I know all of that (except for that you could add methods to str; I thought it was a built-in type that can't be touched)...
Only problem is when you have something like this:
Code:
def render(x):
    """Draws a renderable object to the screen"""
    if hasattr(x, 'render'): #reasonable check to see if x has the right type
        Graphics.pushMatrix()
        Graphics.translate(x.pos) #oops! x might not have a pos attribute
        x.render()
        Graphics.popMatrix()

#object oriented; works
class Renderable:
    pos = 1, 2
    def render():
        print 'Stuff'

x = Renderable()
render(x)

#duck typing; maybe doesn't work
#(imagine Box being in a separate, non-touchable library or whatever)
class Box:
    points = (1,1),(1,0),(0,1),(0,0)

def r(): print "Rendering box"

Box.render = r
x = Box()
render(x) #oops, doesn't work; no way to check the contract and x doesn't have "pos"
And yeah, that's sloppy code, but the thing is: with interfaces or strong typing, you aren't *able* to be sloppy, while with code like this, you *can* be sloppy but get buggy code out of it.
sindbad said:
If you use multiple inheritance willy-nilly, you will have hard to debug problems. If you use mixins, however, it is very unlikely to get problems and if you do get them, they're easy to debug. Check out ABCs, as well.
Dude, I'm using Scala, so my code basically consists of nothing else except mixins. I think that they are nice too, but they don't necessarily solve all problems. And ABCs seem to again be a hack solution to a problem that could have been solved properly; they solve the same problem that implicit conversions do in Scala (and the solution isn't that good either as I said before) or also the problem that's solved by abstract classes or interfaces in any other language, in which case this is a Python bugfix and not a feature.

sindbad said:
You can add a sort method to integer in python as well, if you really want to. But it's bad manners to do so, why exactly are you so adverse to subclassing?
I'm not adverse to subclassing; if I need abstraction, I use OOP, and if I need performance, I use non-OOP. OOP is a convenience abstraction that makes your software run slower, with the benefit that you're able to grasp what your code is doing more easily. Go offers the best of both worlds IMO.
sindbad said:
I think you've gone too out of touch with dynamic languages to remember just how much better they are.
I've gone too out of touch with them, but for different reasons ;)
 
Last edited by a moderator:
dflemstr said:
sindbad said:
You can just say def f(i): ....; str.ElementAtIndex = f. It's not particularly idiomatic, but that's because there are much better solutions to that problem in Python. For Ruby it is somewhat idiomatic to monkey patch, so the syntax is nicer.
Yeah yeah, I know all of that (except for that you could add methods to str; I thought it was a built-in type that can't be touched)...
Only problem is when you have something like this:
Code:
def render(x):
    """Draws a renderable object to the screen"""
    if hasattr(x, 'render'): #reasonable check to see if x has the right type
        Graphics.pushMatrix()
        Graphics.translate(x.pos) #oops! x might not have a pos attribute
        x.render()
        Graphics.popMatrix()

#object oriented; works
class Renderable:
    pos = 1, 2
    def render():
        print 'Stuff'

x = Renderable()
render(x)

#duck typing; maybe doesn't work
#(imagine Box being in a separate, non-touchable library or whatever)
class Box:
    points = (1,1),(1,0),(0,1),(0,0)

def r(): print "Rendering box"

Box.render = r
x = Box()
render(x) #oops, doesn't work; no way to check the contract and x doesn't have "pos"
And yeah, that's sloppy code, but the thing is: with interfaces or strong typing, you aren't *able* to be sloppy, while with code like this, you *can* be sloppy but get buggy code out of it.

You'd never actually do that in Python. You'd either just call .render and catch the exception (and it will throw a different exception if Box.render is doing the wrong thing), or if you really want to check, do isinstance(x, Renderable). Python isn't javascript, there are strict, explicit types and things don't change types just because you call a method.

Saving programmers from themselves by adding hard restrictions has never worked as well as strongly suggesting good practice (and if the language makes good code natural, even better).

dflemstr said:
sindbad said:
If you use multiple inheritance willy-nilly, you will have hard to debug problems. If you use mixins, however, it is very unlikely to get problems and if you do get them, they're easy to debug. Check out ABCs, as well.
Dude, I'm using Scala, so my code basically consists of nothing else except mixins. I think that they are nice too, but they don't necessarily solve all problems. And ABCs seem to again be a hack solution to a problem that could have been solved properly; they solve the same problem that implicit conversions do in Scala (and the solution isn't that good either as I said before) or also the problem that's solved by abstract classes or interfaces in any other language, in which case this is a Python bugfix and not a feature.
I find ABC to be a pretty good solution to a hard, complex problem. I don't think Scala solves it any better, but perhaps beter for someone who thinks static typing is a good idea.
dflemstr said:
sindbad said:
You can add a sort method to integer in python as well, if you really want to. But it's bad manners to do so, why exactly are you so adverse to subclassing?
I'm not adverse to subclassing; if I need abstraction, I use OOP, and if I need performance, I use non-OOP. OOP is a convenience abstraction that makes your software run slower, with the benefit that you're able to grasp what your code is doing more easily. Go offers the best of both worlds IMO.
I think D is a better Go, except for the green threads in go. I also don't see why statically typed pre-compiled, non-OO code must certainly be slower than JITed dynamic OO code. It may be so with current compilers/JITs, but that's changing. JavaScript has very fast JITs that work very well for OO code because they were designed for that. Have a look at PyPy as well.
dflemstr said:
sindbad said:
I think you've gone too out of touch with dynamic languages to remember just how much better they are.
I've gone too out of touch with them, but for different reasons ;)
 
Last edited by a moderator:
sindbad said:
You'd never actually do that in Python. You'd either just call .render and catch the exception (and it will throw a different exception if Box.render is doing the wrong thing), or if you really want to check, do isinstance(x, Renderable). Python isn't javascript, there are strict, explicit types and things don't change types just because you call a method.
Exceptions are expensive, and I really mean expensive. They have to capture the entire call stack, and for e.g. recursive functions (let's say that you want to render a mesh with graphical nodes) it's really slow. Even JIT won't convert an exception throw/catch into a simple jump (The HotSpot JVM can do that, but it's pretty unique). A type check is fast, since it only involves one integer comparison, which is a O(1) operation.

And if you check if x is an instance of Renderable, well, then you aren't using a dynamically typed language any longer, since you are checking types. And duck-typing a Renderable then becomes impossible, too, btw.

sindbad said:
Saving programmers from themselves by adding hard restrictions has never worked as well as strongly suggesting good practice
Which restrictions? Using your above argument, you're basically saying that to get bug-free code, you have to either do type checks in Python, or use expensive exceptions. The former method puts you on the same level as statically typed languages do, except that you have to do more typing in Python, and the latter method is just inferior...

(Sidenote:
In Go, Scala and languages like e.g. Haskell, you are able to create monadic functions that have what are called "structural types", which are types that only describe as much about a variable that you have to know.
The above Python function could be written thusly in Scala:
Code:
def render(x: {def render(): Unit; def pos:(Int, Int, Int)}) = /*...*/
...and it will work on *anything* with those two methods.
In Go, you would use interfaces, and there are other mechanisms in other languages. Clean, efficient, and you don't have to do "checkattr"s or instance checks.
sindbad said:
(and if the language makes good code natural, even better).
I've never seen a language that does that.

sindbad said:
I find ABC to be a pretty good solution to a hard, complex problem. I don't think Scala solves it any better, but perhaps beter for someone who thinks static typing is a good idea.
I don't think Scala has a better solution either, but it's at least a more foolproof model. The main difference, I think, between static and dynamic typing systems, is that with static types, the one who uses a {library, piece of code, unit} has to adjust to that library and live by that library's standards, while with dynamic typing, it's the library that has to adjust to all its users. There are multiple shades of gray, of course; Scala has structural types, traits, type variables and type parameters, which give the caller a lot of freedom, and Python has instance checks and ABCs, which givesthe library creator a lot of freedom.
Go is somewhere in between; it has interfaces, which are much like structural types in a way, but they can also be resolved at runtime, which make them kinda like duck-typing constructs.
sindbad said:
I think D is a better Go, except for the green threads in go.

D not only has horrible threading support, but also no way of using those threads beyond the usual lock model. Go has channels, which remind me of Unix pipes or Scala/Erlang actors, and they are far superior to anything you can do with shared state or locks in other procedural languages.
sindbad said:
I also don't see why statically typed pre-compiled, non-OO code must certainly be slower than JITed dynamic OO code.
Well, it isn't :p
If you meant the opposite of what you said, then I'll say that statically typed languages can be optimized more than dynamic languages indeed. With statically typed languages, you can do various code predictions, like for example removing a piece of code that the optimizer knows will never be called.
E.g. if I have this (useless demonstration) code snippet in my code:
Code:
/** Produces a list of 4 ints */
def f(x: Int) = concat(List(x, x), List(x, x))
/** Produces a list of 4 floats */
def g(x: Float) = concat(List(x, x), List(x, x))
/** Concatenates two lists with any type of elements */
def concat[T](x: List[T], y: List[T]) = x ::: y
Let's assume that we don't know until runtime whence "concat" is going to be called (let "concat" be in a separate library or whatever), and we then put a runtime optimizer to work on the function as we run it.
The optimizer will see that concat is used in two places only, and will also see that in both of those places, it's being called with machine-level types (ints and floats respectively). It will then specialize "concat" into two versions, one that only takes lists of floats and one that only takes lists of ints (instead of the current version that takes any type), and it could for example use a shallow linked list implementation (list elems with 1 pointer each to their value and 1 poitner each to the next list element) instead of a deeper list implementation that is needed for heap-allocated objects (list elems with a pointer to a pointer for their values), or, if required, replace any "equals" occurences that are needed for objects with the machine-level element comparisons. These optimizations aren't possible with dynamically typed languages, since you'll never know whether code branches can be excluded. Who knows that f will be called with ints only? What if someone suddenly sends a string instead? Etc.
sindbad said:
It may be so with current compilers/JITs, but that's changing. JavaScript has very fast JITs that work very well for OO code because they were designed for that. Have a look at PyPy as well.
Java HotSpot is the fastest JIT compiler on the market (arguably; LLVM can be faster sometimes). It, too, is designed for OO code.

Java 6 is roughly 1.5 times slower than C compiled by GNU gcc. Python with CPython is 258 times slower than C. Unladen swallow has at best reached one 18[sup]th[/sup] the speed of C. I don't know exactly how fast PyPy is, but Unladen swallow is supposed to be faster. Google Go has already reached one 10[sup]th[/sup] the speed of C, and that is without any runtime optimization whatsoever, and almost sans any compile-time optimizations, even, and it's already faster than any Python implementation.
sources

Dynamic languages have a LONG way to go when it comes to speed. Functional languages used to say that they would reach the speed of C someday, and I actually still think that they can reach that goal, but unpredictable dynamic languages? Improbable.
 
Last edited by a moderator:
<OFFTOPIC>

dflemstr said:
lulzfish said:
The syntax is confusing and it's not nearly as good as The One True Language, which is Lua.
This is Blasphemy!

This is madness!

</OFFTOPIC>

On-topic, I'm going to be interested as to how Google Go turns out, as it does look like it has potential. I could be wrong however, as my programming knowledge is limited.
 
Last edited by a moderator:
dflemstr said:
Exceptions are expensive, and I really mean expensive. They have to capture the entire call stack, and for e.g. recursive functions (let's say that you want to render a mesh with graphical nodes) it's really slow. Even JIT won't convert an exception throw/catch into a simple jump (The HotSpot JVM can do that, but it's pretty unique). A type check is fast, since it only involves one integer comparison, which is a O(1) operation.

And if you check if x is an instance of Renderable, well, then you aren't using a dynamically typed language any longer, since you are checking types. And duck-typing a Renderable then becomes impossible, too, btw.
Exceptions are the proper way to do it and JITs should give us cheap exceptions (if they don't, the should be improved). You're confusing dynamic typing with duck typing. A dynamic language has the freedom to either duck type or check for specific types or shapes of objects. It is often a good idea to check what kind of objects you are given through the entry points to your library.

dflemstr said:
Which restrictions? Using your above argument, you're basically saying that to get bug-free code, you have to either do type checks in Python, or use expensive exceptions. The former method puts you on the same level as statically typed languages do, except that you have to do more typing in Python, and the latter method is just inferior...
My point was that in Python you rarely need to check types, and when you do you have the entire expressiveness of the language at your disposal to check for anything you want. Indeed in most functional languages you get similar freedom in checking. Also have a look at Python 3's function argument annotations, which can be used for checking types as well.

dflemstr said:
D not only has horrible threading support, but also no way of using those threads beyond the usual lock model. Go has channels, which remind me of Unix pipes or Scala/Erlang actors, and they are far superior to anything you can do with shared state or locks in other procedural languages.
I know that Go has Erlang-like concurrency and that's the only thing that I find better in Go than in D.

dflemstr said:
sindbad said:
I also don't see why statically typed pre-compiled, non-OO code must certainly be slower than JITed dynamic OO code.
Well, it isn't :p
If you meant the opposite of what you said, then I'll say that statically typed languages can be optimized more than dynamic languages indeed. With statically typed languages, you can do various code predictions, like for example removing a piece of code that the optimizer knows will never be called.(snip)
Yes, I did mean the opposite :) What stops a JIT from specialising functions at runtime? In fact, that's exactly what Python's psyco does (and similar to what PyPy's JIT does). Lazy is almost always better.

dflemstr said:
sindbad said:
It may be so with current compilers/JITs, but that's changing. JavaScript has very fast JITs that work very well for OO code because they were designed for that. Have a look at PyPy as well.
Java HotSpot is the fastest JIT compiler on the market (arguably; LLVM can be faster sometimes). It, too, is designed for OO code. (snip)
[/quote] Yes, HotSpot is quite possibly the best JIT on earth right now. LLVM's JIT is quite frankly crap in comparison (even mono's is better). It's quite good as a static compiler, but not yet as useful as they make it out to be for making JITs.

Well, PyPy's current JIT is much faster than Unladen Swallow at synthetic benchmarks. The one disadvantage PyPy has (besides the crappy regex engine) is that it can't use CPython C extensions, only ctypes.
 
Last edited by a moderator:
sindbad said:
Exceptions are the proper way to do it and JITs should give us cheap exceptions (if they don't, the should be improved).
I don't know about you but I only use exceptions in my code when something exceptional occurs, aka that isn't part of your program's logic. As a side example, take for instance the parsing of ints in strings (which is done with the int() function in Python):
If I do "int('chabala')", I get a ValueError, but why is this so? Why should I get an error because an int failed to parse?

If I program e.g. a GUI, I expect that the user will enter invalid data from time to time, and am prepared for it, so why should I taint my code with a try/catch, possibly catching other exceptions that I would like to pass through (sometimes you simply forget to rethrow wanted exceptions, especially when they share the same type as another exception you want to *catch*)? I'm not completely against exceptions, so Go's way of returning a tuple of "(returnvalue, error)" from common methods, "returnvalue" being nil and "error" being an error if the function failed, isn't something I want either (you have to have an if clause after every return, brr)

But to have a simple int parser return an exception, that contains a heap-allocated stack of method calls that is averagely 128 bytes big maybe thousand times a second when parsing long files, that's simply stupid, and there should be other ways to deal with it, like the "Option[]" concept or the "Maybe" Haskell type which are much better performing, but alas only work for type-safe languages.
sindbad said:
You're confusing dynamic typing with duck typing. A dynamic language has the freedom to either duck type or check for specific types or shapes of objects. It is often a good idea to check what kind of objects you are given through the entry points to your library.
Well, in most statically typed languages I'm given that choice too, so I don't see how this point is relevant, as it kinda defeats the purpose of having a dynamically typed language if you don't use it and vice versa. Granted, using type checking in Python (where a type check is as expensive as a string comparison) is cheaper than using dynamic types in Scala (where a dynamic call is as expensive as a hash table lookup + string comparison), but both options are available, and there are multiple shades of grey like e.g. Go where dynamic types are as cheap as static types (dynamic invocation will always have some overhead but it's less than usual in Go)
sindbad said:
My point was that in Python you rarely need to check types, and when you do you have the entire expressiveness of the language at your disposal to check for anything you want. Indeed in most functional languages you get similar freedom in checking. Also have a look at Python 3's function argument annotations, which can be used for checking types as well.
Well, the thing is that in other languages, I get the same concise code as in Python, but I get type checking for free anyways (aka, if I don't check types myself the compiler does it for me anyways; less typing). The difference isn't whether you create type annotations, because that's just syntax; the difference is in the fundamental system of the language. A type-safe language can give you as much freedom as a dynamic language can.
E.g.:
Code:
//I'm NOT mentioning Scala her because in Scala, types can be left out everywhere except for when declaring new functions.
-- Haskell (type-safe):
concat x y = x ++ y

concat [1, 2, 3] [4, 5, 6]
--→ [1, 2, 3, 4, 5, 6]

concat "abc" "123"
--→ "abc123"

concat "123" 5
--    No instance for (Num [t])
--      arising from the literal `5' at <interactive>:1:22
--    Possible fix: add an instance declaration for (Num [t])
--    In the second argument of `Main.concat', namely `5'
--    In the expression: Main.concat [2, 3, 4] 5
--    In the definition of `it': it = Main.concat [2, 3, 4] 5


#Python:
def concat(x, y): return x + y
#This works for all compatible types too, like the above.
concat('123', 'abc')
#→ '123abc'
#BUT Python will behave in an unexpected way sometimes because of the missing type-safety, e.g.:
concat(5, 6)
#→ 11
#What? Why didn't I get 56? (Don't answer that, I'm not dumb :P )
sindbad said:
What stops a JIT from specialising functions at runtime? In fact, that's exactly what Python's psyco does (and similar to what PyPy's JIT does). Lazy is almost always better.
I did never say that a type-safe language would specialize that method offline. I just said that even online, a JIT can't do anything in that particular situation, since it has no way of doing branch prediction because it doesn't know the type of the arguments that might be passed to the top function.
If the function is specialized, and I suddenly pass in a value of type Foo, it won't work, or alternatively, if the original function is kept after specialization, it will be inefficient since the runtime then has to check whether the input is a float, int or Foo, and no speed is gained at all because of the extra branch.

sindbad said:
LLVM's JIT is quite frankly crap in comparison (even mono's is better). It's quite good as a static compiler, but not yet as useful as they make it out to be for making JITs.
I just said that LLVM occasionally generates better code, even in JIT mode, than HotSpot does.
sindbad said:
Well, PyPy's current JIT is much faster than Unladen Swallow at synthetic benchmarks. The one disadvantage PyPy has (besides the crappy regex engine) is that it can't use CPython C extensions, only ctypes.
It's still far from being as fast as other JIT implementations for statically-typed languages, and that's my point. And just the fact that actual *research* is required and new algorithms have to be *developed* before a dynamic JIT even has a chance of having the same speed as a static JIT says one thing or two about dynamic languages and their compilability.

Oh and btw, dynamic JITs can become as fast as they like; statically typed JITs will always be faster, since they'll be able to use all of the optimizations as the dynamic JITs use, and also have the advantage that they neither have to optimize down types (→extra overhead) nor do they have to consider that a method could be called with anything, and they're therefore able to do branch predictions and specialization much more efficiently than dynamic JITs.
 
Last edited by a moderator:
Exceptions are for exceptional case. If the user inputs a wrong number, that's exceptional. If there were no exception and you assumed that the return from int() is an actual int, your code could blow up later. Throwing an exception is the correct behaviour.

What do you find more readable? this
Code:
try:
     a = int(num)
except ValueError as err:
     handle(num, err)

or this?
Code:
a, err = int(num)
if err is not None:
    handle(num, err)

Also, Python is not Java. There are no checked exceptions and exception hierarchies make sense. I don't remember ever having to rethrow an exception in Python, although I may have done so at some point.


About JIT specialisation, you don't seem to understand how it works. The specialised version has a guard. Every time the function is called with new types, a new specialised version is compiled.


It may be that dynamic JITs have less potential for optimisation, but that doesn't mean they can't be fast enough. Even so, many techniques still can't be applied to static languages, but work quite well with runtime optimisation on dynamic languages (like partial evaluation).
 
sindbad said:
If the user inputs a wrong number, that's exceptional.
Not in my book :p
sindbad said:
If there were no exception and you assumed that the return from int() is an actual int, your code could blow up later.
...which is why exceptions are bad in such situations (because the function *would* return int), and you *shouldn't* return an int but instead something that is easier to handle. Exceptions from such a function *would* make the code blow up if not handled.

sindbad said:
What do you find more readable? this
Code:
try:
     a = int(num)
except ValueError as err:
     handle(num, err)

or this?
Code:
a, err = int(num)
if err is not None:
    handle(num, err)
Quite frankly, alternative number two.
Alternative number 1 forces me to make "a" into a mutable variable, which is bad for so many reasons that I'll leave them out for now; feel free to ask (Does Python even have enforced immutability? I can't recall).
It also forces you to actually find out which exceptions int() can throw, or whether it throws an exception at all, which is easy to remember for core functions but maybe not for library functions.

But this↓ will always be my preferred code, and the guys on the Go mailing list are probably going to implement that structure once they get generics into the language:
Code:
//Scala:
val myInt = tryParse("123") match {
  case Some(validInteger) => validInteger //myInt = validInteger
  case None => doSomething("error while parsing");
}
process(myInt)

//Go in the future (yes, it has horrible syntax, we all agree on that):
switch nr := parseInt("123") {
    case nr.isDefined:
        process(nr.get);
    default:
        doSomething("error while parsing");
}

sindbad said:
Also, Python is not Java. There are no checked exceptions and exception hierarchies make sense. I don't remember ever having to rethrow an exception in Python, although I may have done so at some point.
Java has a failed exception architecture, yes. That doesn't make exceptions any cheaper or more predictable in other languages, however.

sindbad said:
About JIT specialisation, you don't seem to understand how it works. The specialised version has a guard. Every time the function is called with new types, a new specialised version is compiled.
Of course, and that guard is expensive (considering that many functions we write nowadays sometimes are one-liners that e.g. just convert a type to a string).

Note that I said:
"if the original function is kept after specialization, it will be inefficient since the runtime then has to check whether the input is a float, int or Foo, and no speed is gained at all because of the extra branch."

If you consider: in Python, there is an uniform class hierarchy, but you can't e.g. extend integers, strings, floats etc because they aren't real classes; they are just value types with pimped wrapper methods. Ergo, not every value is truly a class at runtime. This means that if you're going to make a specialization guard, you have three alternatives:
1. You let even value types carry around type information with them, so an int won't be stored as a 4-byte value but instead as a 8-byte value with a type header. Do the same for classes and you'll be able to make a guard like "if(x.typeId == Int.typeId) ... else if (x.typeId == String.typeId) ..." (this is of course all simplified). This does, however, lead to roughly double the optimal memory usage for your application (because basically every data structure in your code is made up of primitive types, right?), AND you need a lookup entry for every primitive type which amounts to about 32 instructions in machine code (approx) considering that there's float, int, double, byte, ...
2. You make the integer into an actual class that you can extend etc. This is of course not a real solution, since performance would drop immensely (all ints would be heap allocated) and code would be unpredictable (imagine what happens if you override the addition operator of int!).
3. You use some kind of memory management strategy to identify primitive types, like for example storing ints on its own call stack or somesuch. Less memory usage, but the same performance problems as in 1, and calling methods in general becomes more expensive since multiple stacks have to be increased/decreased.

In statically typed languages, you don't need the guard at all, neither before nor after specialization. That's my point.

sindbad said:
It may be that dynamic JITs have less potential for optimisation, but that doesn't mean they can't be fast enough. Even so, many techniques still can't be applied to static languages, but work quite well with runtime optimisation on dynamic languages (like partial evaluation).
Eh, why wouldn't partial evaluation work? Are you confusing dynamically typed languages with scripting languages now, or what do you mean?
 
Last edited by a moderator:
dflemstr said:
there is an uniform class hierarchy, but you can't e.g. extend integers, strings, floats etc because they aren't real classes; they are just value types with pimped wrapper methods. Ergo, not every value is truly a class at runtime.
This is simply wrong. Everything in python is an object and you can extend any type whatsoever. When JITing you can do stuff like virtuals, where you don't actually create a full object until it's needed, but that's just an implementation detail of some JITs.

Re specialisation: My point was that even if theoretical performance potential is smaller, dynamic languages with good JITs can be very, very fast.

Re partial evaluation: Most statically typed compiled languages (including java, i'm not sure about scala) are built in such a way that partial evaluation is very hard or impossible to implement. The opposite is true for dynamic languages (especially if strongly typed).
 
Last edited by a moderator:
sindbad said:
This is simply wrong. Everything in python is an object and you can extend any type whatsoever. When JITing you can do stuff like virtuals, where you don't actually create a full object until it's needed, but that's just an implementation detail of some JITs.
My point was that you can't extend integers etc, because integers are 4-byte values. How ever the JIT represents extended ints at runtime, it cannot be as a 4-byte value. Reread that paragraph with this in mind. Oh, and with virtuals, well, that's yet another fork required on each method call, unless you make the call stack stateful by introducing some kind of stack sentinel that marks the call stack as being primitive.

E.g. there's a class X with a member y(int) and a method a(p1) and a method b(p1), a calls b. When JITed, b is specialized for int and a is specialized for X, and before the call to b inside of a, a places a sentinel on the stack to be able to place an int on it for b to read. If b doesn't detect the sentinel, "it" doesn't "use" the specialized version of itself.

This is the only method I can think of that operates in O(n*s*p), s = stack depth of the sentinel, p = average parameter list size per function, instead of O(n*p*t), p = parameter list size, t = number of types that could potentially reach the function.
sindbad said:
Re specialisation: My point was that even if theoretical performance potential is smaller, dynamic languages with good JITs can be very, very fast.
The Abacus: Very Very Fast™ compared to counting on your fingers. Doesn't tell me anything about how it relates to a Texas Instruments Voyage 200.
sindbad said:
Re partial evaluation: Most statically typed compiled languages (including java, i'm not sure about scala) are built in such a way that partial evaluation is very hard or impossible to implement. The opposite is true for dynamic languages (especially if strongly typed).
This might be true, but do I have to take your word for it? I see no reason for it being so.
 
Last edited by a moderator:
From my point of view Go is a very interesting language for this and similar platforms. It's very fast and yet rather convenient to use. The file size overhead is also rather small. Hello World for example is about 600-700kb in size. That's the price you have to pay for the runtime. But that's ok, really.

Well, it isn't really mature enough yet. ARM support is pretty poor (doesn't seem to work on v5 CPUs like the Wiz's one for example) and gccgo still lacks garbage collection. There also aren't many libraries yet, but you can use C libs if you like (e.g. for audio or image decoding).

If things go well it will be a pretty decent alternative in about a year or so.
 
Back
Top