4 ms·
> But its also possible to implement a statically typed language within a dynamically typed one. Not as a simple set of library functions that you import. You
by zenhack 8y ago
> But its also possible to implement a statically typed language within a dynamically typed one.
Not as a simple set of library functions that you import. You need to actually write an offline tool, because if the checking is done by library code then it's already too late -- the program is already running. Contrast the implementation above which is just a function, and still composes with the rest of the language without any assistance, and doesn't do anything that's terribly out of the ordinary for a function to do. Static typing is a language property, strong typing is a library property.
> As soon as you write you're own object, you've created a new language with similar syntax but distinct semantics
This seems like a uselessly broad criterion for what constitutes a language. If you're just using the same everyday mechanisms you do writing any program, I don't think you can claim you're using a different language, without diluting the meaning of the term beyond all utility.
> I'm going to challenge you to give an example, because I don't think you'll be able to.
The trivial example is keeping units straight. Merely having an attribute __mul__ does not not adequately capture the interface of multiplication in the presence of units. Unless I specifically write the kind of boilerplate with a bunch of checks like my JavaScript example, it will just silently do the wrong thing.
In some sense Python's object does satisfy every interface, with a default implementation of throw AttributeError/TypeError. This may seem trivial, but it is important in that overriding one of these isn't different from overriding valeOf, in order to get a different implementation for your object (one that throws).
- joshuamorton 8y ago>Not as a simple set of library functions that you import. You need to actually write an offline tool, because if the checking is done by library code then it's already too late -- the program is already running. Contrast the implementation above which is just a function, and still composes with the rest of the language without any assistance, and doesn't do anything that's terribly out of the ordinary for a function to do. Static typing is a language property, strong typing is a library property. You're, probably unintentionally, setting your requirements a bit differently. As an example, Tensorflow is a statically checked DSL within python. You create a tensorflow graph, and (unless you're using the relatively recent dynamic graph features), that graph is statically checked for validity. Python is the virtual machine in which Tensorflow is written, but within the dynamic python VM, Tensorflow is a statically checked language. Basically, what happens is 1. python runs, parses the tensorflow graph and creates an AST 2. the AST is statically checked by tensorflow. This happens before tensorflow really does anything else. Errors like tensor shape mismatches are raised here, instead of the first time you actually attempt to multiply tensors of mismatched shape. It doesn't use runtime information about the types, it isn't dynamic checking. Its very much static checking, but static checking that happens after python runs, but before tensorflow runs. 3. You execute the now checked graph. Errors can still appear here, but you have avoided some via static analysis. Similarly, you can create a strong language in the VM of a weak language. That doesn't suddenly make the weak language strong. Neither static typing nor strong typing is solely a property of the language or ecosystem. It comes as a combination of both. I personally like to keep this distinction clean by calling DSLs that break core properties of the parent language different languages, because that's what they are. >If you're just using the same everyday mechanisms you do writing any program, I don't think you can claim you're using a different language, without diluting the meaning of the term beyond all utility. When you define a new object system with distinct mechanics, you aren't using the same mechanisms you do to write any program. They're no longer compatible with the rest of the language. >The trivial example is keeping units straight. Merely having an attribute __mul__ does not not adequately capture the interface of multiplication in the presence of units. Unless I specifically write the kind of boilerplate with a bunch of checks like my JavaScript example, it will just silently do the wrong thing. This has nothing to do with strong or static typing. Its yet a third axis on which a language can vary. But to be clear, if you define things correctly, managing units like that doesn't actually require much boilerplate. You create a Unit interface and 7 BaseUnits impls for each of the SI base units. Then within the bounds of Unit * Unit (or Unit * number) multiplication, the interface does indeed correctly capture multiplication in the presence of units. There's never any need to do the casework like you're describing, and the more you go down this tangent the more it seems like you have misunderstandings about core Object Oriented principles. Meter = Unit(['m'], 1) ... Kilometer = Meter * 1000 KiloNewton = Kilogram * Kilometer / Second ** 2 f = KiloNewton(12) f.unit # kg * m / s ** 2 f.magnitude # 1200 The only "cases" are handling Units, which have a magnitude and a unit, and non-unit numbers, which have only a magnitude. As long as the base Unit class knows how to do that, you have one bit of casework, which has two options: class Unit: def __mul__(self, other): if isinstance(other, Numeric): return super(self.units, self.magnitude * other) if other.units: return super(self.units + other.units, self.magnitude * other.magnitude) raise TypeError I'm simplifying self.units + other.units, but that's mostly irrelevant. The point here is that you're defining a new interface, Unit, which is able to do what it wants. But in a JS world, if I tried to multiply my Unit by 12, I'd lose the unit. >In some sense Python's object does satisfy every interface, with a default implementation of throw AttributeError/TypeError. This may seem trivial, but it is important in that overriding one of these isn't different from overriding valeOf, in order to get a different implementation for your object (one that throws). Well, no. If you're claiming that the action of saying "I explicitly do not satisfy this interface" is, in a sense, a way of satisfying an interface, we've reached the point where you're not interested in a dialogue and are instead only interested in arguing a wrongheaded perspective, and at this point a perspective that it seems you clearly recognize is wrongheaded, but wish to defend anyway. The difference between overriding a method and overriding valueOf is that with a method, I control which specific interfaces I implement. With valueOf, its all or nothing. I cannot pick and choose which interfaces to implement. I've said this or a variant of it in nearly every post so far, and you've yet to respond to this point. So I'll try it once more: In a strong language, each object controls which specific interfaces it implements. In a weak language, each object does not. Thus, in python, I choose individually if I want to be iterable or multipliable or a container. In JS, I do not get to decide those things. My object is a container, my object is a number (which means that it can be added and multiplied and such in ways I don't intend), and my object is a string all at once. While the ability to overload specific operators is a symptom of Python being stronger (each operator is bound to an interface, instead of all of the operators being bound to the number interface which everything implicitly implements), it is a symptom, and not the cause.
- zenhack 8y ago> at this point a perspective that it seems you clearly recognize is wrongheaded, but wish to defend anyway. I don't appreciate the accusation. At this point your own position is baffling to me as well, but I'm not going to make attacks; if it comes to that better to just walk away (I did this for a few days, because I was getting frustrated). > Well, no. If you're claiming that the action of saying "I explicitly do not satisfy this interface" is, in a sense, a way of satisfying an interface What I'm saying is that the built-in 'object' takes just this approach -- so if it does count as a way of satisfying an interface then (in both Python and JavaScript) every value satisfies every interface. If it doesn't, then user defined classes doing it isn't different from classes that ship with Python. I agree it's kindof weird to call that satisfying an interface; my claim is only that the explicit opting-out that `object` doesn't isn't any different than explicit opting-out in user code. > The difference between overriding a method and overriding valueOf is that with a method, I control which specific interfaces I implement. With valueOf, its all or nothing. I cannot pick and choose which interfaces to implement. I've said this or a variant of it in nearly every post so far, and you've yet to respond to this point. I similarly feel like I've been repeating myself with my response, which is not being heard: This only matters if you're willing to treat the functions with special syntax (, / etc) as privileged. Otherwise `valueOf` is just another interface (representing coercion), and while it is definitely more poorly designed than the Python approach of having separate interfaces for each of ``, `/` etc, this only has any implications for a property of the language if you're willing to treat those interfaces (both `valueOf`/coercion and ``/multiplication, '+'/addition etc) as special. Some arbitrary function I write (in either language) doesn't (necessarily) use these interfaces. There are a fixed set of functions, imported by default, with special syntax, that use these interfaces (and of course code that calls those functions). I think it absolutely is* defensible to claim the fact that these operations have specialized syntax makes them special in a way that's really "part of" the language, and if you take that as given, I think the rest of your argument is valid. But if you take those functions to be not really special, then behavior that only pertains to them doesn't inform questions about the language proper, just the libraries. You've said that you agree the operator syntax is unimportant, but I'm having a hard time groking how else this is relevant. I should also reiterate that this is a really nitty-gritty technical point I'm making; the ecosystem and libraries are the best thing about Python, so saying "it's just a library" should not be construed as "it's unimportant." Re: Tensorflow, the resulting Tensorflow program/AST is statically typed, but it isn't a Python program, by any means. You can't put a Python while or for loop inside a Tensorflow program, call a python function, or basically do anything that's not encoded in the AST. You can use those when constructing the AST, but that's different. (Do correct me if I've misunderstood the design of Tensorflow). To use your own words > [Tensorflow programs a]re no longer compatible with the rest of the language. But this just isn't true of my examples where I forgo using javascript's built-in operators in favor of my own `mul`, `add` etc. functions. In the latter case, it's still JavaScript -- it can still be used with other JavaScript code in the full generality of any other functions I write. I have some things to say about the Unit example as well, but I'm running late, so am going to stop here.