5 ms·
Stronger typing is more secure code, totally agreed. F#'s custom primitive types (I'm corrected, it's Units of Measure) are a feature that should be copied by e
by dep_b 9y ago
Stronger typing is more secure code, totally agreed. F#'s custom primitive types (I'm corrected, it's Units of Measure) are a feature that should be copied by every language. You're not passing around floats anymore with a descriptive name ('distance') but they're floats defined as kilometers or miles. Then you can define rules about how they should interact, like:
Mile distance1 = 12
Kilometer distance2 = Kilometer(distance1) // Does a conversion while constructing
void someFuncThatWantsKilometers(Kilometer distance) { }
someFuncThatWantsKilometers(distance1) // Does not compile
someFuncThatWantsKilometers(distance2) // Compiles
Even better you can do this:
Hour time = 1
Kilometer distance = 12
Kilometer/Hour speed = time/distance // Doesn't compile
Kilometer/Hour speed = distance/time // Compiles! Even gives you the 'new' type for free!
You could use it for everything where primitives have a deeper meaning than their pure numeric value. Database ID's, money or other financial or scientific values. Impossible to swap X and Y coordinates unless you implicitly do so.
You can eliminate so many confusing bugs just by having better typed primitives.
--- disclaimer: ---
This is not exactly how it works in F# but the languages are too different to really compare
- bonesss 9y agoJust for clarity: what you've described are (Units of Measure)[https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/units-of-measure https://docs.microsoft.com/en-us/dotnet/fsharp/language-refe...], which let you define types, and their relationships, so you can define meters, and kilometers as meters * 1000 and then rest safe that your spaceshuttle will be using the right units. Unit of Measure in practice: [<Measure>] type cm [<Measure>] type ml = cm^3 // a millilitre is 1 cm cubed To the point in the article about using types to convey meaning, as I commented elsewhere, you want F#s awesome type aliasing and single-case discriminated unions :) Type aliasing: type CustomerId = int // We can send now use IDs interchangeably with int, use CustomerId in method signatures, and expand the type with methods Single Case Discriminated Unions: type CustomerId = CustomerId of int let id = CustomerId (3) // Have to use a specific constructor let myFunction (CustomerId(id)) = // DUs can be unpacked in parameters printfn "%i" id // this function only accepts CustomerId's
- dep_b 9y agoThat's what it was called, sorry I was citing from the top of my head. I don't find the name Units of Measure a bit confusing as it seems to imply that it's only use is scientific units while there are plenty of use cases outside of that realm. A SandwichCount should not be confused with a BurgerCount!
- TeMPOraL 9y agoThat's one of two things I always dreamed to have attached to numeric values and used/propagated automatically by a programming language. The second thing is error bars.
- ecshafer 9y agoI will start by saying I agree with you, and also that many functional languages bring types to the next level, f# included. But I will also say this is possible in other languages to some degree with some level of boiler plate. For example, in java I can do similar. Instead of using float, wrap that in a class called kilometer and use that instead. Then define the api around it as needed (equals, compare, value, etc). Even this small thing I think is really important, and is helpful instead of using primitives or strings. Make email instead of string, dollar instead of just big decimal, etc.
- dep_b 9y agoIf it's hard to use people tend to avoid using it. Also the wrapped int would have to be private to avoid people to use it anyway.