Skip to content

Instantly share code, notes, and snippets.

@jkone27
Last active July 14, 2023 13:04
Show Gist options
  • Save jkone27/47ac31e05afd756ffda5eb697308a166 to your computer and use it in GitHub Desktop.
Save jkone27/47ac31e05afd756ffda5eb697308a166 to your computer and use it in GitHub Desktop.
learn F# in one go
(*
module ---> like a static class in C#
open (like using or using static in C#) --> just open a namespace or module
let ----> like var but used both for varialbles and functions, any binding is done with let, last value in scope is the result
type ---> custom/user-defined types , records, classes, discriminated unions, interfaces ecc..
task ---> like async Task<T> in C#, let! is await, return! is return await, return is return
*)
namespace Some.Test
open System
open System.Collections.Generic
module SomeModule =
let x = 5
// we can enforce type signatures to correct the compiler if needed in special cases
let xWithTypeConstraint: int = 5
// in true functional languages everything is a "function"
// or better all is an expression binding
// so you can bind functions to names
let mySumBinding = (+)
let sumResult = mySumBinding 1 2
// all functions are curried by default, it means you can pass just one argument
// and a new function is returned back to you with the missing argument only
// this is useful but can also be tricky
// if you don't check that you have passed all arguments, always check what type is inferred
// by hovering with your mouse, or you can enforce the type constraint if needed
let xPlusFive = (+) 5 // this function is `x + 5`
let r = xPlusFive 10 // gives 15
let parseStringToInt =
(int) "5" // same as Int32.Parse
+
int "5" // equivalent
+
("5" |> int) // equivalent
let scopedVar =
let someScopedVal = 5
5 + 1 // last value in a scope always the return value!
let procedure () =
printfn "hello procedural world! void is called "
() // return unit/void
let list =
[ 1 ; 2; 3 ] // in line use ; separtor for stuff
@ //concat lists
[
4 // multiline does not need ; separator
5
6
]
let arrayOfInts = [| 1 ; 2; 3 |]
// List<T> in C# is ResizeArray<T> in F#, default collections in F# are immutable
let convertToDotnetList =
arrayOfInts
|> ResizeArray
let convertToDotnetIDictionary =
[
"A",1
"B",2
"C",3
]
|> dict
let dotnetDict = new Dictionary<string,string>()
// Seq module manipulates IEnumerable (seq in F#)
let enumerableManipulation =
list
|> Seq.map (fun s -> s + 1) // like Select in Linq
|> Seq.filter (fun s -> s > 0) // like .Where in Linq
|> Seq.collect (fun s -> [ s ] ) // like .Flatten in Linq
|> Seq.fold (fun s a -> s + a) 0 // like .Aggregate in Linq
// similar function exist for all main collection modules Seq, List, Array, Map, ecc
let tuple =
1 , 2, "hello", [ "hey" ; "hi" ]
let someFunction firstArg secondArg =
firstArg + secondArg
|> sprintf "result: %i"
module AnotherModule =
let someResult =
SomeModule.someFunction 1 2
let someAsyncMethod() = task {
return 5
}
let dotnetAsyncAwait = task {
let! awaitedValue = someAsyncMethod()
return awaitedValue + 5
}
module FunctionalTypes =
// records are like named tuples, similar to C# pocos/java pojos but immutable
type Person = {
Name: string ; Surname : string
Age: int // like list, no need to add ; when you go to a new line
}
let john = { Name = "john" ; Surname = "hehe" ; Age = 1 }
let olderJohn = { john with Age = 199 }
// F# type extensions, also regular extension methods are available (see online)
type Person with
member this.Hello() = $"hello i am {this.Name}"
// this is a simple Enum like in C#, note that a value has to be assigned to it, to make it an enum
type EnumValues =
| First = 1
| Second = 2
| Third = 99
// this one is not an enum but in many cases can be used to a similar extent
// without risking to change underlying representations by mistake
// to string gives you the string representation already like "First"
type SimpleDu =
| First
| Second
| Third
// a string is not an email, this is a nice way to wrap a type i a single case Discriminated union
// and do a correct domain modeling
type OneCaseDu = Email of string
// Discriminated unions can model domains better than classes and inheritance
// at compile time (static time) instead of "runtime" and objects with runtime method dispatch
type Employee =
| CEO of Person * yearsInTheCompany: int
| Manager of Person
| CFO of Person
| CTO of Person * techSkills : string list
| Developer of Person
| ProductOwner of Person
| CleaningPersonel of Person
// here different kinds of pattern matching in action
let hireNewPerson person salary =
match person with
| { Age = age } when age > 18 ->
if salary > 100.000m then
Employee.CEO(person, 0)
else if salary > 50.000m then
Employee.CTO(person, ["java"; "springboot"; "scrum"])
else
Employee.CleaningPersonel(person)
|_ ->
failwith "we cannot hire people with < 18 years of age!"
let johnCeo = hireNewPerson olderJohn 400.000m
module ObjectOrientedProgramming =
type SomeClass() =
class
end
type ICoolInterface =
interface
end
type IRealInterface =
abstract member Do : unit -> int
abstract member Something : string * string -> int
type OtherClass(privateReadonly: int) =
let privateReadonlyInternal = 5
do
printf "constructor stuff"
member this.Method(parameter) =
privateReadonly + privateReadonlyInternal * parameter
interface ICoolInterface // implement empty interface
interface IRealInterface with
member this.Do() = 5
member this.Something(name, surname) = 4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment