Introduction to F# · David Raab

Introduction to F#

When I remember the first time I looked at functional(-first) languages like F#, ML, Haskell and others. The typical reaction that I had, and I always see from other people is: This is unreadable, it must be hard to read, it feels complicated and hard.

After spending some time in F# I cannot agree to that at all anymore. Often the syntax itself is easier (for example compared to C#), shorter and in my opinion more readable. The problem is more over that most functional languages shares a syntax that is completely different compared to languages like C, C++, C#, Java, JavaScript and other more mainstream languages. The problem is more that it is just unfamiliar.

In this post I want you to give a quick overview over the most common and important concepts. With this overview it should be easy to understand the most basic part to read and understand functional code.

For better understanding I will provide some C# to F# code examples.

Variables

Definition C#

Variables are an important concept, in C# you can define variables in two ways. First with an explicit type. You can optionally initialize it with a value.

1: 
2: 
3: 
int    num  = 33;
string name = "Hello";
Person person;

The second way is to use the var keyword. It uses type-inference to automatically determine the type of a variable. You are also forced to specify a value in this way.

1: 
2: 
3: 
var num    = 33;
var name   = "Hello";
var person = new Person("foo");

Definition in F#

In F# we usually only use the second form of definition. But instead of var we write let.

1: 
2: 
3: 
let num    = 33
let name   = "Hello"
let person = Person("foo")

We already can see some important differences.

  1. Semicolons are not needed to end/separate commands.
  2. We don't have to specify new to create an object.

We still can add type-annotations if we want.

1: 
2: 
3: 
let num    : int    = 33
let name   : string = "Hello"
let person : Person = Person("foo")

(Im)mutability

(Im)mutability in C#

One important difference is that every variable in C# is mutable by default. This means you can change a variable at any time you want

1: 
2: 
3: 
num    = 66;
name  += " World!";
person = new Person("bar");

In C# you otherwise only can create immutable class fields with the readonly keyword. You cannot create immutable local-variables.

(Im)mutability in F#

In F# on the other hand, everything is immutable by default. You cannot change a variable by default. If you want to create a mutable variable you have to mark a variable as mutable

1: 
2: 
3: 
let mutable num    = 33
let mutable name   = "Hello"
let mutable person = Person("foo")

You change the content of a variable with <- instead of =. Equal is only used to specify or comparison. There is no operator like += in F#. F# doesn't try to make mutability convenient.

1: 
2: 
3: 
num    <- 66
name   <- name + " World!"
person <- Person("bar")

Functions / Static Methods

Definition in C#

In C# you define static methods as part of a class.

1: 
2: 
3: 
4: 
5: 
public static class MyOperations {
    public static Add(int x, int y) {
        return x + y;
    }
}

Definition in F#

In F# you put functions inside modules.

1: 
2: 
module MyOperations =
    let add x y = x + y

We can see once again some important differences.

  1. You also use let for the definition of a function
  2. Arguments x and y will just be separated by spaces instead of (x, y)
  3. Type-inference also works for functions.
  4. There doesn't exists a return keyword. The last expression is automatically returned as a value.

You also can add explicit type-annotations.

1: 
2: 
module MyOperations =
    let add (x:int) (y:int) : int = x + y

Calling functions in C#

1: 
var result = MyOperations.Add(5, 10);

Calling functions in F#

1: 
let result = MyOperations.add 5 10

The only difference is that you don't use braces and commas to separate the arguments. You just provide the arguments as-is.

Generics

One important concept that you will see more often in F# (compared to C#) is the usage of generics. Because type-inference also works with functions. F# often automatically generalize a function with generic arguments, instead of specific-types. And overall generics are more important in functional languages.

Generics in C#

1: 
2: 
3: 
public static T SomeFunction<T>(T input) {
    // Some code
}

Generics in F#

