re: relationship to TypeScript, I'm interested in which parts seem similar between the two to you. TypeScript is primarily structurally typed while F# is primarily nominal.
Sure thing, I'm still pretty new at F# but have a good handle on Typescript since I get to use it at work.
The three main things I find similar are the syntax, ability to compose new types from other types, and the ability to limit types to certain options. Both support | as an OR and support an AND operation. F# does a bit more allowing the product of types though.
Syntax Example:
Typescript
---------------
type Check = {
amount: number;
account: number;
...
}
F#
--------------
type Check = {
account: int;
amount: int
}
I think I need to become a bit more familiar though with F# to understand the differences better. I hadn't yet heard the term nominal to describe a type system before so I'll look into it further. Perhaps I'm focusing more on the syntax than is warranted...
Completely separately, how does one properly format on HN? Is it possible? Is there a doc somewhere? I'm fairly new to commenting...
Your example is actually a really good one for discussing these differences, and the syntax is indeed similar. That TypeScript code is introducing a "type alias," which is a special name for another type, rather than a new type of itself. (https://www.typescriptlang.org/docs/handbook/advanced-types....)
This is a concept that F# does not have, because F# doesn't support structural typing. In TypeScript, you could just as well specify that a function returns
{
amount: number;
account: number;
}
directly, and it would be equally compatible with anything expecting a "Check," because Check is just a shorthand for that object type. I can't easily declare that a function accepts { account: int; amount: int; } in F#, I have to declare that it accepts "Check." That is the main difference between TS and F# here, and incidentally also the most basic difference between structural and nominal type systems.
I'm not sure why the syntax is similar though, F#'s is based on OCaml's record syntax while TypeScript's is really just a JavaScript object literal but with semicolons instead of commas. I believe it supports commas too. It may be genuine coincidence, or the designers of TS may know of the ML family, which is highly influential among language designers and implementers.
You can use it like an interface, and accept loadReceipt functions for anyone who needs to load receipts... Then partially apply a SQL version, noSQL version etc.
Thanks for the formatting link, exactly what I was looking for to format code a bit better.
That actually helps me understand some F# code I wrote last weekend as well regarding construction of objects of a certain type...
I didn't know about the difference between nominal vs structural types but your explanataion makes sense. It's the subtle distinction between a type vs a type alias that you pointed out.
I personally use type aliases in typescript over interfaces, I think they're a more natural fit for JS than interfaces but that's a separate conversation :)
Language design is really interesting especially the choices designers make. I feel like they must all pull at least in part from each other and would be curious to explore any relationships but if its total coincidence that's pretty incredible too.
> I personally use type aliases in typescript over interfaces, I think they're a more natural fit for JS than interfaces but that's a separate conversation :)
The TypeScript docs recommend the opposite, preferring interfaces. Interfaces are also structurally typed in TypeScript though; the major differences are that interfaces can only be used to declare object types: https://microsoft.github.io/TypeScript-New-Handbook/everythi...
The fact that interfaces are limited to typing objects but type aliases can also be used for functions is an easy +1 to type aliases.
My view is that declaration merging is a bit of an anti pattern that could cause trouble if two different people write an interface with the same name but to represent different data... now they get merged incorrectly. A type alias forces more explicit behavior where composition must be done by creating a new type alias as the union of two or more other types
I think it’s a +1 to using both depending on where they fit. I use type aliases for defining unions and intersections and such and interfaces for everything else, personally.
And that’s an intersection you’re describing there, not a union. Interfaces are explicit about extension as well, they use the “extends” keyword.
Whoops, yes that is indeed a union not an intersection. Thanks for the correction. I'm still getting used to the terminology.
The explicitness I was referencing is around the declaration merging. Its automatic so unless there's a conflict between two interfaces with the same name but with different properties, a dev wouldn't know that it was being done.
I agree with using what makes sense for the scenario. I prefer type aliases for functions and interfaces for classes and that seems to work out pretty well so far.
You can do anonymous records in F# - you don't have to explicitly give a name to your record types - but structural typing solves similar problems to those solved by interfaces, and F# thoroughly supports interfaces, so I imagine it wasn't thought necessary to make the type system any more structural.