News:

MyKidsDiary.in :: Capture your kids magical moment and create your Online Private Diary for your kids

Main Menu

Generic Type Parameters

Started by thiruvasagamani, Jun 15, 2009, 01:13 PM

Previous topic - Next topic

thiruvasagamani

Generic Type Parameters

Generic type parameters are specified in angle brackets (< and >), in the form of a single quotation mark followed by an identifier. Multiple generic type parameters are separated by commas. The generic type parameter is in scope throughout the declaration. The following code example shows how to specify generic type parameters.
Copy Code

type MyGenericClass<'a> (x: 'a) =
   do printfn "%A" x


Type arguments are inferred when the type is used. In the following code, the inferred type is a sequence of tuples.
Copy Code

let g1 = MyGenericClass( seq { for i in 1 .. 10 -> (i, i*i) } )

Thiruvasakamani Karnan


thiruvasagamani

Mutually Recursive Types

When you define types that reference each other in a circular way, you string together the type definitions by using the and keyword. The and keyword replaces the type keyword on all except the first definition, as follows.
Copy Code

open System.IO

type Folder(path_in: string) as this =
  let path = path_in
  let filenameArray : string array = Directory.GetFiles(path)
  member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray

and File(filename: string, containingFolder: Folder) =
   member this.Name = filename
   member this.ContainingFolder = containingFolder

let folder1 = new Folder(".")
for file in folder1.FileArray do
   printfn "%s" file.Name


The output is a list of all the files in the current directory.
Thiruvasakamani Karnan


thiruvasagamani

When to Use Classes, Unions, Records, and Structures

Given the variety of types to choose from, you need to have a good understanding of what each type is designed for to select the appropriate type for a particular situation. Classes are designed for use in object-oriented programming contexts. Object-oriented programming is the dominant paradigm used in applications that are written for the .NET Framework. If your F# code has to work closely with the .NET Framework or another object-oriented library, and especially if you have to extend from an object-oriented type system such as a UI library, classes are probably appropriate.