1: 
let someFunction (input:'a) = // Some code

Generics in F# are just annotated like normal types. The only difference is that all of them start with an apostrophe. Instead of T, TIn, TOut and so on, as often used in C#, in F# they will be just letters 'a, 'b, 'c ...

As stated previously. You also don't need to annotate generics. If you have written a generic function, F# will automatically infer a generic type for you. So overall you also could just write.

1: 
let someFunction input = // Some code

Data-Types

Other than object-oriented languages the key concept of functional programming is a separation between data and behaviour. In OO programming we use classes. Classes can contain public/private fields to save data, and provide methods for working with this data.

In a functional-language instead we usually define our data-types as separate immutable data. We then provide (pure) functions that gets immutable data as input, and generate some new data as output. Because working with data is so important, a functional language offers more than just classes to define data-types. Besides classes we can use tuples, records and discriminated unions.

Tuples in C#

Tuples are also present in C#. There already exists as a Tuple class. But working with them is not so convenient as in F#. Anyway let's quickly look at how you use them.

1: 
2: 
3: 
4: 
var position = Tuple.Create(3, 4, 5); // The type is: Tuple<int,int,int>
var x = position.Item1; // 3
var y = position.Item2; // 4
var z = position.Item3; // 5

Tuples are a good way for intermediate types. If you easily want to pass some values as one unit to a function. But more often they are used as a result. So you can easily return multiple different values from a function.

Tuples in F#

Working with Tuples is much easier in F#

1: 
2: 
let position = 3,4,5  // The type is: int * int * int
let x,y,z    = position

You create Tuples just by separating values with a comma. You can extract a Tuple with a let definition. This way you can easily create a function that return multiple data at once. Tuples don't must contain the same types.

1: 
let someFunction x = x, x*2

This function for example returns a Tuple with two elements. The input itself, and the input multiplied by Two.

1: 
2: 
let result = someFunction 10
let x, y   = result

We also can write it in one line

1: 
let x,y = someFunction 10

Other than C#, tuples have its own type signature. Instead of a Generic Class like Tuple<int,int,int> a tuple is built into the language itself. They will be represented as int * int * int as a type. int * float * string * Person would be a four element tuple (quadruple) that contains an int a float a string and a Person in that exact order.

Records in F#

Working with tuples is good for intermediate function, for example if you create Pipelines like you see them with LINQ in C#. They are also good for grouping two or three elements, but as soon you have have more elements, they are unhandy to work with. An alternative to this is a Record type. If you know JavaScript you can compare them just to an object. Or a hash in Perl. The only difference is that they are static typed. So you must define a type beforehand.

Records are planned as a feature in C# 7.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
type Human = {
    Id:        int
    FirstName: string
    LastName:  string
    Born:      DateTime
}

let me = {
    Id        = 1
    FirstName = "David"
    LastName  = "Rab"
    Born      = DateTime(1983, 02, 19)
}

let age = DateTime.Today.Year - me.Born.Year
printfn "%s %s is currently %d years old" me.FirstName me.LastName age

let newMe = {me with LastName = "Raab"}
printfn "%s %s is currently %d years old" me.FirstName    me.LastName    age
printfn "%s %s is currently %d years old" newMe.FirstName newMe.LastName age

This code will produce the output:

1: 
2: 
3: 
David Rab is currently 33 years old
David Rab is currently 33 years old
David Raab is currently 33 years old

Defining a Record needs explicit type annotations. Creating a Record is pretty easy. You just use the { ... } syntax. This is nearly identical to JavaScript. As functional-languages prefer immutability a Record type itself is also immutable by default. It also has default equality and comparison implementations.

There exists a special copy and update operation. It is {record with newField = newValue }. You also can set multiple fields at once. As seen in the example. This creates a new record and doesn't modify the old record.

You can access member of a record with a dot. Records also can be deeply nested, so you can create hierarchical data-structures.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
type Attributes = {
    Strength:     int
    Dexterity:    int
    Intelligence: int
    Vitality:     int
}

type CaharacterSheet = {
    Name:      string
    Attribute: Attributes
}

let warrior = {
    Name = "Conan"
    Attribute =
    {
        Strength = 1000
        Dexterity = 200
        Intelligence = 3
        Vitality = 1000
    }
}

printfn "%s was a Warrior with Strength of %d" warrior.Name warrior.Attribute.Strength

Discriminated Unions in F#

A Discriminated Union (DU) also doesn't currently exists in C#, but they are also planed as a feature for C# 7. A DU is important as they provide a OR type. When you look at classes, tuples or records all of them are basically AND types. All of those types group some data-together, but you always have all of them at the same time. But what happens if you want to express some kind of Either A or B? The closest thing you can get are enums in C#, but enums cannot contain additional values for each case.

DU are important, because if a language supports both kinds, we also say that it has an Algebraic type-system. Let's assume we have a shopping system, and we want to express that a user can pay with different methods.

  1. Cash -- No additional data needed
  2. PayPal -- We need the email address
  3. CreditCard -- We need the credit card number
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
type Payment =
    | Cash
    | PayPal     of string
    | CreditCard of string

let inform payment =
    match payment with
    | Cash          -> printfn "User payed cash"
    | PayPal email  -> printfn "User payed with PayPal. Email: %s" email
    | CreditCard no -> printfn "User payed with CC. No: %s" no

inform Cash
inform (PayPal "foo@example.com")
inform (CreditCard "123456789")

The above code will produce an output like

1: 
2: 
3: 
User payed cash
User payed with PayPal. Email: foo@example.com
User payed with CC. No: 123456789

Here inform is a function with one argument payment. Still note that we don't need any kind of type-annotation. We use pattern matching on payment. Just the fact that we use Cash, PayPal and CreditCard the F# Compiler can automatically infer that the argument has to be of type Payment.

Pattern matching is a kind of switch statement but more powerful, because it not only matches on the different cases, you also can extract the additional values that are carried within each case.

Also note the syntax inform (PayPal "foo@example.com") We need the braces here not for invocations. We need them for grouping. This is probably one source of confusion for people coming from C-style languages. If we wouldn't use the braces and write something like inform PayPal "foo@example.com" we would try to invoke the inform function with two arguments. The first argument would be PayPal and the second argument would be "foo@exmaple.com". That would fail because inform is not a two argument function. We first need to create a value. That is just done with PayPal "foo@example.com" and we want the result to pass to our function. That is why we need to add braces around our call.

This is comparable to just simple maths. 3 + 4 * 5 would yield in 23. If we otherwise write (3 + 4) * 5 we would get 35. Braces are just grouping constructs! This becomes more important if we have something like these.

1: 
someFunction (Foo x) (Bar z)

This would be a Function call with two arguments. The first argument is the result of Foo x, the second argument would be the Result of Bar z. Coming from a C-style language people often try to read it as

1: 
someFunction(Foo x)

as a single function invocation with one argument, and they see a trailing (Bar z) and they don't know what it stands for. Actually converting such a function call to C# would result in something like this

1: 
someFunction(new Foo(x), new Bar(z));

The big advantage of Discriminated Unions is that each case can contain objects, tuples, records or other discriminated unions as values. It even can contain itself as an element. In this way you can easily build recursive data-structures.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
type Markdown =
    | NewLine
    | Literal    of string
    | Bold       of string
    | InlineCode of string
    | Block      of Markdown list

let document =
    Block [
        Literal "Hello"; Bold "World!"; NewLine
        Literal "InlineCode of"; InlineCode "let sum x y = x + y"; NewLine
        Block [
            Literal "This is the end"
        ]
    ]

let rec produceHtml markdown (sb:StringBuilder) =
    match markdown with
    | NewLine         -> sb.Append("<br/>") |> ignore
    | Literal    str  -> sb.Append(str) |> ignore
    | Bold       str  -> sb.AppendFormat("<strong>{0}</strong>", str) |> ignore
    | InlineCode code -> sb.AppendFormat("<code>{0}</code>", code) |> ignore
    | Block  markdown ->
        sb.Append("<p>") |> ignore
        for x in markdown do
            produceHtml x sb |> ignore
        sb.Append("</p>") |> ignore

let html = StringBuilder()
produceHtml document html

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

Running the above code will produce us the following output

1: 
<p>Hello<strong>World!</strong><br/>InlineCode of<code>let sum x y = x + y</code><br/><p>This is the end</p></p>

So we can easily create hierarchical data-structures, and with Pattern Matching we can easily write recursive function to traverse them.

List in F#

The example above already introduced lists. Otherwise a list in F# is different to the C# List<T> type. In C# you create a mutable List<T> object and you can directly Add items to. In F# on the other hand you create lists just with the syntax [ ... ] (Like in JavaScript). Otherwise elements get separated by ; instead of ,. This is often a source of confusion, because both styles are allowed but they mean something different.

1: 
let data = [1;2;3;4]

This is a List of int. And it contains four elements.

1: 
let data = [1,2,3,4]

This is a List of a Tuple int * int * int * int and it contains a single element. Remember , is for creating Tuples!

Additional lists in F# are also immutable. They also provide default implementations of equality, comparison and so on. If you want to add elements to a list you have to create a new list. This can be easily done with ::.

1: 
2: 
let data    = [1;2;3;4]
let oneMore = 5 :: data

oneMore is now

1: 
[5;1;2;3;4]

note that data is unchanged and is still a four element list. The way how lists are build (immutable and as linked-list) means adding and removing from the beginning is an efficient operation O(1).

There are various functions inside the List module to transform lists itself. With [|1;2;3|] we also can create mutable fixed-size array. There also exists a Array Module with nearly the same functions as in the List module.

Composition and Piping

The last concepts we look at in our introduction is the concept of Composition and Piping. Both are very important in functional languages, as more complex logic is achieved by composing of functions. Compose ability is actually pretty easy. Let's assume we have a function that takes an int as its input and a string as its output. In C# we would usually define such a method interface in that way.

1: 
string SomeMethod(int x);

This could be for example part of an interface definition in C#. In F# we would define such an interface just as

1: 
int -> string

This definition means. A function that has an int as an input, and will return a string. Note that we don't specify a function name. Every function itself is actually an interface of its own. Something like this also exists in C#. Usually expressed as either Action or Func. We also could have written.

1: 
Func<int,string>

In C# Action and Func types are usually used in Methods if we want to expect a function as an argument. In C# you need Action to describe function with a void return value.

1: 
2: 
Func<int,string>
string SomeFunction(int x)

And Action means

1: 
2: 
Action<int,string>
void SomeFunction(int x, string y)

In F# we just have a sepcial type named unit to express Nothing. So we can write

1: 
2: 
3: 
int -> string
int -> unit
int -> string -> unit

The Last line can be read as. A function with two arguments int and string and it will return unit (Nothing).

Now let's assume we have two functions with the following signatures

1: 
2: 
string   -> int list
int list -> int

So we have a function that has a string as it's input, and a int list (List of int) as its output. Our second functions takes a int list as its input, and will produce just a int as its output. Looking at those signatures we now can compose them. Even if we don't now what those functions do. We just know that the output of the first function can be directly given as the input of the second function.

We can directly create a function with a string input returning an int. This kind of idea is what we name composing. In F# we have a special operator for this kind of composition. The >> operator.

But let's work step by step to it. Let's assume we have a parseInts function that takes a string, splits a string on ',' and parses every number as an int and returns int list. The signature would be string -> int list.

We then have another function sumList that just takes a int list and sums all numbers together returning an int. We could use those two functions like this:

1: 
2: 
let nums = parseInts "1,2,3,4,5"
let sum  = sumList nums // 15

We also could create a new function that combines these two steps into a new function

1: 
2: 
3: 
4: 
let strToSum stringList =
    let nums = parseInts stringList
    let sum  = sumList nums
    sum

We now have a function strToSum that goes directly from string -> int

But these kind of operation is actually pretty generic. As this kind of composing works for any kind of function with any kind of type. In general we can say. When we have two functions.

1: 
2: 
'a -> 'b
'b -> 'c

we can compose those two into a new function

1: 
'a -> 'c

So let's write a compose function that does that kind of stuff for us.

1: 
let compose f g x = g (f x)

So let's look at the implementation. We have a function compose with three arguments. f is expected to be a function. The same is true for g. x is just some kind of value. What we first do is

1: 
(f x)

meaning we will call our f function with the x value. The Result of that is passed to the g function. The result of the g function is then returned as a value. We also could have written it like this.

1: 
2: 
3: 
4: 
let compose f g x =
    let y = f x
    let z = g y
    z

The F# compiler automatically infers that f and g are functions. Just by using it like f x or g y the compiler knows that f and g must be functions with a single argument.

But what kind of types do we have here? The answer is, they are generic. When we look at the type signature that the compiler created for us, it looks some kind of scary first. We have.

1: 
val compose : f:('a -> 'b) -> g:('b -> 'c) -> x:'a -> 'c

Let's go over it step-by-step

Argument

Signature

Meaning

f

('a -> 'b)

A function that goes from 'a to 'b

g

('b -> 'c)

A function that goes from 'b to 'c

x

'a

A value of type 'a

'c

It will return 'c

Just by looking at the types we can examine what the function does. We have 'a as a value and two functions. And we need to return a 'c. So how do we get a 'c?

At first the only thing that function can do is pass the x value (a 'a) into the f function. That will return a 'b value. After we have a 'b value it only can pass that value into the g function. Finally this returns a 'c that the compose function then returns.

We now could use compose like this.

1: 
let sum = compose parseInts sumList "1,2,3,4,5" // 15

Here we now call compose with three arguments. We provide the parseInts function itself as a value. We then provide the sumList function as a the second argument. And our third argument is our "1,2,3,4,5" string.

The last thing we can do now. F# supports omitting arguments from a function call. If you omit a value, you get a function back with the remaining arguments. Currently our compose function is a three arguments function. So what happens if we just provide the first two functions as arguments? We get a function back that is still waiting for the last third argument.

1: 
2: 
let strToSum = compose parseInts sumList
let result   = strToSum "1,2,3,4,5" // 15

This kind of composing is so common that we have a special operator >> for this. So all we really need to do is put >> between two functions, and we get a new function back! So what we are doing is

1: 
(string -> int list) >> (int list -> int)

and we just get a string -> int back.

1: 
2: 
let strToSum = parseInts >> sumList
let result   = strToSum "1,2,3,4,5"

So we can easily create new functions out of smaller functions. This is the essence of functional programming. We have immutable data-types that gets transformed from one type to another. And we compose functions together to create new functions. Note that we also could create such a compose function in C#. But because of a lack of some features in C#, such a function is less practical as it seems.

1: 
2: 
3: 
public static Func<A, C> Compose<A, B, C>(Func<A, B> f, Func<B, C> g) {
    return input => g(f(input));
}

But it is can help to understand what composition means.

The remaining part is now Piping that is used more often in F#. Piping can be compared with Linux/Bash Pipes. For example in Bash you can do stuff like this.

1: 
cat file.txt | grep "foo" | sort

It basically means that it prints out the file file.txt line by line. The output is passed into grep "foo" that only shows the line that contains a foo. And that output is finally sorted by sort. F# has the operator |> to provide such a functionality. |> just means, pass the value on the left to the function on the right. So instead of

1: 
strToSum "1,2,3,4,5"

We also could write

1: 
"1,2,3,4,5" |> strToSum

Having this kind of Piping means we also could have written strToSum like this

1: 
let strToStum x = x |> parseInts |> sumList

instead of

1: 
let strToSum = parseInts >> sumList

Both styles means the same. In the |> we just provide the input argument explcitily. x |> parseInts |> sumList also can be read as. Take argument x and pass it to the parseInts function. The result of parseInts is then passed into the sumList function. This kind of style is often what you see with List manipulations.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let numbers xs =
    xs
    |> List.map    (fun x -> x + 3)     // Add +3 to every element
    |> List.map    (fun x -> x * 2)     // Multiply every element by 2
    |> List.filter (fun x -> x % 2 = 0) // Only pick even elements
    |> List.filter (fun x -> x > 10)    // Only pick elements greater than 10
    |> List.take   10                   // Only take 10 Elements

let result = numbers [1..100] // [12; 14; 16; 18; 20; 22; 24; 26; 28; 30]

This style of composing is also what you see with LINQ in C# or Java 8 Stream interface. The above code could also be implemented in this way with C# LINQ feature.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
public static IEnumerable<int> Numbers(IEnumerable<int> xs) {
    return xs
        .Select(x => x + 3)
        .Select(x => x * 2)
        .Where(x => x % 2 == 0)
        .Where(x => x > 10)
        .Take(10);
}

var result = Numbers(Enumerable.Range(1,100));

Final Note

I covered quite a lot of topics. But I hope that now functional languages looks less scary to you. By understanding all of the topics you basically already made a big step in understanding F# in general.

Further Reading

module Main
type DateTime = System.DateTime

Full name: Main.DateTime
namespace System
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

Full name: System.DateTime

--------------------
System.DateTime()
   (+0 other overloads)
System.DateTime(ticks: int64) : unit
   (+0 other overloads)
System.DateTime(ticks: int64, kind: System.DateTimeKind) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, calendar: System.Globalization.Calendar) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: System.DateTimeKind) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: System.Globalization.Calendar) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: System.DateTimeKind) : unit
   (+0 other overloads)
