I figured Crystal was for Ruby developers who want a good static type system.
I figured Crystal was for Ruby developers who want a good static type system.
Crystal is interesting to me because its a systems language, using Ruby syntax and idioms.
I'm a Rails developer by trade but I've been pining to go back into desktop GUI development. Right now the only viable cross-platform options seem to be Swing and Electron. I'm not sure what the desktop story is with Crystal but if it there is a workable solution based on GTK or something similar I'd be very happy!
Categorically, what I've seen of this falls closer to "lintable comments" than it does "actual static types". Static types are a foundational aspect of crystal, it's not like a sticker slapped on as an after thought.
this issue tracks Windows progress, it hasn't been really updated in a while. I've been following Crystal Windows port for over a year, nothing changed yet.
- Ruby itself
- Crystal
- Elixir
I'm massively keen on Zig, too, but it still feels like early days for the language itself. The C-interop is utterly astounding but I don't want to write Zig as syntax-sugar around C libraries.
However, as far as I understand it there is also an argument to be made that multiple dispatch is at least as close to Alan Kay’s original intent for OO (e.g. [1]) than is the currently-ubiquitous class-based version of OO that leads to things like `RequestProcessorFactoryFactory` s with `RequestProcessorFactoryFactory.getRequestProcessorFactory(Class)` methods.
[1] https://medium.com/javascript-scene/the-forgotten-history-of...
That said, my current dayjob is around a Rails codebase, so I might not be representative of the normal systems programming crowd.
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.
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.
> Julia is dynamically typed, feels like a scripting language, and has good support for interactive use.
Literally on the front page.
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.