If you are not interoperating closely with object-oriented code, or if you are writing code that is self-contained and therefore protected from frequent interaction with object-oriented code, you should consider using records and discriminated unions. A single, well thought–out discriminated union, together with appropriate pattern matching code, can often be used as a simpler alternative to an object hierarchy. For more information about discriminated unions, see Discriminated Unions (F#).

Records have the advantage of being simpler than classes, but records are not appropriate when the demands of a type exceed what can be accomplished with their simplicity. Records are basically simple aggregates of values, without separate constructors that can perform custom actions, without hidden fields, and without inheritance or interface implementations. Although members such as properties and methods can be added to records to make their behavior more complex, the fields stored in a record are still a simple aggregate of values. For more information about records, see Records (F#).

Structures are also useful for small aggregates of data, but they differ from classes and records in that they are .NET value types. Classes and records are .NET reference types. The semantics of value types and reference types are different in that value types are passed by value. This means that they are copied bit for bit when they are passed as a parameter or returned from a function. They are also stored on the stack or, if they are used as a field, embedded inside the parent object instead of stored in their own separate location on the heap. Therefore, structures are appropriate for frequently accessed data when the overhead of accessing the heap is a problem.
Thiruvasakamani Karnan


thiruvasagamani

Structures (F#)

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

A structure is a compact object type that can be more efficient than a class for types that have a small amount of data and simple behavior.

[ attributes ]
type [accessibility-modifier] type-name =
   struct
            type-definition-elements
   end
// or
[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
      type-definition-elements

Remarks

Structures are value types, which means that they are stored directly on the stack or, when they are used as fields, inline in the parent type. Unlike classes and records, structures have pass-by-value semantics. This means that they are useful primarily for small aggregates of data that are accessed and copied frequently.

In the previous syntax, two forms are shown. The first is not the lightweight syntax, but it is nevertheless frequently used because, when you use the struct and end keywords, you can omit the StructAttribute attribute, which appears in the second form. You can abbreviate StructAttribute to just Struct.

The type-definition-elements in the previous syntax represents member declarations and definitions. Structures can have constructors and mutable and immutable fields, and they can declare members and interface implementations. For more information, see Members (F#).

Structures cannot participate in inheritance, cannot contain let or do bindings, and cannot recursively contain fields of their own type (although they can contain reference cells that reference their own type).

Because structures do not allow let bindings, you must declare fields in structures by using the val keyword. The val keyword defines a field and its type but does not allow initialization. Instead, val declarations are initialized to zero or null. For this reason, structures that have an implicit constructor (that is, parameters that are given immediately after the structure name in the declaration) require that val declarations be annotated with the DefaultValue attribute. Structures that have a defined constructor still support zero-initialization. Therefore, the DefaultValue attribute is a declaration that such a zero value is valid for the field. Implicit constructors for structures do not perform any actions because let and do bindings are allowed on the type, but the implicit constructor parameter values passed in are available as private fields.

Explicit constructors might involve initialization of field values. When you have a structure that has an explicit constructor, it still supports zero-initialization; however, you do not use the DefaultValue attribute on the val declarations because it conflicts with the explicit constructor. For more information about val declarations, see Explicit Fields: The val Keyword (F#).

Attributes and accessibility modifiers are allowed on structures, and follow the same rules as those for other types. For more information, see Attributes (F#) and Access Control (F#).

The following code examples illustrate structure definitions.
Copy Code

// In Point3D, three immutable values are defined.
// x, y, and z will be initialized to 0.0.
type Point3D =
   struct
      val x: float
      val y: float
      val z: float
   end

// In Point2D, two immutable values are defined.
// Point2D has an explicit constructor.
// You can create zero-initialized instances of Point2D, or you can
// pass in arguments to initialize the values.
type Point2D =
   struct
      val X: float
      val Y: float
      new(x: float, y: float) = { X = x; Y = y }
   end


Thiruvasakamani Karnan


thiruvasagamani

Inheritance (F#)


Inheritance is used to model the "is-a" relationship, or subtyping, in object-oriented programming.
Specifying Inheritance Relationships

You specify inheritance relationships by using the inherit keyword in a class declaration. The basic syntactical form is shown in the following example.
Copy Code

type MyDerived(...) =
   inherit MyBase(...)

A class can have at most one direct base class. If you do not specify a base class by using the inherit keyword, the class implicitly inherits from Object.
Inherited Members

If a class inherits from another class, the methods and members of the base class are available to users of the derived class as if they were direct members of the derived class.

Any let bindings and constructor parameters are private to a class and, therefore, cannot be accessed from derived classes.

The keyword base is available in derived classes and refers to the base class instance. It is used like the self-identifier.

Source : MSDN
Thiruvasakamani Karnan


thiruvasagamani

Virtual Methods and Overrides

Virtual methods (and properties) work somewhat differently in F# as compared to other .NET languages. To declare a new virtual member, you use the abstract keyword. You do this regardless of whether you provide a default implementation for that method. Thus a complete definition of a virtual method in a base class follows this pattern:

abstract member method-name : type

default self-identifier.method-name argument-list = method-body

And in a derived class, an override of this virtual method follows this pattern:

override self-identifier.method-name argument-list = method-body

If you omit the default implementation in the base class, the base class becomes an abstract class.

The following code example illustrates the declaration of a new virtual method function1 in a base class and how to override it in a derived class.
Copy Code

type MyClassBase() =
   let mutable z = 0
   abstract member function1 : int -> int
   default u.function1(a : int) = z <- z + a; z

type MyClassDerived() =
   inherit MyClassBase()
   override u.function1(a: int) = a + 1


Thiruvasakamani Karnan


thiruvasagamani

Constructors and Inheritance

The constructor for the base class must be called in the derived class. The arguments for the base class constructor appear in the argument list in the inherit clause. The values that are used must be determined from the arguments supplied to the derived class constructor.

The following code shows a base class and a derived class, where the derived class calls the base class constructor in the inherit clause:
Copy Code

type MyClassBase(x: int) =
   let mutable z = x * x
   do for i in 1..z do printf "%d " i
   

type MyClassDerived(y: int) =
   inherit MyClassBase(y * 2)
   do for i in 1..y do printf "%d " i



Source : MSDN
Thiruvasakamani Karnan


thiruvasagamani

Alternatives to Inheritance

In cases where a minor modification of a type is required, consider using an object expression as an alternative to inheritance. The following example illustrates the use of an object expression as an alternative to creating a new derived type:
Copy Code

open System

let object1 = { new Object() with
      override this.ToString() = "This overrides object.ToString()"
      }

printfn "%s" (object1.ToString())


For more information about object expressions
When you are creating object hierarchies, consider using a discriminated union instead of inheritance. Discriminated unions can also model varied behavior of different of objects that share a common overall type. A single discriminated union can often eliminate the need for a number of derived classes that are minor variations of each other.
Thiruvasakamani Karnan


thiruvasagamani

Interfaces (F#)

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

Interfaces specify sets of related members that other classes implement.

// Interface declaration:
[ attributes ]
type interface-name =
   [ interface ]
        abstract member1 : [ argument-types1 -> ] return-type1
        abstract member2 : [ argument-types2 -> ] return-type2
        ...
   [ end ]

// Implementing, inside a class type definition:
interface interface-name with
   member self-identifier.member1 argument-list = method-body1
   member self-identifier.member2 argument-list = method-body2

// Implementing, by using an object expression:
[ attributes ]
type class-name (argument-list) =
   { new interface-name with
       member self-identifier.member1 argument-list = method-body1
       member self-identifier.member2 argument-list = method-body2
       [ base-interface-definitions ]
   }
      member-list


Remarks

Interface declarations resemble class declarations except that no members are implemented. Instead, all the members are abstract, as indicated by the keyword abstract. You do not provide a method body for abstract methods. However, you can provide a default implementation by also including a separate definition of the member as a method together with the default keyword. Doing so is equivalent to creating a virtual method in a base class in other .NET languages. Such a virtual method can be overridden in classes that implement the interface.

There are two ways to implement interfaces: by using object expressions, and by using class types. In either case, the class type or object expression provides method bodies for abstract methods of the interface. Implementations are specific to each type that implements the interface. Therefore, interface methods on different types might be different from each other.

The keywords interface and end, which mark the start and end of the definition, are optional when you use lightweight syntax. If you do not use these keywords, the compiler attempts to infer whether the type is a class or an interface by analyzing the constructs that you use. If you define a member or use other class syntax, the type is interpreted as a class.
Thiruvasakamani Karnan


thiruvasagamani

Implementing Interfaces by Using Class Types

You can implement an interface in a class type by using the interface keyword, the name of the interface, and the with keyword, followed by the interface member definitions, as shown in the following code.
Copy Code

type IPrintable =
   abstract member Print : unit -> unit

type SomeClass(x: int, y: float) =
   interface IPrintable with
      member this.Print() = printfn "%d %f" x y

Source : MSDN
Thiruvasakamani Karnan