type StringBuilder = System.Text.StringBuilder

Full name: Main.StringBuilder
namespace System.Text
Multiple items
type StringBuilder =
  new : unit -> StringBuilder + 5 overloads
  member Append : value:string -> StringBuilder + 18 overloads
  member AppendFormat : format:string * arg0:obj -> StringBuilder + 4 overloads
  member AppendLine : unit -> StringBuilder + 1 overload
  member Capacity : int with get, set
  member Chars : int -> char with get, set
  member Clear : unit -> StringBuilder
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EnsureCapacity : capacity:int -> int
  member Equals : sb:StringBuilder -> bool
  ...

Full name: System.Text.StringBuilder

--------------------
System.Text.StringBuilder() : unit
System.Text.StringBuilder(capacity: int) : unit
System.Text.StringBuilder(value: string) : unit
System.Text.StringBuilder(value: string, capacity: int) : unit
System.Text.StringBuilder(capacity: int, maxCapacity: int) : unit
System.Text.StringBuilder(value: string, startIndex: int, length: int, capacity: int) : unit
Multiple items
type Person =
  new : name:string -> Person
  member Name : string
  member Name : string with set

Full name: Main.Person

--------------------
new : name:string -> Person
val name : string
val set : elements:seq<'T> -> Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val num : int

Full name: Main.num
val name : string

