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.
Related
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>
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.
From the book by Tomas Petricek the following code doesn't work as compiler is unable to infer the type of the dt parameter:
> Option.map (fun dt -> dt.Year) (Some(DateTime.Now));;
error FS0072: Lookup on object of indeterminate type.
And if we specify type explicitly everything works fine:
> Option.map (fun (dt:DateTime) -> dt.Year) (Some(DateTime.Now));;
val it : int option = Some(2008)
Or we can use pipelining operator to "help" compiler to infer type:
> Some(DateTime.Now) |> Option.map (fun dt -> dt.Year);;
val it : int option = Some(2008)
The question is why F# compiler can't infer the type of the dt parameter? In this particular case it looks quite easy to infer the dt's type.
The logic behind it can be the following:
the signature of Option.map is map : ('T -> 'U) -> 'T option -> 'U option
the type of the last parameter is DateTime option
so our map usage looks like map : ('T -> 'U) -> 'DateTime option -> 'U option
the compiler then can try to substitute DateTime as 'T to see if it would be correct, so we have (DateTime -> 'U) -> 'DateTime option -> 'U option
then it can infer the 'U type by looking at the body of the lambda-function, so the 'U becomes int
and we finally have (DateTime -> int) -> 'DateTime option -> 'int option
So why F# can't do this inference? Tomas mentions in his book that F# infers types by going from the first to the last argument and that's why the order of arguments matters. And that's why F# can't infer the types in the first example. But why F# can't behave like C#, i.e. try to infer types incrementally starting with what is known?
In most cases F# is much more powerful when speaking about type inference... that't why I'm confused a bit.
So why F# can't do this inference?
F# could do that as OCaml does that. The disadvantage of this kind of more sophisticated inference is the obfuscation of error messages. OCaml taught us that the result generated such incomprehensible errors that, in practice, you always resort to annotating types in order to prevent the compiler from being led down a type inference garden path. Consequently, there was little motivation to implement this in F# because OCaml had already shown that it is not very pragmatic.
For example, if you do that in OCaml but mis-spell the method name then you will get a huge error message at some later point in the code where two inferred class types mismatch and you will have to hunt through it to find the discrepancy and then search back through your code to find the actual location of the error.
IMO, Haskell's type classes also suffer from an equivalent practical problem.
F# can do everything C#'s type inference can do...and much, much more. AFAIK, the extent of C#'s type inference is auto-typing a variable based on the right-hand side of an assignment.
var x = new Dictionary<string, int>();
The equivalent F# would be:
let x = Dictionary()
or
let x = Dictionary<_,_>()
or
let x = Dictionary<string,_>()
or
let x = Dictionary<string,int>()
You can provide as much or as little type information as you want, but you would almost never declare the type of x. So, even in this simple case, F#'s type inference is obviously much more powerful. Hindley-Milner type inference types entire programs, unifying all the expressions involved. As far as I can tell, C# type inference is limited to a single expression, assignment at that.
My first thought was:
type ManyNavigationPropertyInfo<'a,'b>(cfg:ManyNavigationPropertyConfiguration<'a, 'b>) =
however it resolves 'a and 'b as obj, but it should be classes - therefore I did:
type ManyNavigationPropertyInfo<'a
when 'a : not struct,'b when 'b : not
struct>(cfg:ManyNavigationPropertyConfiguration<'a,
'b>) =
but that just throws an error saying
Unexpected symbol ',' in type name.
Expected '>' or other token.
What's the correct way for declaring such a type?
UPDATE:
My full code is:
type ManyNavigationPropertyInfo<'a,'b>(cfg:ManyNavigationPropertyConfiguration<'a, 'b>) =
member x.WithMany (expr: Expr<'a -> ICollection<'b>>) =
cfg.WithMany(ToLinq(expr))
and it comes up with 2 compiler errors saying that 'a and 'b should be not struct.
Your first thought is correct. You should be able to just write:
type ManyNavigationPropertyInfo<'a,'b>
(cfg:ManyNavigationPropertyConfiguration<'a, 'b>) =
// ...
The problem is probably somewhere later in the body of the type. From something that you wrote in the body, the compiler thinks that 'a and 'b must be of type obj (e.g. you're passing values of this type somewhere where obj is expected, or probably something more subtle).
You can try adding type annotations in the body of the class - this usually helps to find the issue, because the error message changes when you annotate the bit that F# compiler interprets differently than you expected.
To solve the immediate problem in your question - the syntax for specifying constraint is a bit different (first write all type variables and then constraints):
type ManyNavigationPropertyInfo<'a, 'b when 'a : not struct and 'b : not struct>( ... )
(But if you can post larger portion of code, maybe somebody can give a concrete advice.)
F# keyword 'Some' - what does it mean?
Some is not a keyword. There is an option type however, which is a discriminated union containing two things:
Some which holds a value of some type.
None which represents lack of value.
It's defined as:
type 'a option =
| None
| Some of 'a
It acts kind of like a nullable type, where you want to have an object which can hold a value of some type or have no value at all.
let stringRepresentationOfSomeObject (x : 'a option) =
match x with
| None -> "NONE!"
| Some(t) -> t.ToString()
Can check out Discriminated Unions in F# for more info on DUs in general and the option type (Some, None) in particular. As a previous answer says, Some is just a union-case of the option<'a> type, which is a particularly common/useful example of an algebraic data type.
Some is used to specify an option type, or in other words, a type that may or may not exist.
F# is different from most languages in that control flow is mostly done through pattern matching as opposed to traditional if/else logic.
In traditional if/else logic, you may see something like this:
if (isNull(x)) {
do ...
} else { //x exists
do ...
}
With pattern matching logic, matching we need a similar way to execute certain code if a value is null, or in F# syntax, None
Thus we would have the same code as
match x with
| None -> do ...
| Some x -> do ...