Introduction to Fable.Remoting

Introduction to Fable.Remoting

Introduction to F# Mikhail Smal https://mikhail.dev Win a ticket to Update Conference 2019! https://forms.gle/Z9EBCmSG877BBQMg7 Introduction to F# Mikhail Smal https://mikhail.dev F# ▪ Multiparadigm programming language ▪ Functional first ▪ Strongly and statically typed ▪ Open-source ▪ Cross-platform thanks to .NET Core ▪ Compiles to JavaScript thanks to Fable ▪ Scripting language ▪ Awesome community Where to use ▪ Web development with SAFE ▪ Saturn – web-server framework ▪ Azure – cloud provider ▪ Fable – F# -> JS compiler ▪ Elmish – Model-View-Update implementation ▪ Serverless (FaaS) ▪ Mobile development (Xamarin, React Native) Tooling ▪ Visual Studio ▪ Visual Studio Code (Ionide extension) ▪ JetBrains Rider ▪ MonoDevelop ▪ vim-fsharp Basic syntax Values // int let age = 30 // inferred // int let age : int = 31 // explicit // string let age = string age // totally different type // unit let nothing = () Functions // 'a -> 'a -> 'a let add x y = x + y // indentation instead of curly brackets // int -> int let addTen = add 10 // partial application // int let result = addTen 2 // 12 // ('a * 'b) -> ('a -> 'b -> 'c) -> 'c let exec (x, y) f = f x y let result1 = exec (100, 10) (-) // 90 let result2 = exec ("Hello", "developers") (sprintf "%s, %s!") // ”Hello, developers!” let (>>!) = exec let result3 = ("Hello", "devs") >>! (sprintf "%s, %s!") let result4 = ("Hello", "devs") ||> (sprintf "%s, %s!") Functions with types let add (x : int) (y : int) : int = x + y let printAll<'a> (collection : 'a seq) = Seq.iter (fun x -> printfn "%A" x) collection printAll<int> [1;2;3] type AddFunction = int -> int -> int let add : AddFunction = fun x y -> x + y Records (Product types) type User = { FirstName : string LastName : string Age : int } let user = { FirstName = "Mikhail" LastName = "Smal" Age = 30 } user.Age = 31 // = - comparison operator. Result is false let olderUser = { user with Age = 31 } type UserWithMutableAge = { FirstName : string LastName : string mutable Age : int } user.Age <- 31 Anonymous records let data = {| X = 1; Y = 2 |} let data' = {| data with Y = 3 |} let data = {| X = 1; Y = 2 |} let expandedData = {| data with Z = 3 |} // Gives {| X=1; Y=2; Z=3 |} type R = { X: int } let data = { X = 1 } let data' = {| data with Y = 2 |} // Gives {| X=1; Y=2 |} Discriminated unions (Sum types) type CardType = | Visa | Mastercard type CardNumber = CardNumber of string // Single case DU type CheckNumber = CheckNumber of int type PaymentMethod = | Cash | Check of CheckNumber | Card of CardType * CardNumber let checkNumber = CheckNumber 999 let cashPaymentMethod = Cash let checkPaymentMethod = Check checkNumber let checkPaymentMethod = Check (CheckNumber 999) let cardPaymentMethod = Card (Visa, CardNumber "XXXX-XXXX-XXXX-XXXX") F# is awesome for DDD Naming let ``Function throws an exception when receives ¯\_(ツ)_/¯`` () = ... Type system type [<Measure>] g type [<Measure>] inch type Gramms = Gramms of int<g> type Inches = Inches of float<inch> type GadgetName = GadgetName of string // 5 characters starting with T type PhoneCode = PhoneCode of string // 10000 < code < 100000 type TabletCode = TabletCode of int type GadgetCode = | PhoneCode of PhoneCode | TabletCode of TabletCode type Gadget = { Code : GadgetCode Name : GadgetName Weight : Gramms ScreenSize : Inches } Restrictions // 5 characters starting with T type PhoneCode = private PhoneCode of string let phoneCode = PhoneCode "T1234" // Won't compile let (PhoneCode code) = phoneCode // Won't work in other modules type Option<'a> = | Some of 'a | None module PhoneCode = let create (s : string) = if not (isNull s) && s.StartsWith("T") && s.Length = 5 then Some (PhoneCode s) else None let value (PhoneCode code) = // Works only in one module code Pattern matching open FSharp.Data.UnitSystems.SI.UnitSymbols type Direction = North | South | East | West type Weather = | Cold of temperature:float<C> | Sunny | Wet | Windy of Direction * windspeed:float<m/s> let (|Low|Medium|High|) speed = // Active patterns if speed > 10.<m/s> then High elif speed > 5<m/s>. then Medium else Low match weather with | Cold temp when temp < 2.0<C> -> "Really cold!" | Cold _ | Wet -> "Miserable weather!" | Sunny -> "Nice weather" | Windy (North, High) -> "High speed northernly wind!" | Windy (South, _) -> "Blowing southwards" | Windy _ -> "It's windy!" Collections let listOfInts1 = [1;2;3] let listOfInts2 = [ "Line1" "Line2" "Line3" ] let listOfInts3 = [1..10] let arrayOfInts = [|1;2;3|] // Cannot be matched match listOfInts1 with | [] -> 0 | [1;_] -> 1 | head::tail -> head | _ -> 100 Composition // int -> float let foo x = ... // float -> string let bar x = ... // int -> string let fooBar = foo >> bar Pipelines let data = [(true, 100); (true, 200); (false, -10)] data |> List.filter fst |> List.sortByDescending snd |> List.take 1 |> List.map string |> List.head Computation expressions let squares = seq { for i in 1..10 do yield i * i } let ints = seq { yield! [1;2;3] } Computation expressions let option1 = Some 1 let option2 = Some 2 let result = let result = optional { option1 let! value1 = option1 |> Option.bind (fun value1 -> let! value2 = option2 option2 return value1 + value2 |> } Option.bind (fun value2 -> Some (value1 + value2) ) ) Saturn CEs let app = application { pipe_through endpointPipe use_router topRouter url "http://0.0.0.0:8085/" memory_cache use_static "static" use_gzip } let ctrl = controller { index someIndexHandler add someAddHandler show someShowHandler edit someEditHandler } Railway-oriented programming Imperative code string ExecuteUseCase() { var request = receiveRequest(); validateRequest(request); canonicalizeEmail(request); db.UpdateDbFromRequest(request); smtpServer.SendEmail(request.Email); return "Success"; } Functional flow let executeUseCase = receiveRequest >> validateRequest >> canonicalizeEmail >> updateDbFromRequest >> sendEmail >> returnMessage Imperative code with error handling string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); validateRequest(request); canonicalizeEmail(request); db.UpdateDbFromRequest(request); smtpServer.SendEmail(request.Email); return "OK"; } Imperative code with error handling string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid"; } canonicalizeEmail(request); db.UpdateDbFromRequest(request); smtpServer.SendEmail(request.Email); return "OK"; } Imperative code with error handling string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid"; } canonicalizeEmail(request); var result = db.UpdateDbFromRequest(request); if (!result) { return "Customer record not found"; } smtpServer.SendEmail(request.Email); return "OK"; } Imperative code with error handling string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid"; } canonicalizeEmail(request); try { var result = db.UpdateDbFromRequest(request); if (!result) { return "Customer record not found"; } } catch { return "DB error: Customer record not updated"; } smtpServer.SendEmail(request.Email); return "OK"; } Imperative code with error handling string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid"; } canonicalizeEmail(request); try { var result = db.UpdateDbFromRequest(request); if (!result) { return "Customer record not found"; } } catch { return "DB error: Customer record not updated"; } if (!smtpServer.SendEmail(request.Email)) { log.Error "Customer email not sent" } return "OK"; } Functional flow with error handling Before let executeUseCase = receiveRequest >> validateRequest >> canonicalizeEmail >> updateDbFromRequest >> sendEmail >> returnMessage Functional flow with error handling After let updateCustomerWithErrorHandling = receiveRequest >> validateRequest >> canonicalizeEmail >> updateDbFromRequest >> sendEmail >> returnMessage Result type type Result<'Success,'Failure> = | Ok of 'Success | Error of 'Failure Validation function Request Validate Success Failure let validateInput input = if input.name = "" then Error "Name must not be blank" elif input.email = "" then Error "Email must not be blank" else Ok input // Success Switch Switches composition Validate UpdateDb on success bypass Switches composition Validate UpdateDb Switches composition Validate UpdateDb SendEmail Switches composition Switches composition One track composition Two-track composition Wrong composition Adapter block let bind switchFunction = fun twoTrackInput -> match twoTrackInput with | Ok s -> switchFunction s | Error f -> Error f val bind : ('a -> Result<'b,'c>) -> Result<'a,'c> -> Result<'b,'c> Bind example nameNotBlank (combined with) name50 (combined with) emailNotBlank // Input -> Result<Input,’Failure> Bind example bind nameNotBlank bind name50 bind emailNotBlank // (bind nameNotBlank): Result<Input,’Failure> -> Result<Input,’Failure> Bind example bind nameNotBlank >> bind name50 >> bind emailNotBlank Bind example let validateRequest = bind nameNotBlank >> bind name50 >> bind emailNotBlank // validateRequest: Result<Input,’Failure> -> Result<Input,’Failure> Bind example let (>>=) twoTrackInput switchFunction = bind switchFunction twoTrackInput let validateRequest twoTrackInput = twoTrackInput >>= nameNotBlank >>= name50 >>= emailNotBlank Further

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    75 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us