9 ms·
This module doesn't seem to make it possible to type-annotate map and filter, which are Python builtins. I believe that dynamic typing results from frustration
by xiaq 12y ago
This module doesn't seem to make it possible to type-annotate map and filter, which are Python builtins.
I believe that dynamic typing results from frustration over generics, rather than over static typing in general. (Type stuttering is another problem, but modern languages solve it with local type inference.) I mean, annotating non-generic functions is always straightforward doesn't introduce much complexity:
# s should be str
# returns nothing
def print(s):
...
# just becomes (using Py3 annotation syntax)
def print(s: str) -> None:
...
However, it's much harder to write a type annotation of generic functions. Worse, remember that Python's map is variadic.
# for any types t0, t1, t2, ... tn:
# fn is a function that takes arguments of types t0, t1, ... t(n-1)
# and returns a value of type tn;
# seqs are iterables of types t0, t1, ... t(n-1);
# returns a list of type tn.
def map(fn, *seqs):
...
# How to write this signature is non-obvious
Many mature static typing systems would allow you to express such types (usually called parameterized types). But any one of them would require more than those trivial notations in print.
That's when the dynamic typing people get annoyed and go "fxxk static typing systems, I can handle this in my mind". Which is about 65% (totally random estimation) the point of dynamic typing in my opinion.
Any type checker for dynamic typed languages that doesn't seriously try to solve the generics problem is not genuinely interesting.
- pekk 12y agoThere is nothing special about map and filter, they are not idiomatic Python because there are better ways to do the same things. Python 3 annotations are already not used in any specific predetermined way, so if you create a system to do something with type annotations then you can implement List(str) or whatever it is you want an annotation to work like.
- xiaq 12y ago> There is nothing special about map and filter, they are not idiomatic Python because there are better ways to do the same things. True, list comprehension is often used instead. But a complete typing system should be able to type-check that too. The point is not changed. Also, I only used map and filter as an example. Python programmers tend to write a lot of generics functions without actually knowing it. def f(g, *args): ... r = g(*args) ... return r This function is actually quite generic, akin to map. And we are not yet getting into the realm of "trait" or "concept" or "typeclass" or whatever. def sum(*nums): s = 0 for n in nums: s += n return s Remember, although s is definitely an int, s + n may yield some completely unrelated type if n implements __radd__. The function here involves potentially infinite number of types, and their __add__ or __radd__ traits (reusing the name of the Python magic method). To fully express its type without unnecessary constraints is an interesting exercise. Since Python allows dynamic parameter list building (like sum(*nums)), dependent typing may be necessary for that purpose.
- crux 12y agoAre there languages that include the strong type checking, inference, and typeclass functionality of Haskell while also being a little more forgiving in terms of purity, so that writing ordinary programs is not quite so troublesome as in Haskell?
- habitue 12y agoRust has a decent amount of type inference, traits and strong guarantees. Ocaml/SML are both functional languages similar to haskell, and allow IO pretty much anywhere. They don't have typeclasses, (although ML people would say there's nothing you can't do with the ML module system[0]). [0]http://lambda-the-ultimate.org/node/1558 http://lambda-the-ultimate.org/node/1558
- frowaway001 12y agoScala (although it has limited type inference to require people to specify at least the inputs to methods for readability).
- andybak 12y ago"type stuttering" eludes my ability to Google. Could you provide a link or a quick explanation?
- sheetjs 12y agouint8_t foo = (uint8_t)69; In theory, there should be no need to specify the type twice. The ideal circumstance is something like uint8_t foo = 69;
- djeikyb 12y agoUntil java 7 you had to write: List<String> list = new ArrayList<String>(); It feels silly to have to write the type parameter twice. But now you can say: List<String> list = new ArrayList<>(); It's usually not a huge deal, but occasionally the stutter can be more pronounced: Map<String, List<Map<Integer, Set<Float>>>> map = ...
- skybrian 12y agoYou have a strange definition of interesting. Even dynamically typed languages are used in a static way most of the time; this is why JavaScript engines can perform well on most code. A type system without generics can still find plenty of bugs.
- tel 12y agoI'd agree with his definition and your point simultaneously. Type system with only 0-th order types are still useful for optimization and error capture, but higher order structures and higher order polymorphism are both incredibly useful and more powerful than 0-th order types.
- gsg 12y agoIt's not hard to imagine an extension to ML-style types that could cover variadicity: map : ('a... -> 'b) -> ('a list)... -> 'b list Where <type>... denotes <type1> -> <type2> -> ... -> <typeN> spliced into the type signature, with each type variable 'a in <type> replaced with 'a1, 'a2, ... 'aN. You could cover tuples too: zip : ('a list)... -> 'a,,, list Bit ugly, and writing a type checker for it could be fun, but it seems workable.
- tel 12y agoTake a look at Typed Racket for a variation on this.
- gsg 12y agohttp://docs.racket-lang.org/ts-guide/types.html?q=typed#%28part._varargs%29 http://docs.racket-lang.org/ts-guide/types.html?q=typed#%28p... Right, looks very similar.
- xiaq 12y agoTyped Racket is pretty decent. However, behold the byzantine Scheme numerical tower and the effort to type it: http://www.ccs.neu.edu/home/stamourv/papers/numeric-tower.pdf http://www.ccs.neu.edu/home/stamourv/papers/numeric-tower.pd...
- xiaq 12y agoFully typing variadicity in Python is only possible with dependent typing, since Python allows you to build the argument list dynamically. Generally speaking you can't even determine the number of arguments statically: args = [] while some_condition(): args.append('x') f(*args) Typed Racket allows you type some common cases, though.
- cabalamat 12y ago> This module doesn't seem to make it possible to type-annotate map and filter, which are Python builtins. In Haskell map is map :: (a->b) -> [a] -> [b] How could we do something like that in a Python type extension? One way would be to just use the Haskell notation in quotes: @type("(a->b) -> [a] -> [b]") def myMap(fn, as): #...etc... An alternative would be to define a function Fun for describing functional types, and gen.{something} for a generic type, e.g.: @type(Fun(gen.a, gen.b), [gen.a], ret=[gen.b]) def myMap ...etc... But that notation doesn't (at least to me) look as clean as the Haskell.
- deleted 12y ago[deleted]
- eevee 12y agoEven that trivial `print` annotation breaks duck-typing; the print function can actually accept anything that responds to `__str__`. Similarly, is there any reason the example `add` function in the OP shouldn't be able to add strings, or lists? I've seen a lot of these quick-runtime-type-checking libraries, but they always have the same problem as manual type-checking: they make the constraints much stricter than they need to be and prevent entire classes of useful behavior.
- acjohnson55 12y agoIn some languages, like Scala, you can use structural type annotations to handle this situation.
- eevee 12y agoPython has the abc module, but it would be a bit wordier than "arg=list". Maybe if the builtins were treated as the corresponding abcs.
- acjohnson55 12y agoI think `abc` would be overkill for this. My understanding is that you might use that to add behavior to a bunch of classes that aren't related hiearchically. But for type safety, all you need to do is check for the single magic method.
- eevee 12y agoYou can use it for that, but you can also use it to check for rough interfaces. (The classes are rigged to fool isinstance() via dark magic.) It's fairly common that you want more than one magic method, but don't care too much what the type is as long as it's sorta like a `list` — thus, MutableMapping.