Adam
Adam

Reputation: 1009

Is F# Constructed Type syntax special?

I was curious about F#'s "constructed type" syntax. It's documented here.

type-argument generic-type-name

or

generic-type-name

With the following examples:

int option

string list

int ref

option<int>

list<string>

ref<int>

Dictionary<int, string>

I was curious if there's anything special about the "backwards" syntax, with the parameter before the type, or if it's just sugar for generic types with one parameter. The following is valid:

type 'a MyOption = // MyOption<'a> also works
  | MySome of 'a 
  | MyNone

But I could not get it to work with multiple type parameters. Why do F# developers prefer this syntax for types with one parameter? Is it possible or desirable to make it work with two?

Upvotes: 3

Views: 139

Answers (2)

Roland Andrag
Roland Andrag

Reputation: 462

Why do F# developers prefer this syntax for types with one parameter?

Not all of us do. I love F# and am in awe of it, but find the OCaml style distracting.

It gets especially confusing when the two styles are mixed - compare the readability of Async<Result<int,string list>> list with that of List<Async<Result<int,List<string>>>>.

Here is a thread with some arguments from both sides from fslang suggestions, which I think led to the deprecation of OCaml-style for everything but list, option and a few others.

I find it regrettable that the OCaml style is specified as the preferred option (for these types) in the various style guides, and used throughout the core libraries, while there is such a strong drive to make the language more accessible to newcomers. It definitely adds to the learning curve, as documented in this question, and here, here, here, here, here.

Is it possible or desirable to make [OCaml style naming] work with two [type parameters]?

I think a better question is: "Is it possible to only use .NET style?".

Unfortunately the tooling shows types the way they are declared, and the core libraries consistently use OCaml style. I have asked Rider about always showing declarations .NET style in code vision, who referred me to FSharp compiler services. I have not (yet) investigated that avenue further.

In our own code we have taken to overriding the OCaml signatures of functions that ship with F# and other libraries as we come across them, for example:

[<AutoOpen>]
module NoCaml =

  module List =
    /// Returns a new collection containing only the elements of the collection for which the given predicate returns "true"
    let filter = List.filter : ('a -> bool) -> List<'a> -> List<'a>

    /// val groupBy : projection:('T -> 'Key) -> list:'T list -> ('Key * 'T list) list (requires equality and equality)  'T is 'a 'Key is 'b  Applies a key-generating function to each element of a list and yields a list of unique keys. Each unique key contains a list of all elements that match to this key.
    let groupBy = List.groupBy : ('a -> 'b) -> List<'a> -> List<'b * List<'a>>
    
    // etc.

This solves the problem in almost all cases (some exceptions like list construction using [] remain, and need to be overridden at the point of declaration).

I'm not sure what influence this has on performance at runtime - hopefully the extra function calls are optimised away.

Upvotes: 1

Brian Berns
Brian Berns

Reputation: 17038

The backwards syntax is a legacy from OCaml. Personally, I never use it. If you really want to, you can make it work with multiple type arguments like this:

type MyMap = (int, string) Map

However, this generates a pointed warning (that might soon become an error):

This construct is for ML compatibility. The syntax '(typ,...,typ) ident' is not used in F# code. Consider using 'ident<typ,...,typ>' instead. You can disable this warning by using '--mlcompatibility' or '--nowarn:62'.

Bottom line, I would recommend always using .NET syntax instead: MyOption<'a> instead of 'a MyOption.

Upvotes: 6

Related Questions