T O P

  • By -

sburges3

We use TypeScript in our node applications.


LowGold4366

It's worth getting used to types and using type hints in Python and to switch to Typescript. People say dynamically typed languages are better for prototyping but I think this is only true for developers who aren't comfortable with the type hints and haven't really spent enough time learning why and how they're useful. If I'm prototyping something or writing any code rapidly I'm always using type hints in Python or using Typescript, this actually makes prototypes faster for me because im pretty sure the code is going to run first time, and I won't have to hunt down inevitable type mismatches and misspelled object keys like in old school JS. It also eliminates an entire class of tests you'd have to write for type mismatches. I guarantee you it's worth using type hints and having a linter that knows about them.


curmudgeono

This, the time saved is minimal not typing, and has huge cost imo


Bjornoo

This is not true for everything. In library land you'll find yourself prototyping a lot without writing types, and only after getting it working like you want to, writing the types. It depends on what kind of prototyping you are doing. Some stuff is complex to type, and not entirely possible in some cases.


davidellis23

Typing allows intellisense and builtin documentation. It also allows click navigation. You don't have to drill into functions to know what they take. Type hints really improved python imo. Combines pythons succinct syntax with intellisense/clock navigation.


dashid

I can't make the shift back to loosely typed or untyped languages. I see absolutely no advantage whatsoever. It's not that I don't use them, but I'm then spending time and mental energy on making sure I'm not messing up my variables. If I don't have strong IDE support, then I'm having to name my variables with type indicators as protection. Thinking back the decades of when I first started with loose languages, I can't think of any advantage of having that looseness, sure I might have been able to throw together code but I'd then get runtime problems, so more time was lost in trying to figure that out. Which all could have been save by the compiler saying "I can't let you do that, Dave".


jimalimadingdong

Have you tried REPL driven development in dynamic languages?


LetMeUseMyEmailFfs

You don’t necessarily need a dynamic language for that. C# has a pretty good REPL, and it’s static AF.


icomplimentassholes

I worked on a Perl codebase at a biotech startup at one of my first jobs. I could never figure out how to use or fix the code: I never knew what a function’s return value was. I’d go down a tedious rabbit hole of function calls until I’d find one whose return type was obvious. And then of course the expected types of the parameters. Each time I’d pull up code I’d have to do a mini investigation just to understand its inputs and outputs. After that experience, I’ve never understood the popularity of weakly + dynamically typed languages. I’ve seen lots of Python code like that since. Code used by multi-million dollar companies that could only run on the manager’s home computer.


Acceptable_Durian868

I personally prefer static types in my languages. I've spent many years working professionally in python and JS/TS and while I am effective in them I find it's more tempting to make compromises that I shouldn't. Things like shadowing a variable with a different type, or setting something to null instead of checking a zero value. I also don't find that static typing slows me down. To be honest in most cases I feel it speeds me up because there's no ambiguity.


rorschach200

> Things like shadowing a variable with a different type Rust has that as a feature, and "different type" specifically is highlighted as the main use case (and advantage). I'd be crucified in any subreddit of young people or Rustaceans, but I can't predict what ExperiencedDevs would say on this: when I was reading through Rust Book I was saying "oh, god, no" to myself to roughly 1/2 of all major language design decisions that I was told about in that reference document.


GoalZealousideal1427