Full name: Main.name
val person : Person

Full name: Main.person
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val mutable num : int

Full name: Main.num
val mutable name : string

Full name: Main.name
val mutable person : Person

Full name: Main.person
val add : x:int -> y:int -> int

Full name: introductioninfsharp.MyOperations.add
val x : int
val y : int
module MyOperations

from introductioninfsharp
val result : int

Full name: introductioninfsharp.result
val someFunction : input:'a -> 'a0

Full name: introductioninfsharp.someFunction
val input : 'a
val someFunction : ('b -> int -> int)
val input : 'b
val position : int * int * int

Full name: Main.position
val x : int

Full name: Main.x
val y : int

Full name: Main.y
val z : int

Full name: Main.z
val someFunction : x:int -> int * int

Full name: Main.someFunction
val result : int * int

Full name: Main.result
type Human =
  {Id: int;
   FirstName: string;
   LastName: string;
   Born: DateTime;}

Full name: Main.Human
Human.Id: int
Human.FirstName: string
Human.LastName: string
Human.Born: DateTime
val me : Human

Full name: Main.me
val age : int

Full name: Main.age
property System.DateTime.Today: System.DateTime
property System.DateTime.Year: int
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val newMe : Human

Full name: Main.newMe
type Attributes =
  {Strength: int;
   Dexterity: int;
   Intelligence: int;
   Vitality: int;}

Full name: Main.Attributes
Attributes.Strength: int
Attributes.Dexterity: int
Attributes.Intelligence: int
Attributes.Vitality: int
type CaharacterSheet =
  {Name: string;
   Attribute: Attributes;}

Full name: Main.CaharacterSheet
CaharacterSheet.Name: string
CaharacterSheet.Attribute: Attributes
val warrior : CaharacterSheet

Full name: Main.warrior
type Payment =
  | Cash
  | PayPal of string
  | CreditCard of string

Full name: Main.Payment
union case Payment.Cash: Payment
union case Payment.PayPal: string -> Payment
union case Payment.CreditCard: string -> Payment
val inform : payment:Payment -> unit

Full name: Main.inform
val payment : Payment
val email : string
val no : string
type Markdown =
  | NewLine
  | Literal of string
  | Bold of string
  | InlineCode of string
  | Block of Markdown list

Full name: Main.Markdown
union case Markdown.NewLine: Markdown
Multiple items
union case Markdown.Literal: string -> Markdown

--------------------
type LiteralAttribute =
  inherit Attribute
  new : unit -> LiteralAttribute

Full name: Microsoft.FSharp.Core.LiteralAttribute

--------------------
new : unit -> LiteralAttribute
union case Markdown.Bold: string -> Markdown
union case Markdown.InlineCode: string -> Markdown
union case Markdown.Block: Markdown list -> Markdown
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val document : Markdown

Full name: Main.document
val produceHtml : markdown:Markdown -> sb:StringBuilder -> unit

Full name: Main.produceHtml
val markdown : Markdown
val sb : StringBuilder
System.Text.StringBuilder.Append(value: char []) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: obj) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: uint64) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: uint32) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: uint16) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: decimal) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: float) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: float32) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: int64) : System.Text.StringBuilder
   (+0 other overloads)
