namespace System
val a : int

Full name: Index.Syntax.a
val c : int

Full name: Index.Syntax.c
val twice : x:int -> int

Full name: Index.Syntax.twice
val x : int
val d : int

Full name: Index.Syntax.d
val x : int

Full name: Index.Assignment.x
val mutable y : int

Full name: Index.Assignment.y
val z : bool

Full name: Index.Assignment.z
val z2 : bool

Full name: Index.Assignment.z2
val f1 : x:int -> string

Full name: Index.f1
val f2 : lower:'a -> x:'a -> upper:'a -> string (requires comparison)

Full name: Index.f2
val lower : 'a (requires comparison)
val x : 'a (requires comparison)
val upper : 'a (requires comparison)
val x : int

Full name: Index.Casting.x
val y : obj

Full name: Index.Casting.y
type obj = Object

Full name: Microsoft.FSharp.Core.obj
val z : int

Full name: Index.Casting.z
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<_>
val a : obj

Full name: Index.Casting.a
val b : int

Full name: Index.Casting.b
val x2 : obj

Full name: Index.Casting.x2
val x : obj

Full name: Index.Casting2.x
val box : value:'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
val result : string

Full name: Index.Casting2.result
val a : int
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val x : obj

Full name: Index.Casting3.x
Multiple items
val string : value:'T -> string

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

--------------------
type string = String

Full name: Microsoft.FSharp.Core.string
val str : string
property String.Length: int
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val x : obj
val x : int

Full name: Index.FExamples.x
val z : unit -> unit

Full name: Index.FExamples.z
val y : unit -> int

Full name: Index.FExamples.y
val f : x:int -> unit

Full name: Index.FExamples.f
val f2 : x:int -> unit

Full name: Index.FExamples.f2
Multiple items
type HelloClass =
  new : unit -> HelloClass
  member Foo : unit -> string
  member Y : int
  member Z : string
  member Y : int with set
  static member Bar : unit -> unit

Full name: Index.HelloClass

--------------------
new : unit -> HelloClass
val x : string
val this : HelloClass
member HelloClass.Foo : unit -> string

Full name: Index.HelloClass.Foo
member HelloClass.Z : string

Full name: Index.HelloClass.Z
member HelloClass.Y : int

Full name: Index.HelloClass.Y
val set : elements:seq<'T> -> Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
static member HelloClass.Bar : unit -> unit

Full name: Index.HelloClass.Bar
Multiple items
type Foo =
  new : unit -> Foo

Full name: Index.Foo

--------------------
new : unit -> Foo
type IAmAnInterface =
  interface
    abstract member Bark : unit -> string
    abstract member Bar : string
    abstract member Foo : string
    abstract member Bar : string with set
  end

Full name: Index.IAmAnInterface
abstract member IAmAnInterface.Bark : unit -> string

Full name: Index.IAmAnInterface.Bark
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
Multiple items
abstract member IAmAnInterface.Foo : string

Full name: Index.IAmAnInterface.Foo

--------------------
type Foo =
  new : unit -> Foo

Full name: Index.Foo

--------------------
new : unit -> Foo
abstract member IAmAnInterface.Bar : string with set

Full name: Index.IAmAnInterface.Bar
Multiple items
type ClassImplements =
  interface IAmAnInterface
  new : unit -> ClassImplements
  member Bark : unit -> string
  member Bar : string
  member Foo : string
  member Foo2 : string
  member Bar : string with set

Full name: Index.ClassImplements

--------------------
new : unit -> ClassImplements
val this : ClassImplements
member ClassImplements.Bark : unit -> string

Full name: Index.ClassImplements.Bark
Multiple items
member ClassImplements.Foo : string

Full name: Index.ClassImplements.Foo

--------------------
type Foo =
  new : unit -> Foo

Full name: Index.Foo

--------------------
new : unit -> Foo
member ClassImplements.Foo2 : string

Full name: Index.ClassImplements.Foo2
Multiple items
override ClassImplements.Foo : string

Full name: Index.ClassImplements.Foo

--------------------
type Foo =
  new : unit -> Foo

Full name: Index.Foo

--------------------
new : unit -> Foo
override ClassImplements.Bark : unit -> string

Full name: Index.ClassImplements.Bark
member ClassImplements.Bark : unit -> string
override ClassImplements.Bar : string with set