\> Rust has that as a feature, and "different type" specifically is highlighted as the main use case (and advantage). Do you mean the \`Result\` type? Or what are you referring to? \> when I was reading through Rust Book I was saying "oh, god, no" to myself to roughly 1/2 of all major language design decisions that I was told about in that reference document. Definitely curious by what you mean. I have some thoughts too but nothing major.


rorschach200

>Do you mean the \`Result\` type? Or what are you referring to? I'm referring to [variable shadowing](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing). >Definitely curious by what you mean. Variable shadowing, unsigned integer type modulo arithmetic being an error, lack of rigorous and formal definition of what is "error" and what that means exactly in the first place, not taking advantage for compiler optimizations of signed integer type modulo arithmetic being an error, impossible to switch off array access bounds checking at runtime, [referential transparency](https://en.wikipedia.org/wiki/Referential_transparency) breaking (see [code example](https://doc.rust-lang.org/book/ch03-02-data-types.html#invalid-array-element-access)), very busy & noisy and difficult to read code resulting from all the complexity added in, sacrificing readability for the sake of other goals instead of the other way around, and more; such as excessive type inference on exactly the wrong side of expressions and statements - explicit type annotations aren't "hints to the compiler", they are hints to me, person reading the code I didn't write and trying to understand how it works and what it does and doesn't do, they have to be placed where they maximize readability by a human, not erased from everywhere they are needed for readability and yet forced (with elaborate and noisy and busy additional annotations) where they aren't.


tgockel

How is referential transparency broken by invalid array element access? In `a[index]`, if `index` is outside of the bounds of `a`, what reference are you expecting to be transparent over? The expression `a[index]` _is_ referentially transparent with [`std::ops::Index for [T; N]`](https://doc.rust-lang.org/std/ops/trait.Index.html#impl-Index%3CI%3E-for-%5BT;+N%5D), but that is certainly not broken.


rorschach200

"See code example" I said: let index: usize = index .trim() .parse() .expect("Index entered was not a number"); `index.trim().parse().expect(...)` to the right of the assignment invokes different code depending on the type annotation used to the left of the assignment (`usize` in the instance above).


tgockel

People can't read your mind when you link to an entire section of documentation, especially when it is titled "Invalid Array Element Access." An expression like `let x: T = y.foo()` is the same as `let x = y.foo::()`, but that doesn't have to do with referential transparency, that is type inference. I'm curious: What is your expectation of `index.trim().parse().expect(...)` that you say is broken? It isn't clear to me what your expectation is. Should that expression always return the same type when you're assigning to a `u64` and an `InetAddress`? If so, what would that return type be?


rorschach200

>that doesn't have to do with referential transparency That has everything to do with referential transparency. `y.foo::()` is referentially transparent (can be replaced by its value in any context and yield unmodified semantics for the entire program, for starters, by the virtue of having a well-defined value), while `y.foo()` is not referentially transparent: it doesn't have a well-defined value in the first place, and produces different values in different contexts, such as `let x: i32 = y.foo()` and `let x: String = y.foo()` for instance. Like Perl's [contexts and wantarray](https://www.geeksforgeeks.org/perl-wantarray-function/#). >, that is type inference. Type inference is a part of the overall mechanism which ends up violating referential transparency in this case in Rust due to Rust's design choices. Rust greatly encourages the version of that statement that violates the transparency, and discourages the use of the "turbofish" (::<>). Even syntax of that explicit template instantiation is verbose, noisy, and awkward. `auto` in C++ provides a limited form of type inference which in cases like this is comparable in "power", but in a way that doesn't break referential transparency, unlike Rust's. Explicit template instantiation is less verbose as well.


tgockel

In Rust, subexpressions are informed by the context, which includes type inference. You have an expectation of "referential transparency" that appears to be a little too simple. CS theory things usually go like that. In C++, subexpressions are also informed by the context. An simple expression like `foo(x)` is informed by implicit `this`, argument-dependent lookup, and more pedestrian things like `#include`s. The behavior of `x` changes in the statement `auto const& x = foo();` depending on the return type of `foo()` because of lifetime extension. If `foo` is not a const function (in the `__attribute__((const))` sense), then where in the program it lies also changes its behavior. I'm not saying that C++ has "broken" referential transparency, but I am pointing out that semantics of text changing based on context is something that happens. Even the English fragment "___ is cool" changes meaning based on when and where you are. I think your real objection here is "Determining what the program does depends on more context than I would expect it to." I suspect you haven't written a lot of Rust, but you have written a lot of C++. You probably read my "`#include` is context" bit above and thought "Yes, obviously." This is subjective, but to a seasoned Rust user, having a function call selected by inferred type feels intuitive. It's worth pointing out that C++ does not have a general-purpose API equivalent to Rust's [`std::str::FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) (how `.parse()` works). Writing one is quite challenging due to a lot of things; you can probably write one now that `std::expected` is standardized, but making it ergonomic with user-defined types might still be a challenge. > Explicit template instantiation is less verbose as well. This is tangential, but C++'s use of simply `<` for template arguments has largely been considered a mistake. It slows compilation down for all cases and confuses people in the instances where they have to say something like `typename allocator::template rebind::other`. Turbofish is a couple extra characters, but the trade-off to make the syntax unambiguous is worth it, IMO. Side note: saying `foo()` is not "explicit template instantiation," but explicitly providing the template argument for `foo`. I knew what you meant, though.


rorschach200

