8 ms·
So whenever I have to study someone else's 'dynamic' python I encounter this sort of thing: def foo(bar, baz): bar(baz) ... What the heck is 'ba
by dandotway 5y ago
So whenever I have to study someone else's 'dynamic' python I encounter this sort of thing:
def foo(bar, baz):
bar(baz)
...
What the heck is 'bar' and 'baz'? I deduce no more than 'bar' can be called with a single 'baz'. I can't use my editor/IDE to "go to definition" of bar/baz to figure out what is going on because everything is dynamically determined at runtime, and even
grep -ri '\(foo\|bar\|baz\)' --include \*.py
Won't tell me much about foo/bar/baz, it will only start a hound dog on a long and windy scent trail.
- nicoburns 5y agoThe real power of dynamic languages is being able to do: const foo = JSON.parse(arbitraryJsonString); and not having to worry about the structure up front.
- naasking 5y ago> The real power of dynamic languages is being able to do: const foo = JSON.parse(arbitraryJsonString); and not having to worry about the structure up front. That's not power, that's a shotgun aimed at your crotch whose trigger is connected to a cosmic ray detector.
- dandotway 5y ago+NaN For comments that make me laugh out loud for duration T>2.0 seconds, I wish HN provided a way to transmute/sacrifice one's past karma points into additional +1 mod points.
- laumars 5y agoThat’s not a capability unique to dynamic languages
- valcron1000 5y agoWhat's 'foo' and what you can do with it?
- carnitine 5y agoAn arbitrary object? What else would arbitrary JSON parse into? Then you can access its properties, like with any JS object.
- preseinger 5y agoStructure is a virtue, not a vice. By doing this you're subverting your own interests.
- pdpi 5y agoStructure is a tool. Like any tool, it can be misused or overused. For anything even remotely production-y I'll always prefer explicitly parsing JSON into a known structure, but there's a lot of value in in being able to do some exploratory scripting without those constraints.
- preseinger 5y agoYes! Exploratory scripting is a categorically different thing than programming, though, I think.
- convolvatron 5y agonot necessarily. there is a bottom up school of thought that encourages people to noodle around and construct primitives by playing in the domain, and then interactively composing those primitives into larger and larger systems.
- preseinger 5y agoYeah, that's true. It's a judgment call for sure, but I've always found that angle on things to be self-subversive. The best programmers I know are all bottom-up learners, not top-down.
- mountainriver 5y agoYou can do that in static languages too by just parsing into a map
- tus666 5y agoWhat's the type definition of the map out of interest?
- pas 5y agoIn TS JSON is usually Record<string, unknown>.
- tus666 5y agoWell unknown is not a type (by definition), so you have just stepped outside of a type system, which is very common in TS if I understand.
- pas 5y agoUnknown is a top type in the TS type system. It serves the very important role of "here you need to apply some pattern matching and validation" and then you can make sure that you can continue working in a type safe environment. TS has a lot of facilities that help you with this (from the usual narrowing things, typeof and instanceof guards, control flow analysis, and at the end of the list there are the big guns like this thing called type predicates which basically allows you to wrap these checks and "casts" in nice reusable functions). There are also recursive types that help you model JSON, but knowing that it's an arbitrary deep nested map/list of maps/lists and number and bool and string mixed like a Bloody Mary cocktail doesn't really help :) With NestJS it's very easy to add decorators/annotations to fields of a class, and the framework handles validation (throws HTTP 422 with nice descriptions of what failed) and then in your controller you can again work in a type safe environment. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type https://www.typescriptlang.org/docs/handbook/release-notes/t... https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...
- HideousKojima 5y agoIn C# I can just parse it to a Dictionary<string, object>, and I can do that without destroying the usability of the rest of the language.
- tus666 5y agoWell in Python everything is an object, so that type definition holds true for Python as well :P
- jayd16 5y agoYou can parse it to the dynamic type too. Everyone is happy...right?
- tomp 5y agoWhat if the JSON represents a list, or an int? Also, how do you then access nested objects, like data['key'][0]['attr'] in Python?
- lmm 5y ago> What if the JSON represents a list, or an int? Then you write one short operator (and I agree that some static languages make this more cumbersome than it should be) to say so, and either handle the case where it isn't, or explicitly declare yourself partial and not handling it. > Also, how do you then access nested objects, like data['key'][0]['attr'] in Python? With lenses, something like: data ^? (key "key") >>> (nth 0) >>> (key "attr") If you do several unsafe operations in a row then this is cumbersome by design - you want to be clear which parts of your program are safe and which are unsafe, so that readers can understand and know where to review. But a good language should let you compose together several unsafe operations in a lightweight way and then execute them as a single unsafe operation, for cases like this where you want to work in the unsafe part of the language for a bit.
- tomp 5y agoSure there are solutions. But my main point is that HideousKojima's "statically-typed" solution would result in a runtime type error if it was given unexpected input, just like a dynamically typed solution.
- commandlinefan 5y agoI thought the real power of dynamic languages was being able to do things like: eval('alert("hello, ' + userInput.name + '!")')
- sandofsky 5y agoWhen that JSON payload changes (intentionally or not), you will run into a mysterious problem in some unrelated area of your code. It will be significantly more expensive to fix than failing fast at the point of parsing.
- suzzer99 5y agoBut depending on the situation, that problem may never happen. I'm not a big fan of introducing complexity to guard against code screwups in the same codebase.
- cdaringe 5y agoThe hardest bugs are the rare bugs. Common and frequent problems are easy bugs to fix. These subtle or rare changes are those which should be feared. If you model only that which you support, finding the source of the problem becomes much easier.
- deleted 5y ago[deleted]
- ironman1478 5y agoBut the thing is, don't you have to worry about structure? You have to unpack the elements from the JSON, so you will need to encode its structure explicitly, which includes type information. The only reason this would be useful is if you're just shuttling data to another API that expects a dict structure (that will then validate everything) and not JSON and you aren't really doing any real work yourself.
- cheriot 5y agoThere's something to this. I love featurefull type systems, but I've seen engineers try to parse JSON the "right" way in Scala, get frustrated, and blame the entire concept of statically typed languages. Elm manages to make this user friendly so perhaps it's "only" a matter of compiler messages and API design?
- woodruffw 5y agoEvery static language that I know of also supports this -- you can parse into a sum type of `JsonAny` (or whatever), where `JsonAny` is one of `Null | Number | String | List[Any] | Dict[String, JsonAny]`. The API then becomes a runtime fallible one, which is perfectly sound.
- yakshaving_jgt 5y agoYou can trivially do exactly the same thing in Haskell, so I think you’re suggesting that dynamic languages have no “real power”.
- adwn 5y agoRust: let foo: serde_json::Value = serde_json::from_str(arbitraryJsonString)?; There, just as powerful [1]. But you know what's even more powerful? After you've done your dynamic checks, you can do this on the entire JSON tree, or on a subtree: let bar: MyStaticType = serde_json::from_value(foo)?; and you get a fully parsed instance of a static type, with all the guarantees and performance benefits that entails. [1] Value represents a JSON tree: https://docs.serde.rs/serde_json/enum.Value.html https://docs.serde.rs/serde_json/enum.Value.html
- arc776 5y agoIn Nim you can even convert JSON to static types! https://nim-lang.org/docs/json.html#to%2CJsonNode%2Ctypedesc%5BT%5D https://nim-lang.org/docs/json.html#to%2CJsonNode%2Ctypedesc... Now you get type checking on JSON at compile time :)
- earth_walker 5y agoHow powerful is this, really? As soon as you try to do anything useful to foo it's not arbitrary anymore. You have to make some kind of an assumption on the underlying type, check for keys, nulls, maybe it's a number (the right number?), maybe it's a list. So now you have to scatter some boilerplate checks everywhere you touch a part of foo. If you could parse it into a typed structure up front, you'd only have to deal with this in one spot, and have guarantees for everything else that follows. Bonus: if your typed language has good support for records, you can even do this in a way that only provides structure to the parts you care about, and is robust to changes to any other parts of the json.
- solox3 5y ago> I can't use my editor/IDE to "go to definition" of bar/baz I use "Find Usages" on foo to see where it is used. Once you see where it is used, you know what the types can be. It's not great, but it's also something that can be progressively remedied. In the event that the function is not as trivial as your example suggests, the author should have written a docstring to help you understand what it is trying to do, in addition to type annotations that will make it more readable. In this example, bar can either be a function, a class, or any object with __call__(), so the type information is less important in this case, than actual docstrings that express intent.
- lkrubner 5y agoIn Clojure, I tend to put pre and post assertions on most of my functions, which is useful for checking errors in the schema of runtime data (very useful when dealing with 3rd party APIs) but it also offers the documentation that you are seeking: (defn advisories [config] {:pre [ (map? config) (:download-advisories-dir config) ] :post [ (map? %) ] } (let [ dir (:download-advisories-dir config) ] ;; more code here
- anyfoo 5y agoAnd now imagine the compiler would actually enforce that practice, and you have static typing, with less boilerplate.
- maleldil 5y agoHow is this any better than static types?
- Jtsummers 5y agoPre/post conditions are complementary to a type system. They can ensure logical properties that may not be encodable in your underlying type system (that is, essentially every mainstream statically typed language). Such as the relationship between two values in a collection. Trivial example, if you have a range such as [x,y] where x < y must hold, how would you convey that in any mainstream type system?
- yakshaving_jgt 5y agoThe Haskell-y way to do this is to use a smart constructor[0]. [0]: https://wiki.haskell.org/Smart_constructors https://wiki.haskell.org/Smart_constructors
- Jtsummers 5y agoThe first part of that page demonstrates what amounts to pre/post conditions, but placed in the constructor. The range is checked dynamically, not statically. The second part is using Peano numbers to enforce the constraint. I guess you could try and force that into some mainstream languages, probably C++. With its template programming you could get something going in this vein, though I'm not sure how well it would work if the number were calculated at runtime rather than compile time. You'd still end up with a dynamic check somewhere.
- mountainriver 5y agoYup, I find this completely insane behavior to think that you somehow benefit from types not being there. You just make it way harder for people to understand your code and contribute to it.
- commandlinefan 5y agoIt doesn't just make the code harder to read, it makes it run slower, too. Static typing provides some compile-time guarantees about what's going to go where, so the compiler can make a lot of simplifying assumptions that speed things up.
- deleted 5y ago[deleted]
- tus666 5y agoOh yeah the easiest code in the world to read is some contorted type system and function signatures that look like hieroglyphics that you need a PHd in CS to comprehend. Python is easy to grok, and if you have programmers writing code like bar(foo,baz) then the problem is not Python. You can write crap in any language. Unit tests do much of what typing checks anyway ... and here's the thing ... you NEED unit tests no matter what. No typing system can tell you that you wrote > when you should have written <.
- pchangr 5y agoYeah, I always say that python is an amazing language to prototype and terrible language to scale precisely because it lets people write the usual terrible code and then gives you the freedom to make it even worse.
- laumars 5y ago> Python is easy to grok It’s what you’re used to. I personally find Python horrible to read because I used to a whole different class of programming languages. But I’m sure some of my code might be hard to read by others who aren’t used to that particular programming language too. > Unit tests do much of what typing checks anyway ... and here's the thing ... you NEED unit tests no matter what. Some, not all. Strictly typed languages are handy when it comes to refactoring and unit tests can sometimes fail there if the design is being changed enough that the unit tests need rewriting too. > No typing system can tell you that you wrote > when you should have written <. Not technically true. Some languages with a richer set of types and operator overloading could have code written to detect that sort of thing. But I do get your point that unit tests are import too. I’ve been programming for > 30 years and in dozens of different languages. In that time I’ve felt strictly typed languages make larger and more mature code based slightly easier to maintain. While loosely typed languages are easier for smaller and/or younger code based. But largely it boils more down to personal preference than anything. I will caveat that by saying the fact that Python supports type annotations should be telling that even dynamic languages benefit from a stricter approach to typing.
- foxfluff 5y agoStatic languages unfortunately don't save you from that. You find automatically inferred types, or types that refer to some abstract interface or template-class-mess but you have no idea where the actual implementation lives until you compile with RTTI and run it under a debugger... and as tfa posits, people working with the limitations of static languages often end up reinventing a dynamic structure. Is this somehow supposed to relevant to the posted article or did you just want to start a tangentially related dynamic-vs-static flame war here in the comments section?
- dmitriid 5y ago> to some abstract interface or template-class-mess And traits! "Oh look, this functionality is implemented in a trait implemented by a trait implemented by a trait implemented by what you're looking at. Maybe"
- hota_mazi 5y ago> Static languages unfortunately don't save you from that. You find automatically inferred types, Oh yes, they do. Even inferred, the types are there and pretty easy to locate, even if you're not using an IDE.
- foxfluff 5y agoThe types are there.. but you don't know which one it is that your program is dealing with. You could have dozens of implementations for any given abstract interface. One gets picked up at run time.
- Diggsey 5y agoYou don't need to know which one, because the abstract interface tells you how to use it...
- foxfluff 5y agoThat's the theory. Works great when there are no bugs and everything's been designed just right. In that world you could wipe implementations from memory because you won't ever need to dive in.. Very often I'm looking at code and "how to use the interface" is not a question I'm looking to find answers for.
- vanusa 5y agoWhat the heck is 'bar' and 'baz'? So there's no docstring? And the actual variables are that random and indecipherable? Sounds like the problem is that you're tasked with looking at code written by someone who is either inexperienced or fundamentally careless. When dealing with reasonably maintained codebases, this kind of situation would seem pretty rare. In modern python we now have type hints of course, which have helped quite a lot.
- maxwell86 5y ago> In modern python we now have type hints of course, which have helped quite a lot. I had to laugh, hard. If your Python program uses any library whatsoever, chances are that library won't have types, so you can't really use them. Even super widely used libraries like numpy don't have good support for types, much less any library that consumes numpy for obvious reasons.
- vanusa 5y agoI had to laugh, hard. It's fine if you want to insulate yourself. But I don't see that you're making much of a point here.
- arc619 5y agoThey're probably laughing because a) you're suggesting manually doing the work static typing does in a dynamic language because its untenable not to for large projects, and b) you can't easily add type hints to other people's libraries.
- vanusa 5y agoNo - (a) is not what I'm suggesting. And (b) while disappointing, just doesn't slow one's work down very frequently in daily practice. Look, I just don't buy the suggestion that static typing magically solves a huge set of problems (or that it does so without imposing negative tradeoffs of its own -- the very topic of the original article). Or that dynamic languages are plainly crippled, and that one has to be a kind of a simpleton not to see this obvious fact.
- deleted 5y ago[deleted]
- aidos 5y agoWell, you could put the types on these days. But also, there’s nothing stopping the code from being much clearer about its intention than this weirdly contrived example (I have a lot of code and most the function names are pretty unique). And surely you want to search for ‘foo(‘ to find invocations.
- lkuty 5y agoIndeed. An interesting reading is https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/ https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-typ...
- yakshaving_jgt 5y agoThis is one of my favourite blog posts on the Internet and I implore every programmer to read it. > The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.
- socialdemocrat 5y agoThat is why I like dynamic languages like Julia better because they use type annotations more frequently.
- rightbyte 5y agoMy worst developer experience ever was trying to make changes to a custom build system with functions exactly like that. Injector or decorator pattern, or what ever it is called. I just gave up and introduced globals and used as flags for some places in the code. To make things even more fun, big parts of the system was written in 2.7 called from runtime generated bat files from 3.4 as remnants of a rewrite and the consultant that had his funding cut.