System.Text.StringBuilder.Append(value: int) : System.Text.StringBuilder
   (+0 other overloads)
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val str : string
System.Text.StringBuilder.AppendFormat(format: string, [<System.ParamArray>] args: obj []) : System.Text.StringBuilder
System.Text.StringBuilder.AppendFormat(format: string, arg0: obj) : System.Text.StringBuilder
System.Text.StringBuilder.AppendFormat(provider: System.IFormatProvider, format: string, [<System.ParamArray>] args: obj []) : System.Text.StringBuilder
System.Text.StringBuilder.AppendFormat(format: string, arg0: obj, arg1: obj) : System.Text.StringBuilder
System.Text.StringBuilder.AppendFormat(format: string, arg0: obj, arg1: obj, arg2: obj) : System.Text.StringBuilder
val code : string
val markdown : Markdown list
val x : Markdown
val html : System.Text.StringBuilder

Full name: Main.html
System.Text.StringBuilder.ToString() : string
System.Text.StringBuilder.ToString(startIndex: int, length: int) : string
val data : int list
val data : (int * int * int * int) list
val oneMore : int list
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
Multiple items
module String

from Microsoft.FSharp.Core

--------------------
type String = System.String

Full name: Main.String
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
System.String(value: nativeptr<char>) : unit
System.String(value: nativeptr<sbyte>) : unit
System.String(value: char []) : unit
System.String(c: char, count: int) : unit
System.String(value: nativeptr<char>, startIndex: int, length: int) : unit
System.String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
System.String(value: char [], startIndex: int, length: int) : unit
System.String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: System.Text.Encoding) : unit
val parseInt : str:string -> int