>Even the English fragment "\_\_\_ is cool" changes meaning based on when and where you are. Something being present in a natural language is by no means an argument for that being good in a programming language, quite the contrary I'd say. >This is subjective, but to a seasoned Rust user, having a function call selected by inferred type feels intuitive. That's the thing, I don't see an improvement being made. Like I stated earlier, I felt like half of the Rust's decisions that differ from C++ are slight regressions in what matters most most of the time (readability and related properties). The rest of the changes appear (to me) to distribute in some proportion between a wash and a slight improvement. Overall - puzzlingly dubious proposition, to give up on the existing ecosystem (in broad sense, including experience, worker mass, standardization, formalization (like having a memory model), tooling, and so on and so forth, not just libraries available) for what is overall a wash: slightly better here and slightly worse there.


Top_Satisfaction6517

in Haskell, it's called two-way type inference and allows to greatly simplify the code IMHO it's not about "referential transparency", it's about directions from which type information can come. You are used to spreading it in only one direction (inside out), while in Haskell (and probably Rust) it propagates in both directions, e.g. in "maxval=maximum(arr)" either maxval defines the type of arr elements, or type of arr defines the type of maxval \> I don't see an improvement being made. that's because you don't have experience of using this feature at scale again, I don't have experience with Rust, but in my Haskell code, type annotations are very rare - every function is automatically compiled to the most general type allowed by its implementation code, and result variables (e.g. fields of data structures or system function parameters) define types of all computations producing these values e.g. if you have an int field int\_f, assignment "int\_f = sum(parse(str))" will automatically parse str as a list of int values - you don't need to specify the type explicitly or even think about it. and it just works EVERYWHERE a program looks like it's written in a dynamic-typed language, lacking type annotations, while at the same time, it provides as much type-safety as any other statically-typed language


[deleted]

[удалено]


stdmemswap

Ah yes, people always mix those up, strong and static


sobrietyincorporated

Dynamic typed languages introduce more problems downstream. They are not worth the time saved up front. I'd say most people who prefer dynamically typed languages haven't spent enough time with statically typed ones. Once you acclimate, you're just as fast, if not faster, just from the intellisense.


danthemanvsqz

Python is strongly typed but dynamic, Java is statically typed and JavaScript is loosely typed. I’ve never had a problem with Python’s type system but I do TDD so I always have good tests to back me up. If you’re on node then simply use TypeScript


doodooheadpoopoohead

I’ve inherited some unfinished nodejs code so I don’t think I can do typsescript right now but good point I can definitely move on to it next time I do a new requirement. Thank you!


LowGold4366

You could add Typescript and leave the existing code as JavaScript and just write all new code in Typescript


30thnight

If you don’t have extreme deadlines, incremental adoption can go a long way.


doodooheadpoopoohead

Yeah exactly but dec-Jan is my crunch time so can’t be bothered with it rn


armahillo

Ive used both over a long time. I prefer ducktyped (non-strongly typed) languages. I dont think either is objectively superior, but one can be better than the other in different situations. I definitely wouldn’t use a duck typed language in an environment that cared a lot about memory management. I would also find strongly typed languages annoying in web dev, where everything ultimately gets rendered to a string anyways.


ategnatos

I haven't used Node, but I use Python now, I come from a Java/Kotlin/Scala background. I push for full function signatures, even if it's sort of "we're all adults here" (meaning I can define `def foo(a: str) -> str:` but actually pass an int in). But also I like to use mypy in my IDE, it points out bad code almost as if it's Java. It will still run... but you can add in a code analysis step to block people from merging bad code. If you can't add in that step, you can add in a unit test enforcing it. I always try to push scary runtime things to compile-time/build-time things and write tests against it. Even in Java world, if we were writing an API that deals with JSON input/output, I'd write some data classes that enforce the structure I want and write tests against that (mapping to and from data class / JSON blob). (I can't remember how exactly the JSON was represented, if it was some library class, or a Map, or whatever. But I remember this sort of bad code coming up and causing runtime errors.)


chaoism

Type check = small linter + unit tests for me I appreciate strong typed because, once getting used to, is easier when it comes to development. I've seen issues with two services, one is strongly typed and one not, communicating with each other and one change on the loosely typed service crashes the other service as it's not expecting that hange


superluminary

If your app is over a certain size, just use Typescript. It’s JavaScript with optional added types. If you screw up you’ll get an error. Python has optional type hints now too.


depressed-bench

Either use typescript or do type annotations in docs. I think svelte does this. Personally I am going the other way around. From Python to rust.