The typical way to create asignature file is to generate it from the module source and then go through and erase any val-ues and functions that you want to be private.. InVisual Studio,
Trang 1module GColl = System.Collections.Generic
let l = new GColl.List<int>()
Signature Files
Signature files are a way of making function and value definitions private to a module You’ve
already seen the syntax for the definition of a signature file in Chapter 2 It is the source thatthe compiler generates when using the –i switch Any definitions that appear in a signaturefile are public and can be accessed by anyone using the module Any that are not in the signa-ture file are private and can be used only inside the module itself The typical way to create asignature file is to generate it from the module source and then go through and erase any val-ues and functions that you want to be private
The signature file name must be the same as the name of the module with which it ispaired It must have the extension fsi or mli You must specify the signature file to the com-piler On the command line, you must give it directly before the source file for its module InVisual Studio, the signature file must appear before the source file in Solution Explorer.For example, if you have the following in the file Lib.fs:
val funkyFunction : string -> string
and would use the command line like this:
in this section and execution order in the next
Values and types within a module cannot be seen from another module unless the ule they’re in appears on the command line before the module that refers to them This isprobably easier to understand with an example Suppose you have a source file, ModuleOne.fs,containing the following:
mod-C H A P T E R 6 n O R G A N I Z I N G, A N N OTAT I N G, A N D Q U OT I N G C O D E
116
7575Ch06.qxp 4/27/07 1:11 PM Page 116
Trang 2module ModuleOne
let text = "some text"
and another module, ModuleTwo.fs, containing the following:
#light
module ModuleTwo
print_endline ModuleOne.text
These two modules can be compiled successfully with the following:
fsc ModuleOne.fs ModuleTwo.fs -o ModuleScope.exe
But the following command:
fsc ModuleTwo.fs ModuleOne.fs -o ModuleScope.exe
would result in this error message:
ModuleTwo.fs(3,17): error: FS0039: The namespace or module 'ModuleOne' is not
Roughly speaking, execution in F# starts at the top of a module and works its way down to
the bottom Any values that are functions are calculated, and any top-level statements are
executed So, the following:
module ModuleOne
print_endline "This is the first line"
print_endline "This is the second"
let file =
let temp = new System.IO.FileInfo("test.txt") inPrintf.printf "File exists: %b\r\n" temp.Exists;
tempwill give the following result:
Trang 3This is the first line
This is the second
File exists: false
This is all pretty much as you might expect When a source file is compiled into an bly, none of the code in it will execute until a value from it is used by a currently executingfunction Then, when the first value in the file is touched, all the let expressions and top-levelstatements in the module will execute in their lexical order When a program is split over morethan one module, the last module passed to the compiler is special All the items in this mod-ule will execute, and the other items will behave as they were in an assembly Items in othermodules will execute only when a value from that module is used by the module currentlyexecuting Suppose you create a program with two modules
print_endline "This is the first line"
print_endline "This is the second"
If this is compiled with the following:
fsc ModuleOne.fs ModuleTwo.fs -o ModuleExecution.exe
this will give the following result:
This is the first line
This is the second
This might not be what you expected, but it is important to remember that since ModuleOnewas not the last module passed to the compiler, nothing in it will execute until a value from it isused by a function currently executing In this case, no value from ModuleOne is ever used, so itnever executes Taking this into account, you can fix your program so it behaves more as youexpect
Trang 4Here is ModuleTwo.fs:
module ModuleTwo
print_endline "This is the first line"
print_endline "This is the second"
let funct() =
Printf.printf "%i" ModuleOne.n
funct()
If this is compiled with the following:
fsc ModuleOne.fs ModuleTwo.fs -o ModuleExecution.exe
it will give the following result:
This is the first line
This is the second
This is the third and final
1
However, using this sort of trick to get the results you want is not recommended It is erally best only to use statements at the top level in the last module passed to the compiler In
gen-fact, the typical form of an F# program is to have one statement at the top level at the bottom
of the last module that is passed to the compiler
Optional Compilation
Optional compilation is a technique where the compiler can ignore various bits of text from a
source file Most programming languages support some kind of optional compilation It can
be handy, for example, if you want to build a library that supports both NET 1.1 and 2.0 and
want to include extra values and types that take advantage of the new features of version 2.0
However, you should use the technique sparingly and with great caution, because it can
quickly make code difficult to understand and maintain
In F# optional compilation is supported by the compiler switch define FLAG and thecommand #if FLAG in a source file
The following example shows how to define two different versions of a function, one forwhen the code is compiled for NET 2.0 and the other for all other versions of NET:
Trang 5let getArray() = CompatArray.of_array [|1; 2; 3|]
#endif
This example assumes that the compiler switch define FRAMEWORK_AT_LEAST_2_0 isdefined when the code is being compiled for NET 2.0 Chapter 13 gives more on the differ-ences between NET 1.0, 1.1, and 2.0 and covers compiler options
Comments
F# provides two kinds of comments Multiline comments start with a left parenthesis and an
asterisk and end with an asterisk and a right parenthesis For example:
(* this is a comment *)
or
(* this
is acomment
Doc comments allow comments to be extracted from the source file in the form of XML or HTML.
This is useful because it allows programmers to browse code comments without having to browsethe source This is convenient for the vendors of APIs because it allows them to provide documen-tation about the code without having to provide the source itself, and it is just more convenient to
be able to browse the docs without having to open the source In addition, the documentation isstored alongside the source where it has more chance of being updated when code changes.Doc comments start with three slashes instead of two They can be associated only withtop-level values or type definitions and are associated with the value or type they appearimmediately before The following code associates the comment this is an explanation withthe value myString:
#light
/// this is an explanation
let myString = "this is a string"
To extract doc comments into an XML file, you use the –doc compiler switch If this ple were saved in a source file, prog.fs, the following command:
exam-fsc -doc doc.xml Prog.fs
C H A P T E R 6 n O R G A N I Z I N G, A N N OTAT I N G, A N D Q U OT I N G C O D E
120
7575Ch06.qxp 4/27/07 1:11 PM Page 120
Trang 6would produce the following XML:
You can then process the XML using various tools, such as NDoc (ndoc.sourceforge.net),
to transform it into a number of more readable formats The compiler also supports the direct
generation of HTML from doc comments Although this is less flexible than XML, it can
pro-duce usable documentation with less effort It can also propro-duce better results, under some
circumstances, because notations such as generics and union types are not always well
sup-ported by documentation generation tools I cover the compiler switches that generate HTML
in “Useful Command-Line Switches” in Chapter 12
In F# there is no need to explicitly add any XML tags; for example, the <summary> and
</summary> tags were added automatically I find this useful because it saves a lot of typing and
avoids wasted space in the source file; however, you can take control and write out the XML
tags themselves if you want The following is a doc comment where the tags have been
explic-itly written out:
Trang 7be included in the resulting XML or HTML if the compiler is given a signature file for it.
Custom Attributes
Custom attributes add information to your code that will be compiled into an assembly andstored alongside your values and types This information can then be read programmaticallyvia reflection or by the runtime itself
Attributes can be associated with types, members of types, and top-level values They canalso be associated with do statements An attribute is specified in brackets, with the attributename in angle brackets For example:
C H A P T E R 6 n O R G A N I Z I N G, A N N OTAT I N G, A N D Q U OT I N G C O D E
122
7575Ch06.qxp 4/27/07 1:11 PM Page 122
Trang 8So far, we’ve used attributes only with values, but using them with type or type members
is just as straightforward The following example marks a type and all its members as obsolete:
because the libraries that provide the graphical components use unmanaged (not compiled
by the CLR) code under the covers The easiest way to do this is by using the STAThread
attrib-ute This must modify the first do statement in the last file passed to the compiler, that is, the
first statement that will execute when the program runs For example:
Trang 9n Note The dokeyword is usually required only when not using the #lightmode; however, it is alsorequired when applying an attribute to a group of statements.
Once attributes have been added to types and values, it’s possible to use reflection to find which values and types are marked with which attributes This is usually done with theIsDefined or GetCustomAttributes methods of the System.Reflection.MemberInfo class,meaning they are available on most objects used for reflection including System.Type Thenext example shows how to look for all types that are marked with the Obsolete attribute:
m.IsDefined((type System.ObsoleteAttribute), true))
Now that you’ve seen how you can use attributes and reflection to examine code, let’s look
at a similar but more powerful technique for analyzing compiled code, called quotation.
Quoted Code
Quotations are a way of telling the compiler, “Don’t generate code for this section of the source
file; turn it into a data structure, an expression tree, instead.” This expression tree can then be
interpreted in a number of ways, transformed or optimized, compiled into another language,
or even just ignored
Quotations come in two types, raw and typed, the difference being that typed quotationscarry static type information whereas raw quotations do not Both carry runtime type annota-tions Typed quotations are designed for use in client programs, so usually you will want to use
C H A P T E R 6 n O R G A N I Z I N G, A N N OTAT I N G, A N D Q U OT I N G C O D E
124
7575Ch06.qxp 4/27/07 1:11 PM Page 124
Trang 10typed quotations These are the only quotations covered in this section Raw quotations are
designed for implementing libraries based on quotations; these will generally be
automati-cally typed quotations before they are consumed
To quote an expression, place it between guillemets (also called French quotes), « » To
ensure the compiler recognizes these characters, you must save your file as UTF-8 Visual
Studio users can do this with File äAdvanced Save If you have some objection to using
UTF-8, you can use an ASCII alternative: <@ @> Both ways of quoting an expression are
essentially just an operator defined in a module, so you need to open the module Microsoft
FSharp.Quotations.Typed to be able to use them The next example uses a quotation and
prints it:
#light
open Microsoft.FSharp.Quotations.Typed
let quotedInt = « 1 »
printf "%A\r\n" quotedInt
The result is as follows:
printf "%A\r\n" asciiQuotedInt
The result is as follows:
printf "%A\r\n" quotedId
The result is as follows:
<@ Prog.n @>
Trang 11Next we’ll quote a function applied to a value Notice that since we are quoting two items,the result of this quotation is split into two parts The first part represents the function, andthe second represents the value to which it is applied.
#light
open Microsoft.FSharp.Quotations.Typed
let inc x = x + 1
let quotedFun = « inc 1 »
printf "%A\r\n" quotedFun
The result is as follows:
printf "%A\r\n" quotedOp
The result is as follows:
<@ Microsoft.FSharp.Operators.op_Addition (Int32 1) (Int32 1) @>
The next example quotes an anonymous function:
#light
open Microsoft.FSharp.Quotations.Typed
let quotedAnonFun = « fun x -> x + 1 »
printf "%A\r\n" quotedAnonFun
The result is as follows:
C H A P T E R 6 n O R G A N I Z I N G, A N N OTAT I N G, A N D Q U OT I N G C O D E
126
7575Ch06.qxp 4/27/07 1:11 PM Page 126
Trang 12function, interpretInt, that queries the expression passed to it to see whether it is an integer.
If it is, it prints the value of that integer; otherwise, it prints the string "not an int"
#light
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Typed
let interpretInt exp =
let uexp = to_raw exp inmatch uexp with
In this chapter, you saw how to organize code in F# You also saw how to comment, annotate,
and quote code, but you just scratched the surface of both annotation and quoting
This concludes the tour of the F# core language The rest of the book will focus on how touse F#, from working with relational databases to creating user interfaces, after you look at the
F# core libraries in the next chapter
Trang 14The F# Libraries
Although F# can use all the classes available in the NET BCL, it also ships with its own set of
libraries
The F# libraries are split into two, FSLib.dll, which is also referred to as the native F# library
or just FSLib, and MLLib.dll, which is sometimes referred to as the ML compatibility library or
MLLib FSLib contains everything that the F# compiler really needs to work; for example, it
con-tains the Tuple class that is used when you use a tuple MLLib is a set of libraries for doing
common programming tasks partially based on the libraries that ship with OCaml
The objective of this chapter is not to completely document every nuance of every F#
library type and function It is to give you an overview of what the modules can do, with a
par-ticular focus on features that aren’t readily available in the BCL The F# online documentation
(http://research.microsoft.com/fsharp/manual/namespaces.html) is the place to find
detailed documentation about each function
There is some crossover between the functionality provided by the F# libraries and the.NET BCL Programmers often ask when should they use functions from the F# library and
when should they use the classes and methods available in the NET BCL Both have their
advantages and disadvantages, and a good rule of thumb is to prefer what is in FSLib over the
classes available in the NET Framework BCL and prefer what is available in the NET
Frame-work BCL over MLLib
Libraries Overview
The following sections list all the modules that are contained in FSLib and MLLib; the
mod-ules covered in this chapter are highlighted in bold
129
C H A P T E R 7
n n n
Trang 15The Native F# Library FSLib.dll
These are the modules in FSLib:
• Microsoft.FSharp
• Idioms
• Reflection
• Microsoft.FSharp.Collections
• ComparisonIdentity • HashIdentity • IEnumerable
• LanguagePrimitives • Operators • OptimizedClosures
• UInt8
• Microsoft.FSharp.Math
C H A P T E R 7 n T H E F # L I B R A R I E S
130
7575Ch07.qxp 4/27/07 1:03 PM Page 130
Trang 16The ML Compatibility Library MLLib.dll
These are the modules in MLLib: