Clarification of List<'T> definition syntax - f#

While reading the source code of F#, I found the definition of the List<'T> type, which looks like this:
type List<'T> =
| ([]) : 'T list
| (::) : Head: 'T * Tail: 'T list -> 'T list
Could someone, please, explain the above syntax? The compiler warns that 'This construct is deprecated: it is only for use in the F# library'. Is the syntax just an old way to define discriminated unions? If so, why does Tail has the type of 'T list -> 'T list instead of just 'T list?

I believe what this means is the following:
type List<'T> =
| ([]) : 'T list
| (::) : (Head: 'T * Tail: 'T list) -> 'T list
That is to say, (::) is a function that, given an ordered pair 'T * 'T list generates a 'T list.
The deprecated construct is giving type annotations to the discriminated union constructors. You never needed to do that, in any version of F#, as far as I know. I don't know why they had to do that in the F# library.

Related

Order of type constraints in F#

This works in F#4.0:
type Something<'a, 'b when 'b :> seq<'b>>() =
This doesn't:
type Something<'b when 'b :> seq<'b>, 'a>() =
Unexpected symbol ',' in type name. Expected '>' or other token.
What's the reason that the order of the type constraint matter?
Because it is in the spec - the relevant part is this (from the start of section 5):
typar-defns:= < typar-defn, ..., typar-defn typar-constraints_opt>
the constraints need to go at the end.
In this typar-constraints must always start with when and can't appear anywhere else.
Type constraints and type arguments are two different things. In your example, 'a and 'b are two type arguments, and when 'b :> seq<'b> is the (only) constraint.
Now, the way you've written your first example it seems to suggest that the type argument definitions have something to do with the type constraints, but that's only in the appearance. Note this (working) code:
type Something<'b, 'a when 'b :> seq<'b>>() =
member this.A (a : 'a, b : 'b) =
()
First, you define all the type arguments. Only afterwards come the type constraints, and the constraint still applies to 'b, not 'a - while it looks a bit confusing, the constraint expression isn't 'a when 'b :> seq<'b>, it's just the when 'b :> seq<'b>.
This is actually pretty much the same as in C#, another .NET language:
public class Something<TA, TB> where TA: someConstraint where TB: someOtherConstraint
The constraints are more visually separate in C#, so people don't tend to make the mistake you made in F#.
Along with the above answers, the ordering allows the constraints to depend on multiple types. e.g.
type Something<'a, 'b when 'b :> seq<'a>>() =
Note:seq<'a> not seq<'b>

F# type definition syntax

I'm working on the '99 problems of F#', and saw the following definition of NestedList:
type 'a NestedList = List of 'a NestedList list | Elem of 'a
I'm a little confused by the syntax here, as normally there is only a type name after the type. Can someone clarify it? Thanks!
This is a discriminated union in a condensed syntax. You can also write it as:
type 'a NestedList =
| List of 'a NestedList list
| Elem of 'a
or
type NestedList<'a> =
| List of NestedList<'a> list
| Elem of 'a
This is using generics, where the type itself is taking another type as an argument.
F# has two ways of specifying a generic type:
C#/.NET style - SomeType<'a, 'b>
OCaml/ML style - ('a * 'b) SomeType
Those are really two ways of saying the same thing and can be used interchangeably. Which one to use is a matter of preference and code standards. Usually people use OCaml style for basic F# types like list, array or option, and C# style for user-defined and BCL ones.
So this is a definition of a generic discriminated union with generic type 'a.
The key idea is that each element has the type 'a.

Why is F# type inference not working in this case