Full name: Main.parseInt
type Int32 =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 3 overloads
    static val MaxValue : int
    static val MinValue : int
    static member Parse : s:string -> int + 3 overloads
    static member TryParse : s:string * result:int -> bool + 1 overload
  end

Full name: System.Int32
System.Int32.Parse(s: string) : int
System.Int32.Parse(s: string, provider: System.IFormatProvider) : int
System.Int32.Parse(s: string, style: System.Globalization.NumberStyles) : int
System.Int32.Parse(s: string, style: System.Globalization.NumberStyles, provider: System.IFormatProvider) : int
val parseInts : str:String -> int list

Full name: Main.parseInts
val str : String
System.String.Split([<System.ParamArray>] separator: char []) : string []
System.String.Split(separator: string [], options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], count: int) : string []
System.String.Split(separator: string [], count: int, options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], count: int, options: System.StringSplitOptions) : string []
module Array

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val ofArray : array:'T [] -> 'T list

Full name: Microsoft.FSharp.Collections.List.ofArray
val sumList : xs:int list -> int

Full name: Main.sumList
val xs : int list
val sum : list:'T list -> 'T (requires member ( + ) and member get_Zero)

Full name: Microsoft.FSharp.Collections.List.sum
val nums : int list

Full name: Main.nums
val sum : int

Full name: Main.sum
val strToSum : stringList:String -> int

Full name: Main.strToSum
val stringList : String
val nums : int list
val sum : int
val compose : f:('a -> 'b) -> g:('b -> 'c) -> x:'a -> 'c

Full name: Main.compose
val f : ('a -> 'b)
val g : ('b -> 'c)
val x : 'a
val z : 'a
val strToSum : (String -> int)

Full name: Main.strToSum
val result : int

Full name: Main.result
val strToStum : x:String -> int

Full name: Main.strToStum
val x : String
val numbers : xs:int list -> int list

Full name: Main.numbers
val map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
val filter : predicate:('T -> bool) -> list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.filter
val take : count:int -> list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.take
val result : int list

Full name: Main.result