Phantom types in Rust are necessary only because sometimes types need extra information to pass to trait impls. I'm pretty sure they weren't made with typestate/newtype pattern in mind.
As someone who develops monitoring systems, CRMs, ERPs and ticketing systems this is hitting very very close to the pain i normally deal with. Many programmers sadly really underestimate the importance of business logic flaws, let alone making phantom types or similar mechanisms to restrict behavior.
Yeah, it's definitely one of those "be the change you want to see" things. If you can start implementing them in the bits of the codebase you own, you may be able to start convincing people through the organisation to use them
You can get this concept to dynamically typed languages as well! Clojure supports metadata on elements, which can contain units of measure, provenance, etc. The book Software Design for Flexibility leverages metadata in scheme to implement an automatic type conversion system.
Amazing video, I had never heard of phantom types until now. A small suggestion, it would be helpful if you zoomed in on the code a little more in the video because it was very hard to see on mobile
You can make generic functions with them across many different phantom types. For example, with the `Distance(a)` type, I could write: `fn add(dst1: Distance(a), dst2: Distance(a)) -> Distance(a)` Then I could add ANY two distances, as long as they have the same phantom type. If I was using new type wrappers, I'd have to have an add_miles, add_kilometres function, etc. Also, phantom types have no runtime overhead, as they're compile-time only.
Any video discussing strongly typed physical units/quantities is an automatic like from me. How easy is it in Gleam to have it work out the correct return type of (say) dividing a quantity in metres by a quantity in seconds?
Very easy! Phantom types are just generic type parameters at the end of the day. `fn calculate_speed(dst: Distance(a), time: Time(b)) -> Speed(a, b)` You'd have to write it as a function though, and have a `Speed(a)` type. You wouldn't be able to just use primitive operators (which makes sense as `Distance` and `Time` aren't primitives)
Phantom types are probably closer to branded types. Dependent types usually require the compiler to know something about the actual runtime value (e.g. my dependent type is only EVEN numbers) whereas phantom types are effectively user-assigned tags.
@@IsaacHarrisHolt In the dependent-type world you could have Open and Closed be the constructors of a single enum-ish type, restricting the "type of the type" of File to be just one of those two (and not an unrestricted type). If I understand it correctly, Gleam has only 2 "levels" of "stuff" - values and types, with no way to differentiate different types (noone can stop me from working with a `File(File(Int))` or something. One might say that the "type of types" (the type of stuff you can put in `File(here)` is "Type" and not something more specific, i.e. the type of types is an "open type", in contrast with a closed one (like a hypothetical enum).
@@IsaacHarrisHolt ValueObject is a name often used in Domain Driven Design, and it describes a concept of creating classes/concepts/types (depending on the paradigm) that wrap primitive types, so instead of using a float, one would create a (for example) Measure class that would wrap a raw float. It's basically a design pattern to fight "primitive obsession", so the goal is the same, but it's in runtime, and allows to have for example methods that would allow adding Miles to Kilometers but with proper conversion ;)
@@antoniong4380 You do. Instead of using some phantom types, just just use normal types. The disadvantage is that it can be a lot of boilerplate, as you now have a full type, so in places where you need a float (for example in some library you're using) you cannot directly use your own type, but must do a conversion, or break encapsulation, something like that.
With ValueObjects you could achieve compile-time checks as well since you can make methods that only take said classes, thus forbidding certain unwanted behaviours. For example, I could make a value object for Email, and validate that the string provided is not empty, has a certain length and matches my email regex. With that, I know that anytime I'm using an Email elsewhere, I don't have to check for such things. Thus any code that relies on them can work based off those assumptions at runtime, and leverage their properties/methods at compile time
The first 500 people to use my link skl.sh/isaacharrisholt01251 will get a 1 month free trial of Skillshare!
Now I can finally understand the rust phantom type here 😂😂😂
Awesome! Glad this was useful
Phantom types in Rust are necessary only because sometimes types need extra information to pass to trait impls. I'm pretty sure they weren't made with typestate/newtype pattern in mind.
THIS is the kind of gleam videos we need! More of this please :)
Of course!
As someone who develops monitoring systems, CRMs, ERPs and ticketing systems this is hitting very very close to the pain i normally deal with. Many programmers sadly really underestimate the importance of business logic flaws, let alone making phantom types or similar mechanisms to restrict behavior.
Yeah, it's definitely one of those "be the change you want to see" things. If you can start implementing them in the bits of the codebase you own, you may be able to start convincing people through the organisation to use them
the camera fix is looking good!!
Thanks!
You can get this concept to dynamically typed languages as well! Clojure supports metadata on elements, which can contain units of measure, provenance, etc. The book Software Design for Flexibility leverages metadata in scheme to implement an automatic type conversion system.
Interesting! I don't know how many languages are likely to support it, but that's pretty cool
Amazing video, I had never heard of phantom types until now. A small suggestion, it would be helpful if you zoomed in on the code a little more in the video because it was very hard to see on mobile
Thanks for the feedback!
What are the advantages of phantom type parameters over new type wrappers?
You can make generic functions with them across many different phantom types.
For example, with the `Distance(a)` type, I could write:
`fn add(dst1: Distance(a), dst2: Distance(a)) -> Distance(a)`
Then I could add ANY two distances, as long as they have the same phantom type.
If I was using new type wrappers, I'd have to have an add_miles, add_kilometres function, etc.
Also, phantom types have no runtime overhead, as they're compile-time only.
I'm sure most of the time we care about runtime bugs, thats why we do testing
Sure, but another tool in your belt is always helpful!
Any video discussing strongly typed physical units/quantities is an automatic like from me. How easy is it in Gleam to have it work out the correct return type of (say) dividing a quantity in metres by a quantity in seconds?
Very easy! Phantom types are just generic type parameters at the end of the day.
`fn calculate_speed(dst: Distance(a), time: Time(b)) -> Speed(a, b)`
You'd have to write it as a function though, and have a `Speed(a)` type. You wouldn't be able to just use primitive operators (which makes sense as `Distance` and `Time` aren't primitives)
Phantom types look similar to dependent types!
Phantom types are probably closer to branded types. Dependent types usually require the compiler to know something about the actual runtime value (e.g. my dependent type is only EVEN numbers) whereas phantom types are effectively user-assigned tags.
@@IsaacHarrisHolt In the dependent-type world you could have Open and Closed be the constructors of a single enum-ish type, restricting the "type of the type" of File to be just one of those two (and not an unrestricted type). If I understand it correctly, Gleam has only 2 "levels" of "stuff" - values and types, with no way to differentiate different types (noone can stop me from working with a `File(File(Int))` or something. One might say that the "type of types" (the type of stuff you can put in `File(here)` is "Type" and not something more specific, i.e. the type of types is an "open type", in contrast with a closed one (like a hypothetical enum).
Interesting. Seems I need to do more research into type theory 😅
These are basically ValueObjects...
I'm not totally sure what they are! Are they a language-specific thing?
@@IsaacHarrisHolt ValueObject is a name often used in Domain Driven Design, and it describes a concept of creating classes/concepts/types (depending on the paradigm) that wrap primitive types, so instead of using a float, one would create a (for example) Measure class that would wrap a raw float. It's basically a design pattern to fight "primitive obsession", so the goal is the same, but it's in runtime, and allows to have for example methods that would allow adding Miles to Kilometers but with proper conversion ;)
I might not understand what you mean. It doesn't feel like you could achieve Compile-time checks with this strategy
@@antoniong4380 You do. Instead of using some phantom types, just just use normal types. The disadvantage is that it can be a lot of boilerplate, as you now have a full type, so in places where you need a float (for example in some library you're using) you cannot directly use your own type, but must do a conversion, or break encapsulation, something like that.
With ValueObjects you could achieve compile-time checks as well since you can make methods that only take said classes, thus forbidding certain unwanted behaviours.
For example, I could make a value object for Email, and validate that the string provided is not empty, has a certain length and matches my email regex. With that, I know that anytime I'm using an Email elsewhere, I don't have to check for such things. Thus any code that relies on them can work based off those assumptions at runtime, and leverage their properties/methods at compile time
I think Kotlin also has this construct, but it is not called I like that.
Phantom typing is just the generic name for the concept. Languages may have their own names for sure
Please complete 💯 subscribe 😢