I think if you really want implicit conversions, then you want to do what Perl does (never thought I'd say that): different operators for different types.
This is kinda insane. People can give reasons X is better than Y on grounds of various theoretical virtues of X -- that has nothing to do with a preference for any language.
Here's an example virute:
Behaviour should be consistent unless specialised. When specialised it should be obvious which specialisation is chosen.
(Violation: basically all of js' operators).
It's really easy to forget a closure in an event driven function and to produce difficult to debug, error prone code.
A classic example I just screwed up:
export function getSFTPConnection(options) {
let key = readFileSync(options.keyfile_path, 'utf8');
let ssh = new ssh2.Client();
return new Promise(
(resolve, reject) => {
ssh.on('error', reject);
ssh.on('ready', () => {
ssh.sftp((err, sftp) => {
resolve(sftp);
});
});
ssh.on('close', (err) => {
reject(new Error('SSH connection closed: ' + err));
})
ssh.connect(
{
host: options.host,
username: options.username,
privateKey: key,
}
);
}
)
}
The bug here isn't obvious, at least to me - but it caused me much heartache and pain because I was moving too fast and not thinking - but a good example of unexpected behavior by default.The bug is that there's not a closure around the ssh instance, so if you call this function multiple times it'll actually return the same connection instance.
The extra fun part of this bug is that the code worked - but because I was using the same instance, when I'd download multiple files in parallel, it would interleave the data, corrupting the files.
And the thing is, I know better. I've been doing js for decades.
I love js, but stuff like this is pretty frustrating, and can be a nightmare for new devs.
I’m not entirely sure what you mean by this, but it doesn’t sound like a correct diagnosis. Each call to `getSFTPConnection` creates a new `ssh2.Client` instance (unless `ssh2.Client`’s constructor does something weird), and the promise can only resolve with the value `ssh.sftp` passes (although there’s a missing error check).
Bad example!
You've either adjusted the code for posting or you misunderstood what the problem was, because no, that's not the case. The ssh variable is local to the getSFTPConnection function and is not reused between multiple calls to the function, and throwing a pointless extra closure in there wouldn't do anything.
- Lacks structural pattern-matching.
- Lacks first-class sum-types (although they can be clumsily encoded as objects with "kind" properties).
These are the sort of things people appreciate in "good" static languages, as opposed to the static languages you alluded to that Python/Ruby/JS/etc were rebelling against in the 2000s.
The existence of `any`; worse yet, the use of `any` in the standard library.
Mutable arrays are treated as covariant: the classic `cats : Cat[] ; animals: Animal[] = cats; animals.push(dog);` problem.
Methods are both co- and contravariant in their arguments by default, which is comically wrong. Member variables which are functions, meanwhile, are handled correctly.
`readonly` is a lie:
const test: {readonly a: number } = {a: 0}
const test2: {a: number} = test
test2.a = 5
`Record<string, string>` is actually `Record<string, string | undefined>`. There's no way for an interface to specify that it really does return a value at any string index (e.g. for a map with a default value).`{...object1, ...object2}` is typed as the intersection `typeof object1 & typeof object2`, which is not correct.
const a: {a: 5} = {a: 5}
const b: {a: 4} = {a: 4}
const spread = <L, R>(l: L, r: R): L & R => ({...l, ...r})
const impossible: never = spread(a, b)
You have to resort to bizarre conditional type hackery to control when unions distribute and when they don't.You can constrain generics as `<T extends string>foo(t: T) => ...` but not `<string extends T>foo(t: T) => ...`, which makes many functions (e.g. Array.includes) much too strict about what they accept.
Personally I want the EVM to (eventually) get the same level of adoption and mindshare as the JVM.
And currently wider Elixir use/education seems like the path to that.
Compile-to-JS languages add a big impedance mismatch when you inevitably need to use JS libs. Typescript's JS + types philosophy helps with this issue (it still sucks though).
The externals binding system for JS libs works well for integrating libraries. And react bindings are included in the standard lib, they also work great. The compiler is fast and produces generally quite reasonable JS.
You could argue that it's not "better" than elm in a theoretical sense. But for practical work it's much closer to the mainstream of web dev in mindset and syntax. Easier to learn, much easier to integrate with other frameworks or into existing teams & codebases. And it's less opinionated about rendering: it generally assumes you're using react, but you don't have to and it can emit anything if you do some extra work wiring it up. I have used it with node and even, irresponsibly, deno fresh.
Been using it whenever I can for frontend stuff for a year now and haven't enjoyed a language this much since... well... elixir.
Elm is a heavily stripped down purescript, plus an ambiguously-benevolent dictator for life. Also terrible interop. It's a good learning environment but I would strongly recommend against using it for anything big.
Rescript is basically Ocaml with half-js syntax.
Typescript is bizarre. On a scale from 0 (C) to 10 (Haskell), it's a 12, a 6, and a 2 duct taped together. It's got some incredibly powerful features (template literals, conditional types), but it's missing most of the standard "advanced"-but-production-ready type system features (e.g. no HKTs, no type-directed emit) and the foundation is absolutely riddled with soundness holes.
1. Static Typing is planned and currently the top priority of the team
https://elixir-lang.org/blog/2022/10/05/my-future-with-elixi...
2. There is a type checking tool
https://github.com/jeremyjh/dialyxir
3. You can go a long way with pattern matching and guides in the meantime and have alot more guarantees that a typical dynamic typed language.