The only exception to this is untrusted input, but for that an string is usually always fine.
Never written generic/template code? Making that much easier is one of the main benefits of dynamic typing.
I have no problem when a language allows the programmer to explicitly opt for a variable to be an undefined/dynamic type, whether as part of generics or as part of a defensive ingest of untrusted data. I admit this is previously unspecified nuance but in my mind this doesn't break my rule: "this variable is always going to be a dynamic type, never not a dynamic type." This means you know when to interact with it defensively.
The thing I'd like in a hybrid static-dynamic language is type assertion blocks which restore some benefits of static typing while dealing with dynamic typed variables. E.g.:
declare x as dynamic;
x = untrusted_source();
if (x IS_A string) {
// Inside here, x behaves as a statically typed string.
// The compiler knows it and checks types appropriately.
// Passing x elsewhere will send a static typed string.
function_that_demands_a_string(x);
}
else if (x CAN_SAFELY_BECOME_A uint) {
// True for any non-negative integer regardless of data type.
// Inside here, x is always a statically typed uint.
// If it was some other type, it was converted for me.
}
(This probably already exists in some language somewhere but my familiarity with languages is narrow.)https://tio.run/##ZVDBbsIwDL33KywmrRcaUXbbhHbeaYdpJ4RQaA1kCk...
Note how the language is pretty smart about flow-typing variables as you narrow down their types. `x` here is typed as a tagged union (String | Int32 | Array(Char) | Float64 | UInt32), but by the time the program gets to the final `else` the compiler has figured out that the String and Array cases have already been dealt with (even though we never mentioned Array by name, just asked if the object implements a method that only Array implements), so it lets us call `x.abs`, which only works for numeric types. Of course, `abs` has different implementations for Int32 and Float64, but the signatures are compatible so the compiler lets it fly, resorting to dynamic dispatch at runtime.
The missing part is that there's nothing like a CAN_SAFELY_BECOME_A operator so I had to implement that test myself (as `try_to_u32`) and call it early to get the flow typing to work.
The Wikipedia article for the concept is https://en.wikipedia.org/wiki/Flow-sensitive_typing
With a
<T>createMapWithStringKey(T arg) -> Map<string, T>
You can keep strong type info with AType a
var b = createMapWithStringKey<AType>(a)
// b is for certain a Map<string, AType>In any serious statically-typed language, the type system does real heavy lifting. In practice that means that libraries execute code at compile time that, in effect, generate the code you would otherwise need to write (again) yourself.
C, Pascal, and Go are examples of statically typed languages whose type system does only trivial work.
The fastest JavaScript engines and LuaJIT are closer in speed to Crystal than Crystal is to C.
Granted - all these languages are really fast - and unless you're doing something INSANELY performance dependent and you REALLY know what you're doing - I think familiarity trumps all. You're almost certainly going to write faster Crystal code if you're a Crystal expert than you would write C, C++, or Rust.
There's a crazy amount of engineering effort that has to counteract a missing "this has one parameter and it's either a string or a float" annotation.
In practice if you don't have a world class well funded team - yes, static typing is necessary for performance.
It uses a method JIT so it is deterministic and easy to analyze unlike JavaScript.
You can lookup ahead of time how each function will get compiled with given input types.
You can't analyse functions like this ahead of time:
function foo(x)
x + y
end
You've got an Any type interacting with an Any global. There's no better answer than you'd get from JS analysis.It's very explicit in almost anything the language designers wrote from the beginning. Julia has always had it's language semantics designed around a JIT. Keep in mind, julia's JIT is not at all like JS JITs, julia's style is often called 'just ahead of time'.
For the you quote, the only thing causing a problem is the global y. Using global variables is very frowned upon in julia for this reason. However, if you write
function g(y)
function f(x)
x + y
end
end
or something, then there is no `Any` involved at runtime. What will happen is that say you call g(1.0)(2), as soon as julia sees this, it will do static type inference on the function bodies, knowing that y :: Float64 and x :: Int, all the code can be inferred and compiled down. Julia specializes on all arguments of functions, whether or not you put a type assertion on them.If there's a point in the program where type inference fails to produce concrete types, then the compiler just generates code up to that point and then waits until the types are resolved at runtime, and then using the new runtime type information does static analysis going forward from that point until it either encounters another type instability, or the program is finished.
This is why writing code that isn't concretely inferrable can carry a heavy performance penalty in julia, but it's also why inferrable code can be so fast. The trick is just to keep type dynamism away from performance bottlenecks and you'll be fine.
Sometimes discipline must be enforced on the programmer.
Next generation climate models are built with Julia. You would not pick a slow language for such a high performance dependent task.
You're confusing dynamic typing and type inference.
> Julia is dynamically typed, feels like a scripting language, and has good support for interactive use.
Literally on the front page.
As an implementation detail, it has a statically typed intermediate representation that's used for very aggressive and impressive optimization.
But that's an implementation detail, and is not what most people mean when they talk about a language being dynamically or statically typed.
Absolutely yes. If you don't know the shape of your data at compile time then you're going to destroy your performance discovering and validating it at runtime.