On a slight tangent, you might enjoy the slides from Scott on a different topic - model checking with TLA+ - from NDC Oslo this week:
https://www.slideshare.net/ScottWlaschin/tla-for-programmers...
He does an incredible job conveying the ideas and intuition behind a lot of the terms and concepts for a given subject.
[1] https://bucklescript.github.io/
[2] https://ocsigen.org/js_of_ocaml/3.6.0/manual/overview
[3] https://discuss.ocaml.org/t/state-of-ocaml-and-web-assembly/...
I really love the ML-family type systems. They're really great aides to programming, and makes planning really nice. F# has that, plus that usability from C# and TS. It's also, like you said, got a really beautiful syntax IMHO.
I keep coming back to Rust though, because Rust is more general purpose, more performant, and applies better to the stuff I like to work on for my hobby projects. I'd love to use F# more but can't find a reason to at the moment.
TypeScript, Python and Rust have become my comfort languages, but I like Rust the best, because it's got a lot of really nice features from ML languages and a powerful and safe type system.
This works great with "makes illegal states unrepresentable" approach. It helps to reduce a need for boring unit tests and lets you focus more on expressing domain in code directly.
I kind of can do it for Strings by doing something like this
type ValidStrings = 'name' | age | 'dob';
The easy way around this is a function to determine this function greaterThan2(num) return num > 2;
but it'd be cool to express this level of dynamism as a type.Smart constructor technique works well with 'parse, don't validate' approach [0]. You can push type construction to the boundries of your system so that you can work on a domain code with more precise types. It's not always so rosy however as too much types can become a burden.
[0] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
(* GreaterThanTwo.fsi *)
type t
val make : int -> t option
val to_int : t -> int
(* GreaterThanTwo.fs *)
type t = Value of int
let make int = if int > 2 then Some (Value int) else None
let to_int (Value int) = int
Now, `GreaterThanTwo.make x` gives you a value of type `GreaterThanTwo.t option`, meaning it can be `Some` value or `None`. If it's `Some` value, that means it really does contain an int greater than two and you can get that int using `GreaterThanTwo.to_int`.I saw in a different HN post a few weeks ago that jet.com heavily used F# but obviously that's no more.
If a company were using F# and I were looking to switch and all the other stuff was a good match, the language choice would be a big pull for me.
While I use and am learning F# for a side project, there's no substitute for working with one professionally and shipping production code with it. I wish it'd be possible to convince my engineering group to use it.
(If you're interested in quant finance in London, I work at G-Research which uses F#; I write almost solely in F# there. You can contact me privately if you like at [email protected].)
More broadly there seems to be a big push for updated (perhaps even modern) versions of established languages and I think this is an incredible development to witness.
Rust, Go, Typescript, Scala, Kotlin are all the rage and seem to replace C/C++, javascript, and java. Others might exist for either side as well, this is just off the top of my head. From what I see and read as well functional programming seems to be a lot more popular and a lot of these languages are either built for it or with a good base of support for it. It'd be really interesting to have some hard data to know if this is accurate or just my perception.
My only other exposure to functional programming was an undergrad class required for a CS major (thank gosh it was a requirement too). It used Scheme for the language and Structure and Interpretation of Computer programs for the text book[1]. Looking back, this was the most useful class I took and the ideas have stuck and continue to inform me today. Its the one text book I've gone back to on occasion just to take a look and I've found some versions of it for JS and F# even on github which I use to help learn the language or review the concepts in languages I'm more familiar with.
[1] https://mitpress.mit.edu/sites/default/files/sicp/full-text/...
I feel like it really has hit the sweet spot between FP and OOP. And while I do tend to prefer the more terse ML syntax of F#, Typescript is pretty close and, frankly, offers a more ergonomic experience in your editor (F# syntax highlighting in every editor I have used is woefully underwhelming).
Add to the above the structural typing vs nominal and Typescript really does offer an immensely rich and flexible paradigm. The only feature I'd really like to see better support for in Typescript is currying/partial application These can be approximated in TS using it's incredibly powerful type system (dependent types using "extends" combined with inference using "infer"), but newer versions will throw an exception when the compiler sees an infinitely recursive type.
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.
It takes the form
type newname = oldname
You can do things like
type LoadReceipt = RecordId -> Record
let sqlLoad: LoadReceipt = implementation
let noSqlLoad: LoadReceipt = implementation
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.
I use this in my code.
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.
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
type Foo = Bar & Baz
Edit: found the page, its here https://www.typescriptlang.org/docs/handbook/advanced-types....Yeah, not sure I agree yet but I could be persuaded... I don't think type aliases prevent extension, they just force it to be explicitly declared.
And that’s an intersection you’re describing there, not a union. Interfaces are explicit about extension as well, they use the “extends” keyword.
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.
I use Javascript/Typescript professionally and I really like both of them. F# though has a minimalism in its design that is very appealing. Code I write in F# feels a lot less cluttered as opposed to the same function in Javascript/Typescript. This minimalism seems to be part of the language design with some following specific examples.
- `let` is used to declare variables and functions.
- The last statement in a function is implicitly the return value.
- The type safety/mechanism is great and seems to be extremely similar to Typescript's so its easy to pick up for me (I'm not sure which came first or if there's a relationship but it certainly wouldn't surprise me).
This along with some classic functional ideas that are built in like immutability by default, automatic currying of functions etc all allow for a language that seems be made to get out of the way. That's a very compelling idea for a language design to me that I previously haven't seen.
Because functions and variables are both declared with let, perceiving functions as data kind of finally clicked for me since I first heard that concept 3 or so years ago. Another feature was to not require a semicolon as a line terminator and instead use it for another meaning as a separator. While JS doesn't require semicolons at the end of a line, its reserved for that purpose.
Overall, I find F# to be a refreshing language (from javascript, python, java and even others like Rust or Go but I haven't used those as much so don't want to make bigger claims) and I'm glad to see a post like this on HN almost like validation of a community behind the language. I'd be curious to get a feel for some companies that actively use it as well.
I also love that there's a compiler for F# to Javascript so I can use it to build front end code as I learn the language. Its pretty incredible we can do cross compilation of high level languages like this and with others like Rust and Go to wasm etc
Edit: formatting of bullet points