Typescript Is SURPRISINGLY OK For Compilers??
Вставка
- Опубліковано 25 сер 2023
- Recorded live on twitch, GET IN
/ theprimeagen
MY MAIN YT CHANNEL: Has well edited engineering videos
/ theprimeagen
Discord
/ discord
Have something for me to read or react to?: / theprimeagenreact
Hey I am sponsored by Turso, an edge database. I think they are pretty neet. Give them a try for free and if you want you can get a decent amount off (the free tier is the best (better than planetscale or any other))
turso.tech/deeznuts - Наука та технологія
The channel reminds me of the days when there was a person in town who would read the news out loud for the people that didn't know how to read.
Prime is a Tech Crier
1:58 As a Ohioan, all I can say is that we are coming for you, you cannot escape.
I have only spent a few days in Ohio about twenty years ago but that was enough. I'm one of you and just waiting for the call to "activate".
I once slept at a rest stop in Ohio on the way back from Indiana to New Jersey, I am also now an Ohioan
The name is Thegen
TypeScript: Allows you to make strongly typed printf from scratch
Prime: "it's only fake types"
he means that it is still JS understand it, and you got all the power in JS, and it is hard for the TS devs, to remove all the power of JS with Types, so it come in to TS.
this normaly happens with the any, null, unknown types, so if you remove them it is not as fake types but still.
for me i don't care as i don't need the types to be my Parents, i have learn the places/thing you need to fix or not do, so the JS types work perfect for me, but Prime can't have that in the mind.
Meanwhile Elm - that's cute
@@nomoredarts8918 ye
what's the problem with unknown? I understand it's as sound as other types. @@zeocamo
There at the bottom are 2 different kind of people: those who use any and those who use ts-ignore
~ 12:58 : PhantomData is for when you’re using something like unit structs as a generic argument for your types to change the type signature of your impl blocks so you can expose different methods depending on what ‘state’ your struct is in but you don’t actually store those unit structs as a field.
Definitely a little confusing the first couple (or tens) times you try to implement it, but useful to only allow methods a() b() c() in ‘impl MyType for MyType’ and d() e() f() in the ‘MyType’ variant.
Can just use State directly, if state is 0 sized compiler is smart enough to phamtomdata it. If not you probably want to use it anyway.
Phantom data is a way of telling the compiler that you want the field to act like a specific data type without storing the actual value. I've used this in the past with an interface so that I could call the methods without implementing it concretely. The usecase that I used it for was in a cryptography secret storage library. I used the phantom data to attach an interface to the master key that locks the data so that it can call encrypt/decrypt directly on the vault type with the key. The interface itself is just the API for calling the crypto functions on the vault and having it attached to the key, means that you can have multiple key types with multiple crypto algorithms associated with them. Another usecase would be in a case where you've serialized data into like a binary format but you still want the type information to be associated with the data. That way you can cast the data later back to the type.
I would never use typescript for a compiler. The type system isn't actually a type system and you're effectively writing your compiler in JavaScript at the end of the day. I guess you could use static type script but I don't know why you'd waste your time. Ocaml does everything you want and then some
Probably you would bother because you are writing a compiler that targets javascript or webassembly, and the easest way to get software portably if you are already operating in the javascript ecosystem is to use something based on javascript.
@@homelessrobotNaw, if I had to target JavaScript, id rather use something like Dart which has static typing. Or I would just use Reason/Bucklescript via Ocaml. You don't really need to write a compiler for a specific target anymore given that so many language ecosystems have multiple targets that they can support. If I build something in Ocaml, it can target the LLVM, JavaScript or WASM, and various other architectures. You could even target the JVM via the GraalVM. As prime pointed out, Generics in TypeScript and Types in TypeScript are a mess. For example, if you wanted to support multiple number types, you would be in a rough spot.
It doesn't help that the tooling won't show you the "resolved" type shape without hacky workarounds. Instead you have to follow a chain of extended types building up your own mental model of what the final resulting type is.. Addressing this is a pretty popular issue on GH.
You can write a type that simplifies the type into the resulting type. It's simple really, you just build up the type from ground up based on a union of types.
You can see how type-fest implements this under its Simplify type.
It does have performance implications, however,because TS would need to visit each intersection in T and rebuild the type anew. But it is possible.
Prime you should couver "rinha de backend". It was a "competition" to make a simples backend in a limited machin with loadbalancer, everything and - the better language ( or dev lol) won. Lol
Ps: It means something like backend dogfight in portuguese!
@primeagen this 🎯
0:37 plzoo was created by a college professor of mine, Andrej Bauer, who taught me a class called Programming language principles
I tried to use TS compiled through a couple of compiler projects (one to C++, one to LLVM), and during that struggle I discovered that Nim is almost exactly what I was looking after, because it targets both C/C++ and JS. Well, after using Nim for a while I think the native mode is great but the JS mode not so much, but it's not a big deal because I'll use emscripten instead of Nim's JS mode (once I finish porting my JS/TS code that is). I may still use JS mode occasionally, for that reason I'll try to improve it.
love matklad articles, he's a rust/zig legend
How you described ocamls type system reminds me of elixirs
Heads up Flip the audio mix on this one is way too quiet
it was my birthday and i was on a bender
-Flip
24:00 and then you go off the right end of the top line when you hit an edge case in the type system that they didn't think off, so you still fall right back down to hell.
"It's bimodal programming, I'm not sure how else to describe it other than that. That's really frustrating."
A great example! Here *ThePrimeagen* is hitting on the issue with gradually typed languages in general. Haskell struggles to encode fundamentally structural aspects of a program nominally, but dynamic languages grind to a confusing bug-fest in the face of problems a nominal program has no problem with. Languages that provide both have their own issues, but they feel navigable. This is why PureScript (which also compiles to JavaScript) with it's row polymorphism feels so great to me, it really embraces both worlds. (Through I'd love it if the core library embraced row poly a bit more endemically).
i kinda wanna learn haskell now
do typechat next. pydantic can suck an egg. i only have 1000 tokens of context to work with and typescript's type format uses up less of that (i think idk i haven't actually tried it yet)
I get TOC by seeing you highlight phrases except for the first and the last letter. Please help me.
Combine Purescript and Rust using Waterslide? Waterslide is an unmaintained, yet supposedly stable library to share datatype definitions between the 2 languages.
I'm new to programming, so my idea might be off, but I'm thinking i can get by learning Rust(i have the time and patience.. even if it hurts me), Purescript, WebAssembly, HTMX and refresher on my old Html Css days. What objections, tips, secrets would you share on this logic? 🙏 thanks.
3:39 Indexes (for all intents and purposes) suffer from the same memory management issues as pointers. They can be better in some ways if implemented well (no ownership, typed ids, bounds checking, etc) but also you can do many similar things for pointers. Saying "use indexes" is the same as saying "circumvent the borrow checker". Thats fine, but also why are you using rust if you're just going to circumvent all the safety its trying to give you.
Just talking about memory safety here though. The post made some good points about serialization etc
I think rust doesn't support defining a generic parameter to a struct, but then not using it in a struct field. PhantomData is just there for to fool the compiler.
PhantomData was originally more for lifetime reasoning, and while it's less necessary nowadays there are still some edge cases where the compiler needs it to understand what's going on. Or if you want an unused generic, it'll make that work too I guess
Typescript, language that came to the rescue and leaves even bigger mess
Just Microsoft things
😂😂😂😂😂
The issue isn't inference, it's which kinds of inference the type system makes. If the type system does too much inference, that's just a dynamic programming language. You just end up reinventing dynamic typing from the other direction. You see some people accidentally do this with powerful type systems where the goal becomes not to construct powerful, descriptive types, but to have the type system infer as much about the types as possible to save yourself typing.
The point of a good type system is that yes, you DO have to think about certain things twice. That dual path is the entire goal. You come at a problem from two different angles, and you have an inference engine that can tell you when those two angles conflict, in cases where you wouldn't have noticed the conflict yourself.
The point of a good type system is to say "I'm pretty sure about A" and then you say "I'm pretty sure about B", and then when you go to write something that involves both A and B the type system will think through some of the implications of their interactions for you, and it can say "wait given what you told me about A and B this doesn't actually make sense". This is useful because often the type of A is easy to reason about and the type of B is easy to reason about, but their interaction is hard to reason about, so pointing out type errors helps you diagnose assumptions about A and B that you forgot or that you weren't thinking of.
The problem is that if you let a type system become too inferential, then this benefit goes away. If you have sufficiently powerful inference, then you can define A, and write the interaction between A and B, and the type system can reason about the type of B based on that interaction. But it does that by assuming the interaction, the complicated part that's hard to reason about, that you wanted help with, was correct. At best, if you interrogate it about its inferences you'll notice that it inferred something weird about B. But that won't be an error, so you won't necessarily know to look.
Simultaneously, there are a lot of useful type inferences that are not universally safe to make. TypeScript is absolutely full of these - things where you think it ought to infer a type because 99% of the time it would be safe, but it can't because the type could actually be violated.
The answer is static typing with minimal inference in the LANGUAGE and tons of inference in the EDITOR. auto and var suck if you leave them in your code, but they are fantastic for entering code. You don't need the language to infer the type of a key, you need the editor to infer it and insert it for you. You do this, and suddenly you get the best of all worlds: you don't have to enter trivial types all the time, your editor can guess types heuristically and it isn't a big issue in those cases where it could technically be wrong in some exotic circumstance, when the editor inserts the type you can immediately see "wait that's not right" instead of hoping you mouse over it and read a tooltip or something, and when you're reading code you can see all the types easily.
5:19 wtf happened there I was playing this video on full volume. Everyone around me gave a wierd look 😂 I couldn't even explain to them what just happened there
just say you were watching a porn. it's easier to explain.
Awkward to hear the "Haskell is not for production" memes when it's much more common in production than OCaml. I wouldn't choose it for most of my projects nowadays, but it'll be a cold day in hell before it's a worse choice than OCaml for the vast majority of projects, compilers included.
You forgot to put the link in the description
Ppl really forget that deno translate ts to js in order to run it
Thats not surprising. You know what would make it better? If it had runtime type checking.
Naming types and variables the same is not a problem-it's conceptually the same as using `class` because a class is both its own type and value. If your intention is to import the _type_ from another file, the use `import type`. There's no other downside to this. TypeScript knows the difference, because they cannot be used interchangeably (i.e. using a type as the value and vice versa), which is why it allows it.
sure, but the class gets exported both together.
@@thekwoka4707 So does importing the function as value, if it has an associated type with the same name it will also import said type information. TypeScript uses on declaration merging for this behaviour.
binAry the way you pronounce it chokes meXD
“Just use indexes instead of pointers” wait wait wait. Pointers are literally indexes into ram so we’ve come full circle
IS OHIO A LIBRARY
All supply chain attacks are just Ohio probing for weaknesses
It can also be a string if you want
wtf was that voice with Deno jajajajajajajajajaja
2:50 please elaborate
ok compiler
wow
I'll tell you a secret... PHP has runtime type check...
I have never heard of the Ohio thing... what the what!? Lol
Come to the dark side, we have Rescript
5:09 I know what happend there was Eminem - Ken Kaniff :)
I wrote a toy compiler in ts and can't recommend it. Performance was at least 100x slower compared to my previous compiler written in C, compile time evaluation sucks because you don't have real data types, and because node.js and the browser have different API's you end up writing a bunch of your own abstraction layers for basic stuff like file I/O and buffers.
Well, yeah? They explicitly say to use it for prototyping, so performance and portability is pretty pointless.
So, you used node and not deno?
@@SimonBuchanNz I chose ts for my own reasons, those being the ability to run in the browser and (what I thought would be) "good enough" performance.
Even for prototyping I wouldn't consider it to be a good choice, development went only marginally faster than with C and as I already mentioned any interesting features you'd actually *want* to prototype are way nicer to implement in a language with real types.
@@benbowers3613 pretty sure I tried it at the time and even just console.log("hello world") took several seconds to run, I'm not putting up with that
@@pokefreak2112 shrug? Might depend on experience, I tend to find greenfield typescript to be super fast, it's only when you start productionising that it becomes more of a pain, and I bet I'd be pretty garbage at C.
Rust not yet production ready... i thought i should rewrite all with Rust. What am i supposed to do now?
Rewrite everything in JDSL
rewrite in C#
@@postsupremacy you mean Java 😆
@@tobyzieglerrrJava is a shitty version of c#
Rust is production ready
all of the languages he list here is fine for prod. and they are use for it.
Fun police
its ok until the compiler encounters *any*
Honestly python is probably nicer for this now that it has a match operator
3:30 The problem, @ThePrimeTimeagen, is that they've just called you an *hobbyist* tinkerer. 😆
Do you resent that?
For me pointers are pseudo- *random* indexes and indexes are *ordered* pointers.
And I like *order.*
You should like order, too: How do you think Sauron, Darth Sidious and Keyser Söze had success ? 🤣
In the end, the whole issue is to know how to efficiently *hash* memory.
SACROSANCTAE MEMORIAE SECTIONEM IN AETERNUM VIGILEMUS
Most people don't even understand this video to begin with
I find this not that good at all... Actually do parsing in TS lately - and also a simpler parser I wrote in C++ for the same thing. Honestly I think some choices here I dislike - but ts is not really clean enough for me for this. But yes... its "ok" meaning that you might be not puking while coding in down - which is good in practice... but if I could choose I would not do it in TS.. just this way rest of the team can read it easier....
Typescript is a bonafide silly goose
boner
I guess the reason is that it let’s you describe a grammar, by accident though
13:28 "all sorts of problems when you name them the same thing" - no there isn't? the namespace for types is completely separate from JavaScript variable/symbol names - type hints can only refer to types, which cannot be referenced from anywhere in JS. what's the problem?
There's no problem and you're right. I use this pattern a lot and have never run into any issues. It's effectively the same as using `class`, it just abstracts away this relationship.
@@dealloc I use it mostly for functions, which for some reason, unlike classes, don't automatically get a type - I actually opened a TS issue and pointed out this inconsistency, and they were like, nah, that would be way too confusing. Okay? But when the class name somehow refers both to the constructor type and the instance type, somehow that's not confusing? I think maybe TS is a little too inspired by C# on this point - it's almost as if they forgot that classes in JS are actually instances. 🤨
@@RasmusSchultz It does make sense to me. The class type points to an instance of that class, whereas `typeof Class` points to the type of the constructor. You can think of the constructor as being equivalent to pointing to a function, since both are callable, albeit one creates an instance through `new` keyword, while the other does not.
Classes in JS can be used to create instances, but are not themselves instances; they are more of a blueprint.
@@deallocclasses are also instances of Function - my point is, a class declaration generates both a type { new(): T } for the constructor function as well as it's return type T. So with one declaration, you get something that is immediately useful for type-hinting, and with functions you do not.
@@RasmusSchultz The reason is that TypeScript wants to differentiate between function declarations and classes because they have different rules when it comes to instantiation. A class is required to be constructed using the `new` keyword whereas a function call does not require it, even if it acts as a "class". Typeof makes this distinction less clear and so TypeScript just wants to make it easier by avoiding this distinction ( { new(): ... } vs { (): ... }.
Even though classes are syntastic sugar for prototypal inheritance, they also come with limitations imposed by the language spec and will be thrown at runtime.
I dislike these cases where to understand the app you have to both get a grasp of the JS code and of the complex type system as well 😕 I prefer to use basic TypeScript only and have my life easier. Tried to incorporate some fancy generic in my recent code, reverted quickly.
I'm sorry, but please call them indices, not indexes. Idk why; It's just sails smoother by me
javascript, I love it
Was that a quote from senior JavaScript engineer interview?
I wouldn't recommend.
Union types is just as bad as inheritance and I'll die on this hill if necessary.
What are your thoughts on "GPT Engineer" and "MetaGPT"? Do you think they will evolve in the following years to become even more capable?
Yeah, it might be good from a pure language perspective, but it still compiles down to regular javascript that frankly is dog slow for a compiler. DO NOT MAKE YOUR TOOLS IN JAVASCRIPT if its for public usage. Slow developer tools have wasted so much time for js developers.
It’s not PRE READ it’s PREAD
Yeah let's write compilers in TS then wonder why a standard project takes 3 days to compile....
i am a bit disappointed in you. Not the first time, that karen needed to interfere :-/
just joking ;-)
Strange they picked Deno over Bun.
I find Bun to be the MUCH MUCH better TS scripting runtime.
if you let the legacy world behind deno is really awesome, all the modern feature and also way more stable then bun
I thought the new PHP was the thing now a days! The world has gone upside-down. As far as I'm concerned if your high level language compiler is not written in itself, from lexing to code through optimisation to code generation, you have not made a real language yet. All this resting on LLVM (C++) is backsliding.
*Enter CPython*
Based.
Coming from OCaml without knowing any TS, this video was a complete shitshow to me 😂
All this TS interface generic blabla voodoo and you still need to add some hack "tag" field because you need to dispatch on the type at runtime... I sometimes wonder if people have simply forgotten that JS can do OOP and you can even write "class" if you are allergic to prototoypes. Instead of writing switches over some hacky string field, maybe use the features the language comes with (in this case, dynamic dispatch)???
What the actual F is this video even about? I don't get what that code is doing
its literally in the title of the video.
idk...TS can be pretty good, but this guy is making a LOT of confusing indirection...
First!
Compilers are simplest things in existence. You can write a compiler in anything. You don't even need a Turing-complete language to write a compiler. Just pretty much any language is ok.
Hot take: Java > TS
This guys a noob. Typescript is good .. but not like this. Tbh should just use node bindings to llvm. Also fwiw , you can use generics without ever declaring a concrete type/context and the program/expression can be proven correct symbolically.
Object.keys is Emotional Damage!