Hacker News new | ask | show | jobs
by kowalgta 2197 days ago
We use F# to build fairly complex portfolio management apps. It's a great match for F# to Javascript transpilers (Fable, WebSharper). It makes it easy to share functions and types between UI and backend and to have powerful type safety. I.e. you change your DB data type and compiler informs you where in the UI you need to make appropriate changes.

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.

2 comments

I love this idea too. Is it possible to create a type (in F# or Typescript) to represent an idea like greaterThan2? A value whose type is greaterThan2 would have the obvious constraint the value is always bigger than 2. Having the compiler check such a condition would be awesome.

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.
As others mentioned - using smart constructor technique, but not directly as F# has no dependend type capability.

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...

This is called dependent typing, and F# doesn't have it as far as I know. There are other ML-family languages that are capable of it however: Liquid Haskell and Idris are the two I can recall off the top of my head.
in F#, yes, in TypeScript, not really. The technique involves using a combo of an opaque nominal type, and a smart constructor:

    (* 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`.
This is so true. It's why I love ML-languages: they help me refactor, the help me logically structure my core (through its data types) even before I start, and it helps catch illegal states (if I use the types right) so that unit tests aren't necessary!