It's a great language regardless.
Swift is the only one on that list that really feels tied to any particular platform.
C# has been extremely portable for years now thanks to .NET Core. Even SQL Server runs on Linux these days, if you really just enjoy spending money.
Java has always had a strong focus on portability, of course.
Nim... I don't know much about. Doesn't it compile to C? I don't think it intentionally has any particular platform affinity, although I wouldn't be surprised if it worked best on Linux.
Java is tied to JVM
Swift is tied to MacOS.
Of course, with the exception of JVM, they are all nominally "cross-platform" in the OS sense of the word. But I haven't seen any substantial Linux/Mac .Net codebases yet, same with Swift on Windows
Edit: I just saw the person that you replied to said the same thing, so I'm not sure why you're pushing that it has a complicated cross-platform story.
To be fair I haven't written .net code in like 10 years, and the docs explaining how to navigate the transition from framework to core were extremely confusing. Has that soup of different technologies solidified any?
TL;DR rewrite it - there is no migration path!
No, which doesn't really matter since we're talking about .NET, not .NET Framework. Poor naming choice by Microsoft I guess.
It left a bad taste, but maybe that was because I wasn't using a distro like Ubuntu.
My experience with C# and .NET Core on ARM Linux is that it comes with a lot of crashes and segfaults. On x86-64 Linux it works well, though.
If your environment is anywhere similar, and you OK with .NET 2.1.18, you can try my package: https://github.com/Const-me/Vrmac/releases/tag/1.0 Sources: https://github.com/Const-me/Vrmac/tree/master/net-core
Naturally it requires understanding the whole stack, how to improve performance in general, picking the right data structures and algorithms as well.
Of course, there are scenarios where that might not be enough, and that is where a C or C++ native library might come into play.
No need to be siloed into a single language.
Nim, Crystal and Swift all compile to native code and is designed for that.
Swift is not a GC language in the normal sense since it uses automatic reference counting. That is fully deterministic and with low latency. There is no stop the world to collect garbage like with Java and C#.
Swift is akin to C++ code with smart pointers.
Also, you can compile both Java and Kotlin to native images with Graal.
VM vs native is one way, but you can also go by the level of abstraction they provide, which very much puts them in the same category.
Also, this debate comes up every time, but ARC is widely classified as a form of garbage collection, just not a tracing one. It is transparent and automatic to the user. (Well, apart from cycles)
FWIW Chris Lattner, the language creator, put Swift into a non-GC camp.
Widely acknowledged CS books see it differently,
LLVM, GIMPLE, TIMI, TenDRA,...
Java and C# also targeet native code,
.NET Native, CoreRT, IL2CPP, Mono AOT, SubstrateVM, GCJ, ExcelsiorJET, OpenJ9, PTC, Aicas,...
Languages and implementations are orthogonal.
I figured Crystal was for Ruby developers who want a good static type system.
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.
That said, my current dayjob is around a Rails codebase, so I might not be representative of the normal systems programming crowd.
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!
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.
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.
- 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...