Full name: Index.ClassImplements.Bar
property ClassImplements.Bar: string
val value : string
type Employee =
  {Name: string;
   EmployeeId: Guid;
   Salary: decimal;}

Full name: Index.Employee
Employee.Name: string
Employee.EmployeeId: Guid
Multiple items
type Guid =
  struct
    new : b:byte[] -> Guid + 4 overloads
    member CompareTo : value:obj -> int + 1 overload
    member Equals : o:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member ToByteArray : unit -> byte[]
    member ToString : unit -> string + 2 overloads
    static val Empty : Guid
    static member NewGuid : unit -> Guid
    static member Parse : input:string -> Guid
    static member ParseExact : input:string * format:string -> Guid
    ...
  end

Full name: System.Guid

--------------------
Guid()
Guid(b: byte []) : unit
Guid(g: string) : unit
Guid(a: int, b: int16, c: int16, d: byte []) : unit
Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Employee.Salary: decimal
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)

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

--------------------
type decimal = Decimal

Full name: Microsoft.FSharp.Core.decimal

--------------------
type decimal<'Measure> = decimal

Full name: Microsoft.FSharp.Core.decimal<_>
val e : Employee

Full name: Index.e
Guid.NewGuid() : Guid
val e2 : Employee

Full name: Index.e2
val a : Collections.Generic.List<int>

Full name: Index.SequenceExamples.a
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type List<'T> =
  new : unit -> List<'T> + 2 overloads
  member Add : item:'T -> unit
  member AddRange : collection:IEnumerable<'T> -> unit
  member AsReadOnly : unit -> ReadOnlyCollection<'T>
  member BinarySearch : item:'T -> int + 2 overloads
  member Capacity : int with get, set
  member Clear : unit -> unit
  member Contains : item:'T -> bool
  member ConvertAll<'TOutput> : converter:Converter<'T, 'TOutput> -> List<'TOutput>
  member CopyTo : array:'T[] -> unit + 2 overloads
  ...
  nested type Enumerator

Full name: System.Collections.Generic.List<_>

--------------------
Collections.Generic.List() : unit
Collections.Generic.List(capacity: int) : unit
Collections.Generic.List(collection: Collections.Generic.IEnumerable<'T>) : unit
val b : Collections.Generic.List<int>

Full name: Index.SequenceExamples.b
type ResizeArray<'T> = Collections.Generic.List<'T>

Full name: Microsoft.FSharp.Collections.ResizeArray<_>
val literalArray : 'a []

Full name: Index.SequenceExamples.literalArray
val arrayOf1To10 : int []

Full name: Index.SequenceExamples.arrayOf1To10
val listOf1To10 : int list

Full name: Index.SequenceExamples.listOf1To10
val literalList : 'a list

Full name: Index.SequenceExamples.literalList
val items : int list

Full name: Index.NotLinq.items
val doubled : seq<int>

Full name: Index.NotLinq.doubled
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val evens : items:seq<int> -> seq<int>

Full name: Index.NotLinq.evens
val items : seq<int>
val filter : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.filter
val items : int list

Full name: Index.Querying.items
val q : seq<int * int>

Full name: Index.Querying.q
val query : Linq.QueryBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.query
val i : int
custom operation: join var in collection on (outerKey = innerKey). Note that parentheses are required after 'on'

Calls Linq.QueryBuilder.Join
val j : int
custom operation: select ('Result)

Calls Linq.QueryBuilder.Select
Multiple items
type MeasureAttribute =
  inherit Attribute
  new : unit -> MeasureAttribute

Full name: Microsoft.FSharp.Core.MeasureAttribute

--------------------
new : unit -> MeasureAttribute
[<Measure>]
type inch

Full name: Index.inch
[<Measure>]
type foot

Full name: Index.foot
[<Measure>]
type sqft = foot ^ 2

Full name: Index.sqft
[<Measure>]
type dollar

Full name: Index.dollar
val sizes : int<sqft> []

Full name: Index.sizes
val prices : int<dollar> []

Full name: Index.prices
val inchesPerFoot : int<inch/foot>

Full name: Index.inchesPerFoot

INTRO TO F#

(For C#ers)

FsReveal notes

Syntax

F# (with tooltips)

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
open System
module Syntax =
  let a = 5 // var a = 5;
  let c = 1 + a // var c = 1 + a;
  // public int Twice(int x) => x * 2;
  // the () are optional
  let twice(x) = 2 * x
  // the () are optional
  let d = twice(a)

fsreveal magic c and d are evaluated for you

6
10

Assignment

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
module Assignment =
  // let is a binding not an assignment
  // = is a binding or comparison not an Assignment
  let x = 5 // not mutable
  let mutable y = 5
  // parens are just to help visualize it for new learners
  let z = (5 = 6)
  // same as above since x is immutable and is 5
  let z2 = (x = 6)
  y <- y + 1

Assignment.z evaluates to

false

Flow Control

if

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
// string F1(int x) => x < 5 ? "<" : ">=";
let f1 x = if x < 5 then "<" else ">="
// string F2(int lower, int x, int upper) =>
//    x < lower ? "less than" : x > upper ? "greater than" : "between";
let f2 lower x upper =
  if x < lower then
    "less than"
  else if x > upper then // or elsif
    "greater than"
  else "between"

Casting

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
module Casting =
  let x = 5 // var x = 5;
  // var y = (obj) x;
  // upcast (always succeeds if it compiles)
  let y = x :> obj
  // downcast (? mark notes the possibility of failure)
  // var z = (int)y;
  let z = y :?> int
  // var a = (obj)x;
  let a:obj = upcast x // when the type is inferrable
  // var b = (int)y;
  // when the type is inferrable (still could fail)
  let b:int = downcast y
  // var x2 = (object)2;
  let x2:obj = upcast 2

Casting 2

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
module Casting2 =
  // box is shorthand for upcast to object
  // var x = (object) 1;
  let x:obj = box 1
  // in C#5(maybe even 6) you can't conditionally define a variable
  // var a2 = x as int?;
  // due to the line above now a2 is in scope for the rest of the method
  // x gives a warning here, we haven't covered all possibilities
  // nothing like that in C# besides default case requirement of switches
  let result =
    match x with
    // advanced/arguably misusable power:
    // in F# we don't need to use a separate variable name for the casted variable
    // if(a2 != null) Console.WriteLine("int value:" + a2);
    | :? int as a -> sprintf "int value: %i" a
    // calls ToString if it is not null, a tuple, record or union
    // if(a1 == null) Console.WriteLine("non-int null value is "+a1);
    | null -> sprintf "non-int null value is %A" x // prints <null> for null values
"int value: 1"

Casting 3

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
module Casting3 =
  // var x = (object)"1";
  let x:obj = box "1"
  // conditional matches
  match x with
  // var a3 =  x as string; if(a3 != null && a3.Length > 0) ...
  | :? string as str when str.Length > 0 -> printfn "string value: %s" str
  // if(a3 == null && x != null) ...
  // also prints <null> for null values
  | x -> printfn "value is %O" x // calls toString if it is not null

Syntax 2

fields vs methods

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
// public static class FExamples
module FExamples =
  // readonly static int x = 1;
  let x = 1
  let z () = () // void Z() {}
  let y () = 1 // int Y() { return 1;}
  // void F(int x) {}
  let f (x:int) = () // method
  // void F2(int x) => F(x);
  let f2 x = f x // method

Objects

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
public class Employee
{
  readonly string x="hello";
  public string Foo() => x;
  public string Z => x;
  public int Y {get;set;}
  public static void Bar() => {};
}
public class Foo{}
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type HelloClass() =
  let x = "hello" // field
  member this.Foo() = x
  member this.Z = x
  member val Y = 0 with get,set
  static member Bar() = ()
// empty class
type Foo() = class end

Interfaces

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type IAmAnInterface =
  // string Bark();
  abstract member Bark : unit -> string
  // string Version { get; }
  abstract member Foo : string with get
  // string Bar {get;set;}
  abstract member Bar : string with get,set
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
type ClassImplements () =
  let x = "hello"
  member this.Bark() = x
  // Foo is a property! not a field
  member this.Foo = x
  // you can use whatever name you like for `this`
  member __.Foo2 with get() = x
  // this creates a backing field of its own, using x's value as the initial value
  member val Bar = x with get,set

  // f# downside no implicit interface implementation
  // interface members are always considered explicity implemented
  // members only show up/compile if you cast the type to the interface first
  interface IAmAnInterface with
    member __.Foo = x
    // this isn't recursive
    member this.Bark () = this.Bark()
    member this.Bar
      with get() = this.Bar
      and set value = this.Bar <- value

C#

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
public class Employee
{
  public string Name { get; } // C# 6 syntax
  public Guid EmployeeId { get; }
  public decimal Salary { get;}

  public Employee(string name, Guid employeeId, decimal salary)
    Name=name;
    EmployeeId=employeeId;
    Salary=salary;
  }
}

Records

1: 
2: 
3: 
4: 
5: 
6: 
type Employee = {Name:string; EmployeeId:Guid; Salary:decimal}
let e = {Name="Brandon D'Imperio"; EmployeeId=Guid.NewGuid(); Salary = 15.5m}
// how much typing would you need to do in C# to do this?
// create a new employee with the same salary
// and any other properties that we don't set get copied.
let e2 = {e with Name="John Doe"; EmployeeId = Guid.NewGuid()}

Collections

Sequences

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
module SequenceExamples =
  // new is not required in F#
  // you will get a warning on IDisposables
  let a = System.Collections.Generic.List<int>()
  // same as the line above (using built-in f# aliases)
  let b = ResizeArray<int>()
  let literalArray = [| |] // also Array.empty
  // var arrayOf1To10 = Enumerable.Range(1, 10).ToArray();
  let arrayOf1To10 = [| 1..10 |]
  // F# list type
  let listOf1To10 = [ 1..10 ]

  // Microsoft.FSharp.Collections.ListModule.Empty<int>()
  let literalList = [] // F# version is immutable

C#

1: 
2: 
3: 
4: 
var items = new []{123,456,10,999,9};
var q= from i in items
  join j in items on i equals j
  select new {i,j};

Linq

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
module NotLinq =
  // var items new [] {123, 456,10,999,9};
  let items = [123;456;10;999;9]
  // var doubled = items.Select(x => x * 2);
  let doubled = items |> Seq.map (fun x -> x * 2)
  // this is why C# can't have nice things. look how long this is.
  // public IEnumerable<int> Evens(IEnumerable<int> items) =>
  //     items.Where(x => x % 2 == 0);
  let evens items = items |> Seq.filter (fun x -> x % 2 = 0)

Query Form

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
module Querying =
  let items = [123;456;10;999;9]
  let q =
    query {
        for i in items do
        join j in items on
            (i = j)
        select (i,j)
    }

Units of Measure

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
[<Measure>] type inch
[<Measure>] type foot
[<Measure>] type sqft = foot*foot
[<Measure>] type dollar
let sizes = [|1700<sqft>;2100<sqft>;1900<sqft>;1300<sqft>|]
let prices = [|53000<dollar>;44000<dollar>;59000<dollar>;82000<dollar>|]
let inchesPerFoot = 12<inch/foot>
// can't seem to get this working here, nor in linqpad
// let numLiteral = 12_000<dollar>

prices.[0]/sizes.[0]

31

Not Covered, barely scratched surface, or not covered well

Good

  • Pattern matching (exhaustive matching and compiler warnings)
  • Option types
  • Units of measure conversions
  • Discriminated Unions (also recursive DUs)
  • Tuples (easier and more useful)
  • Object expressions (we don't need a class or record to implement an interface)
  • Computation expressions
  • keywords - rec, function, async, lazy, _
  • Tupled vs curried method forms
  • Statically resolved type parameters (structural typing)
  • Unwanted features (don't have, and don't want) - implicit casts
  • Underscores in numeric literals as of 4.1
  • """ triple quoted strings """
  • Extension methods, properties, events, static methods
  • shadowing (let x = 1; let x = x + 1;)
  • signature files
  • There are all of 3 features in C# since 2.0 that F# didn't have before C# did (citation needed, if true)

Bad-ish

  • Generic Measures can be difficult to work with
  • Missing features (nameof operator, covariance/contravariance)
  • Interop(ugliness of delegate interop)
  • Nested classes
  • No implicit interface implementations
  • No good tooling for UI work (mvc, wpf, winforms, webforms, etc.)
  • try reflection walking a namespace to get modules
  • works in unity, but can't be main assembly
  • works in mvc, but can't write razor pages in it
  • can't T4

Resources