In following case
let a = [1]
let f x = x
let b = a |> List.map f
a is an int list. We call List.map so for sure f must be a int->? function. Why is f still typed as 'a ->'a instead of int->int?
If this is how type inference (not)works in F# i see little benefits for it.
Is there an workaround without specifying the type for x in f explicitly?
Lets imagine x has type (A*B*C list ( D option, E list)))
Also another interesting thing:
open System.Collections.Generic
let d = new Dictionary()
d.[1] ="a"
This does not work unless the "new" keyword is removed.
Type inferencing works just fine here. f is inferred as having the type 'a -> 'a, which means that it'll take a value of the generic type 'a and return a value of the same generic type 'a.
In this case, type inferencing kicks in when you use it. Because a has the type int list, and because List.map has the signature ('T -> 'U) -> 'T list -> 'U list, it can infer types from the expression
let b = a |> List.map f
In this case, the 'T list argument is a, so it has the type int list. This means it now knows that 'T is int.
The function passed to List.map has the general form 'T -> 'U, but in this case we pass f, which has the form 'a -> 'a, which is equivalent to 'T -> 'T. Because of this, the compiler understands that in this case, 'T and 'U are the same types.
This means that it can infer that the return type 'U list must also be int list.
The function f is typed 'a -> 'a because it works at every type, not just int:
> f "foo";;
val it = "foo" : string
> f 0.8;;
val it = 0.8 : float
This an advantage: the generic type 'a -> 'a gives you the freedom to use f when it is safe to do so, as opposed to only at the type int -> int.
To add a little something to the fine answers already listed here.
There is one circumstance where you will want to not use the generic inferred type. The generic version of f will be slower than the type specific version. But F# has a keyword for taking care of that for you:
let inline f x = x
Which tells the compiler to use the type specific version of any operations you apply to x. The inferred type will remain generic. You will only need this in time critical section of code.
As noted elsewhere, the generic inference is a useful thing and you'll probably grow to like it.

F# denies existence of a constructor (probably type-constraint related)

On the following code, F sharp says: module xyz requires a value new : (IBlah<'a> * 'b) -> test<'a, 'b>')
I have tried supplying that exact constructor as an explicit new but it did not seem to help, though Intellisense though the type was the same. I expect I have somehow got the constraints wrong. Could someone please tell me what to change so the code compiles (without removing the constraints)? Many thanks.
First the fsi file:
module xyz
type IBlah<'a> =
abstract something : 'a -> 'a
type IHuha =
abstract something : unit -> unit
type test<'a, 'b when 'a :> IHuha and 'b : comparison> =
new : (IBlah<'a> * 'b) -> test<'a, 'b>
member huha : unit -> unit
The fs file is:
module xyz
type IBlah<'a> =
abstract something : 'a -> 'a
type IHuha =
abstract something : unit -> unit
type test<'a, 'b when 'a :> IHuha and 'b : comparison> (x:IBlah<'a>, y:'b) =
member x.huha () = printf "%O" x
The problem is the signature file is describing a constructor which takes a single value of type tuple
(IBlah<'a> * 'b)
The actual constructor though takes 2 values of type IBlah<'a> and 'b. Speculating that the .fs implementation is the one you want. If so then just remove the extra parens from the .fsi file
new : IBlah<'a> * 'b -> test<'a, 'b>

Trouble with FSharp active patterns

I'm parsing code quotations in FSharp and am building up pattern helpers. All was going well till I tried
let (|BinaryFn|_|) fn (input:Expr) =
function
| SpecificCall fn (_,_,l::r::[]) -> Some(l,r)
| _ -> None
let (|Multiply|_|) x =
function
| BinaryFn <# (*) #> (l,r) -> Some(l,r)
| _ -> None
The intention is to have a generic binary function matcher that returns the ''left'' and the ''right'' and then create specialised binary matchers such as Multiple, Divide and Add and Subtract.
However I get an error on the second pattern that
Error FS0001: Type mismatch. Expecting a
'a -> 'b option
but given a
'a -> 'c -> (Expr * Expr) option
The type ''a option' does not match the type
''b -> (Expr * Expr) option' (FS0001) (Shambolics)
Can somebody please enlighten me on what I should be doing here?
The issue here is that function doesn't only pattern-match the last argument, but also adds an additional argument (function is a combination between of fun and match). Remove the function argument input from the first pattern, and your problem will be solved.

Resources