Reputation: 11120
Is there any difference between List<int>
and int list
?
For example, when I write a function
let somefn a : int list = a
and
let somefn2 a : List<int> = a
Returned value types differ with notation style, and even in the console output, when I invoke those functions, it shows two distinctly noted types
val it : int list = ...
and
val it : List<int> = ...
Although, logic and idea seems to be same, Interpreter/compiler interprets those two types in a different way.
Is there any difference?
Upvotes: 18
Views: 2906
Reputation: 36688
To expand slightly on John Palmer's correct answer, here's an F# Interactive session that illustrates how int list
and List<int>
are synonyms, right up until the moment when they aren't. And note how there's also list<int>
and ResizeArray<int>
for extra confusion:
F# Interactive for F# 4.0 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License
For help type #help;;
> typeof<list<int>>.Namespace ;;
val it : string = "Microsoft.FSharp.Collections"
> typeof<list<int>>.Name ;;
val it : string = "FSharpList`1"
> typeof<List<int>>.Namespace ;;
val it : string = "Microsoft.FSharp.Collections"
> typeof<List<int>>.Name ;;
val it : string = "FSharpList`1"
> typeof<int list>.Namespace ;;
val it : string = "Microsoft.FSharp.Collections"
> typeof<int list>.Name ;;
val it : string = "FSharpList`1"
> typeof<ResizeArray<int>>.Namespace ;;
val it : string = "System.Collections.Generic"
> typeof<ResizeArray<int>>.Name ;;
val it : string = "List`1"
-
- printfn "Now we'll open System.Collections.Generic. Watch what happens."
- ;;
Now we'll open System.Collections.Generic. Watch what happens.
val it : unit = ()
> open System.Collections.Generic ;;
> typeof<list<int>>.Namespace ;;
val it : string = "Microsoft.FSharp.Collections"
> typeof<list<int>>.Name ;;
val it : string = "FSharpList`1"
> typeof<List<int>>.Namespace ;;
val it : string = "System.Collections.Generic"
> typeof<List<int>>.Name ;;
val it : string = "List`1"
> typeof<int list>.Namespace ;;
val it : string = "Microsoft.FSharp.Collections"
> typeof<int list>.Name ;;
val it : string = "FSharpList`1"
> typeof<ResizeArray<int>>.Namespace ;;
val it : string = "System.Collections.Generic"
> typeof<ResizeArray<int>>.Name ;;
val it : string = "List`1"
So List<int>
with an uppercase L will be the F# list type (an immutable linked list with a head pointer, which has O(1) head access and prepending, but O(N) tail access and appending) if you haven't opened the System.Collections.Generic
namespace. But if you have, then suddenly List<int>
resolves to the .Net System.Collections.Generic.List<T>
class, which is a mutable data structure with O(1) lookups anywhere, amortized O(1) appends, but O(N) prepends. So it really matters which one you're using.
To be on the safe side, if you intend to use the F# list structure, I'd write it as either int list
(my preference since it reads like English) or list<int>
(which some people prefer since it reads like C#). Neither of those will suddenly acquire a different meaning when you open a .Net namespace; they will continue to refer to the F# list structure. And avoid using List<int>
to refer to F# lists; only use that when you have opened the System.Collections.Generic
namespace and you intend to get a .Net System.Collections.Generic.List<T>
instance. Finally, note that in F#, System.Collections.Generic.List<T>
has a type alias available without opening the System.Collections.Generic
namespace. By default, without needing to open any namespaces, you can access this type under the name ResizeArray<T>
.
Summary:
Safe type names:
int list
and list<int>
(always refer to the F# singly-linked list type)ResizeArray<int>
(always refers to the C# System.Collections.Generic.List<T>
type)Unsafe type names because their meaning changes depending on what namespace you've opened:
List<int>
(refers to the F# singly-linked list type at first, but changes meaning to the C# type if you open the System.Collections.Generic
namespace). As a general rule, don't use this type name; if you want this type in F#, use ResizeArray<int>
instead.Upvotes: 25
Reputation: 6223
Generally, for generic types with one parameter, TypeParameter GenericType
is equivalent to GenericType<TypeParameter>
.
The F# immutable list type declared in FSharp.Core is in scope with two names by default: List
and list
. It has one type parameter, so int list
, list<int>
, int List
, and List<int>
are equivalent.
System.Collections.Generic
has a different List<'T>
type, which has the more suitable alias ResizeArray
in F#. If you open System.Collections.Generic
, List
refers to this mutable, array-wrapping type.
To avoid confusion, it is customary to not use List
with a capital L
to refer to a type in F#. Instead, list
with a lowercase l
is used for the FP-originating, immutable, singly-linked lists, while ResizeArray
is used to refer to the OO-style mutable collection. Most people also stick to the different styles int list
versus ResizeArray<int>
and avoid the other combinations.
As for type information, IDEs and similar sometimes attempt to display types in the way they were declared, as there are often many ways to refer to the same type. This does not imply a difference in behavior.
Upvotes: 4
Reputation: 25516
There is no difference, there is a type alias.
Except under one case, where you have run
open System.Collections.Generic
Which has its own list type
Upvotes: 8