Last active
December 17, 2022 00:21
-
-
Save bigjonroberts/34b91c65f11c4ac5c414ce112a40f7a8 to your computer and use it in GitHub Desktop.
F# example of monadic functions with C# examples for Task<T>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "[You Only Live Once](https://raw.githubusercontent.com/haf/YoLo/master/YoLo.fs)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "fsharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "fsharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "[<AutoOpen>]\n", | |
| "module YoLo =\n", | |
| "\n", | |
| " open System\n", | |
| " open System.Threading.Tasks\n", | |
| "\n", | |
| " let curry f a b = f (a, b)\n", | |
| "\n", | |
| " let uncurry f (a, b) = f a b\n", | |
| "\n", | |
| " let flip f a b = f b a\n", | |
| "\n", | |
| " let ct x = fun _ -> x\n", | |
| "\n", | |
| " module Choice =\n", | |
| "\n", | |
| " let create v = Choice1Of2 v\n", | |
| "\n", | |
| " let createSnd v = Choice2Of2 v\n", | |
| "\n", | |
| " let map f = function\n", | |
| " | Choice1Of2 v -> Choice1Of2 (f v)\n", | |
| " | Choice2Of2 msg -> Choice2Of2 msg\n", | |
| "\n", | |
| " let mapSnd f = function\n", | |
| " | Choice1Of2 v -> Choice1Of2 v\n", | |
| " | Choice2Of2 v -> Choice2Of2 (f v)\n", | |
| "\n", | |
| " let map2 f1 f2: Choice<'a, 'b> -> Choice<'c, 'd> = function\n", | |
| " | Choice1Of2 v -> Choice1Of2 (f1 v)\n", | |
| " | Choice2Of2 v -> Choice2Of2 (f2 v)\n", | |
| "\n", | |
| " let bind (f: 'a -> Choice<'b, 'c>) (v: Choice<'a, 'c>) =\n", | |
| " match v with\n", | |
| " | Choice1Of2 v -> f v\n", | |
| " | Choice2Of2 c -> Choice2Of2 c\n", | |
| "\n", | |
| " let bindSnd (f: 'a -> Choice<'c, 'b>) (v: Choice<'c, 'a>) =\n", | |
| " match v with\n", | |
| " | Choice1Of2 x -> Choice1Of2 x\n", | |
| " | Choice2Of2 x -> f x\n", | |
| " \n", | |
| " let fold f g =\n", | |
| " function\n", | |
| " | Choice1Of2 x -> f x\n", | |
| " | Choice2Of2 y -> g y\n", | |
| " \n", | |
| " let apply f v =\n", | |
| " bind (fun f' ->\n", | |
| " bind (fun v' ->\n", | |
| " create (f' v')) v) f\n", | |
| "\n", | |
| " let applySnd f v =\n", | |
| " bind (fun f' ->\n", | |
| " bindSnd (fun v' ->\n", | |
| " createSnd (f' v')) v) f\n", | |
| "\n", | |
| " let lift2 f v1 v2 =\n", | |
| " apply (apply (create f) v1) v2\n", | |
| "\n", | |
| " let lift3 f v1 v2 v3 =\n", | |
| " apply (apply (apply (create f) v1) v2) v3\n", | |
| "\n", | |
| " let lift4 f v1 v2 v3 v4 =\n", | |
| " apply (apply (apply (apply (create f) v1) v2) v3) v4\n", | |
| "\n", | |
| " let lift5 f v1 v2 v3 v4 v5 =\n", | |
| " apply (apply (apply (apply (apply (create f) v1) v2) v3) v4) v5\n", | |
| "\n", | |
| " let ofOption onMissing = function\n", | |
| " | Some x -> Choice1Of2 x\n", | |
| " | None -> Choice2Of2 (onMissing ())\n", | |
| "\n", | |
| " let ofResult = function\n", | |
| " | Ok x -> Choice1Of2 x\n", | |
| " | Error x -> Choice2Of2 x\n", | |
| "\n", | |
| " let toResult = function\n", | |
| " | Choice1Of2 x -> Ok x\n", | |
| " | Choice2Of2 x -> Error x\n", | |
| " \n", | |
| " let orDefault value = function\n", | |
| " | Choice1Of2 v -> v\n", | |
| " | _ -> value ()\n", | |
| "\n", | |
| " let inject f = function\n", | |
| " | Choice1Of2 x -> f x; Choice1Of2 x\n", | |
| " | Choice2Of2 x -> Choice2Of2 x\n", | |
| "\n", | |
| " let injectSnd f = function\n", | |
| " | Choice1Of2 x -> Choice1Of2 x\n", | |
| " | Choice2Of2 x -> f x; Choice2Of2 x\n", | |
| "\n", | |
| " module Operators =\n", | |
| "\n", | |
| " let inline (>>=) m f =\n", | |
| " bind f m\n", | |
| "\n", | |
| " let inline (>>-) m f = // snd\n", | |
| " bindSnd f m\n", | |
| "\n", | |
| " let inline (=<<) f m =\n", | |
| " bind f m\n", | |
| "\n", | |
| " let inline (-<<) f m = // snd\n", | |
| " bindSnd f m\n", | |
| "\n", | |
| " let inline (>>*) m f =\n", | |
| " inject f m\n", | |
| "\n", | |
| " let inline (>>@) m f = // snd\n", | |
| " injectSnd f m\n", | |
| "\n", | |
| " let inline (<*>) f m =\n", | |
| " apply f m\n", | |
| "\n", | |
| " let inline (<!>) f m =\n", | |
| " map f m\n", | |
| "\n", | |
| " let inline (>!>) m f =\n", | |
| " map f m\n", | |
| "\n", | |
| " let inline (<@>) f m = // snd\n", | |
| " mapSnd f m\n", | |
| "\n", | |
| " let inline (>@>) m f = // snd\n", | |
| " mapSnd f m\n", | |
| "\n", | |
| " let inline ( *>) m1 m2 =\n", | |
| " lift2 (fun _ x -> x) m1 m2\n", | |
| "\n", | |
| " let inline ( <*) m1 m2 =\n", | |
| " lift2 (fun x _ -> x) m1 m2\n", | |
| "\n", | |
| "\n", | |
| " module Result =\n", | |
| "\n", | |
| " let map2 f1 f2: Result<'a, 'b> -> Result<'c, 'd> = function\n", | |
| " | Ok v -> Ok (f1 v)\n", | |
| " | Error v -> Error (f2 v)\n", | |
| "\n", | |
| " let bindError (f: 'a -> Result<'c, 'b>) (v: Result<'c, 'a>) =\n", | |
| " match v with\n", | |
| " | Ok x -> Ok x\n", | |
| " | Error x -> f x\n", | |
| " \n", | |
| " let fold f g =\n", | |
| " function\n", | |
| " | Ok x -> f x\n", | |
| " | Error y -> g y\n", | |
| " \n", | |
| " let apply f v =\n", | |
| " Result.bind (fun f' ->\n", | |
| " Result.bind (fun v' ->\n", | |
| " Ok (f' v')) v) f\n", | |
| "\n", | |
| " let applyError f v =\n", | |
| " Result.bind (fun f' ->\n", | |
| " bindError (fun v' ->\n", | |
| " Error (f' v')) v) f\n", | |
| "\n", | |
| " let lift2 f v1 v2 =\n", | |
| " apply (apply (Ok f) v1) v2\n", | |
| "\n", | |
| " let lift3 f v1 v2 v3 =\n", | |
| " apply (apply (apply (Ok f) v1) v2) v3\n", | |
| "\n", | |
| " let lift4 f v1 v2 v3 v4 =\n", | |
| " apply (apply (apply (apply (Ok f) v1) v2) v3) v4\n", | |
| "\n", | |
| " let lift5 f v1 v2 v3 v4 v5 =\n", | |
| " apply (apply (apply (apply (apply (Ok f) v1) v2) v3) v4) v5\n", | |
| "\n", | |
| " let ofOption onMissing = function\n", | |
| " | Some x -> Ok x\n", | |
| " | None -> Error onMissing\n", | |
| "\n", | |
| " let toChoice = function\n", | |
| " | Ok x -> Choice1Of2 x\n", | |
| " | Error x -> Choice2Of2 x\n", | |
| "\n", | |
| " let ofChoice = function\n", | |
| " | Choice1Of2 x -> Ok x\n", | |
| " | Choice2Of2 x -> Error x\n", | |
| "\n", | |
| " let inject f = function\n", | |
| " | Ok x -> f x; Ok x\n", | |
| " | Error x -> Error x\n", | |
| "\n", | |
| " let injectError f = function\n", | |
| " | Ok x -> Ok x\n", | |
| " | Error x -> f x; Error x\n", | |
| "\n", | |
| " module Operators =\n", | |
| "\n", | |
| " let inline (>>=) m f =\n", | |
| " Result.bind f m\n", | |
| "\n", | |
| " let inline (>>-) m f = // snd\n", | |
| " bindError f m\n", | |
| "\n", | |
| " let inline (=<<) f m =\n", | |
| " Result.bind f m\n", | |
| "\n", | |
| " let inline (-<<) f m = // snd\n", | |
| " bindError f m\n", | |
| "\n", | |
| " let inline (>>*) m f =\n", | |
| " inject f m\n", | |
| "\n", | |
| " let inline (>>@) m f = // snd\n", | |
| " injectError f m\n", | |
| "\n", | |
| " let inline (<*>) f m =\n", | |
| " apply f m\n", | |
| "\n", | |
| " let inline (<!>) f m =\n", | |
| " Result.map f m\n", | |
| "\n", | |
| " let inline (>!>) m f =\n", | |
| " Result.map f m\n", | |
| "\n", | |
| " let inline (<@>) f m = // snd\n", | |
| " Result.mapError f m\n", | |
| "\n", | |
| " let inline (>@>) m f = // snd\n", | |
| " Result.mapError f m\n", | |
| "\n", | |
| " let inline ( *>) m1 m2 =\n", | |
| " lift2 (fun _ x -> x) m1 m2\n", | |
| "\n", | |
| " let inline ( <*) m1 m2 =\n", | |
| " lift2 (fun x _ -> x) m1 m2\n", | |
| "\n", | |
| " module Option =\n", | |
| "\n", | |
| " let create x = Some x\n", | |
| "\n", | |
| " let apply (f : ('a -> 'b) option) (v: 'a option) =\n", | |
| " Option.bind (fun f' ->\n", | |
| " Option.bind (fun v' ->\n", | |
| " create (f' v')) v) f\n", | |
| "\n", | |
| " let lift2 f v1 v2 =\n", | |
| " apply (apply (create f) v1) v2\n", | |
| "\n", | |
| " let lift3 f v1 v2 v3 =\n", | |
| " apply (apply (apply (create f) v1) v2) v3\n", | |
| "\n", | |
| " let lift4 f v1 v2 v3 v4 =\n", | |
| " apply (apply (apply (apply (create f) v1) v2) v3) v4\n", | |
| "\n", | |
| " let lift5 f v1 v2 v3 v4 v5 =\n", | |
| " apply (apply (apply (apply (apply (create f) v1) v2) v3) v4) v5\n", | |
| "\n", | |
| " let ofChoice = function\n", | |
| " | Choice1Of2 x -> Some x\n", | |
| " | _ -> None\n", | |
| "\n", | |
| " let toChoice case2 = function\n", | |
| " | Some x -> Choice1Of2 x\n", | |
| " | None -> Choice2Of2 (case2 ())\n", | |
| "\n", | |
| " let ofNullable nullable: 'a option =\n", | |
| " match box nullable with\n", | |
| " | null -> None // CLR null\n", | |
| " | :? Nullable<_> as n when not n.HasValue -> None // CLR struct\n", | |
| " | :? Nullable<_> as n when n.HasValue -> Some (n.Value) // CLR struct\n", | |
| " | x when x.Equals (DBNull.Value) -> None // useful when reading from the db into F#\n", | |
| " | x -> Some (unbox x) // anything else\n", | |
| "\n", | |
| " let toNullable = function\n", | |
| " | Some item -> new Nullable<_>(item)\n", | |
| " | None -> new Nullable<_>()\n", | |
| "\n", | |
| " let orDefault x = function\n", | |
| " | None -> x ()\n", | |
| " | Some y -> y\n", | |
| "\n", | |
| " let inject f = function\n", | |
| " | Some x -> f x; Some x\n", | |
| " | None -> None\n", | |
| "\n", | |
| " module Operators =\n", | |
| "\n", | |
| " let inline (>>=) m f =\n", | |
| " Option.bind f m\n", | |
| "\n", | |
| " let inline (=<<) f m =\n", | |
| " Option.bind f m\n", | |
| "\n", | |
| " let inline (>>*) m f =\n", | |
| " inject f m\n", | |
| "\n", | |
| " let inline (<*>) f m =\n", | |
| " apply f m\n", | |
| "\n", | |
| " let inline (<!>) f m =\n", | |
| " Option.map f m\n", | |
| "\n", | |
| " let inline ( *>) m1 m2 =\n", | |
| " lift2 (fun _ x -> x) m1 m2\n", | |
| "\n", | |
| " let inline ( <*) m1 m2 =\n", | |
| " lift2 (fun x _ -> x) m1 m2\n", | |
| "\n", | |
| " type Base64String = string\n", | |
| "\n", | |
| " module String =\n", | |
| " open System.Globalization // needed when using DNXCORE50\n", | |
| " open System.IO\n", | |
| " open System.Security.Cryptography\n", | |
| "\n", | |
| " /// Also, invariant culture\n", | |
| " let equals (a: string) (b: string) =\n", | |
| " #if DNXCORE50\n", | |
| " (CultureInfo.InvariantCulture.CompareInfo.GetStringComparer(CompareOptions.None)).Equals(a, b)\n", | |
| " #else\n", | |
| " a.Equals(b, StringComparison.InvariantCulture)\n", | |
| " #endif\n", | |
| "\n", | |
| " /// Also, invariant culture\n", | |
| " let equalsCaseInsensitive (a: string) (b: string) =\n", | |
| " #if DNXCORE50\n", | |
| " (CultureInfo.InvariantCulture.CompareInfo.GetStringComparer(CompareOptions.IgnoreCase)).Equals(a, b)\n", | |
| " #else\n", | |
| " a.Equals(b, StringComparison.InvariantCultureIgnoreCase)\n", | |
| " #endif\n", | |
| " \n", | |
| " /// Compare ordinally with ignore case.\n", | |
| " let equalsOrdinalCI (str1: string) (str2: string) =\n", | |
| " String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)\n", | |
| "\n", | |
| " /// Ordinally compare two strings in constant time, bounded by the length of the\n", | |
| " /// longest string.\n", | |
| " let equalsConstantTime (str1: string) (str2: string) =\n", | |
| " let mutable xx = uint32 str1.Length ^^^ uint32 str2.Length\n", | |
| " let mutable i = 0\n", | |
| " while i < str1.Length && i < str2.Length do\n", | |
| " xx <- xx ||| uint32 (int str1.[i] ^^^ int str2.[i])\n", | |
| " i <- i + 1\n", | |
| " xx = 0u\n", | |
| "\n", | |
| " let toLowerInvariant (str: string) =\n", | |
| " str.ToLowerInvariant()\n", | |
| "\n", | |
| " let replace (find: string) (replacement: string) (str: string) =\n", | |
| " str.Replace(find, replacement)\n", | |
| "\n", | |
| " let isEmpty (s: string) =\n", | |
| " s.Length = 0\n", | |
| "\n", | |
| " let trim (s: string) =\n", | |
| " s.Trim()\n", | |
| " \n", | |
| " let trimc (toTrim: char) (s: string) =\n", | |
| " s.Trim toTrim\n", | |
| " \n", | |
| " let trimStart (s: string) =\n", | |
| " s.TrimStart()\n", | |
| " \n", | |
| " let split (c: char) (s: string) =\n", | |
| " s.Split c |> Array.toList\n", | |
| " \n", | |
| " let splita (c: char) (s: string) =\n", | |
| " s.Split c\n", | |
| " \n", | |
| " let startsWith (substring: string) (s: string) =\n", | |
| " s.StartsWith substring\n", | |
| " \n", | |
| " let contains (substring: string) (s: string) =\n", | |
| " s.Contains substring\n", | |
| " \n", | |
| " let substring index (s: string) =\n", | |
| " s.Substring index\n", | |
| "\n", | |
| " module Bytes =\n", | |
| " open System.IO\n", | |
| " open System.Linq\n", | |
| " open System.Security.Cryptography\n", | |
| "\n", | |
| " let hash (algo: unit -> #HashAlgorithm) (bs: byte[]) =\n", | |
| " use ms = new MemoryStream()\n", | |
| " ms.Write(bs, 0, bs.Length)\n", | |
| " ms.Seek(0L, SeekOrigin.Begin) |> ignore\n", | |
| " use sha = algo ()\n", | |
| " sha.ComputeHash ms\n", | |
| "\n", | |
| " let sha1 =\n", | |
| " #if DNXCORE50\n", | |
| " hash (fun () -> SHA1.Create())\n", | |
| " #else\n", | |
| " hash (fun () -> new SHA1Managed())\n", | |
| " #endif\n", | |
| "\n", | |
| " let sha256 =\n", | |
| " #if DNXCORE50\n", | |
| " hash (fun () -> SHA256.Create())\n", | |
| " #else\n", | |
| " hash (fun () -> new SHA256Managed())\n", | |
| " #endif\n", | |
| "\n", | |
| " let sha512 =\n", | |
| " #if DNXCORE50\n", | |
| " hash (fun () -> SHA512.Create())\n", | |
| " #else\n", | |
| " hash (fun () -> new SHA512Managed())\n", | |
| " #endif\n", | |
| "\n", | |
| " let toHex (bs: byte[]) =\n", | |
| " BitConverter.ToString bs\n", | |
| " |> String.replace \"-\" \"\"\n", | |
| " |> String.toLowerInvariant\n", | |
| "\n", | |
| " let ofHex (digestString: string) =\n", | |
| " Enumerable.Range(0, digestString.Length)\n", | |
| " .Where(fun x -> x % 2 = 0)\n", | |
| " .Select(fun x -> Convert.ToByte(digestString.Substring(x, 2), 16))\n", | |
| " .ToArray()\n", | |
| "\n", | |
| " /// Compare two byte arrays in constant time, bounded by the length of the\n", | |
| " /// longest byte array.\n", | |
| " let equalsConstantTime (bits: byte []) (bobs: byte []) =\n", | |
| " let mutable xx = uint32 bits.Length ^^^ uint32 bobs.Length\n", | |
| " let mutable i = 0\n", | |
| " while i < bits.Length && i < bobs.Length do\n", | |
| " xx <- xx ||| uint32 (bits.[i] ^^^ bobs.[i])\n", | |
| " i <- i + 1\n", | |
| " xx = 0u\n", | |
| "\n", | |
| " [<RequireQualifiedAccess>]\n", | |
| " module Culture =\n", | |
| " open System.Globalization\n", | |
| "\n", | |
| " let invariant = CultureInfo.InvariantCulture\n", | |
| "\n", | |
| " module UTF8 =\n", | |
| " open System.Text\n", | |
| "\n", | |
| " let private utf8 = Encoding.UTF8\n", | |
| "\n", | |
| " /// Convert the full buffer `b` filled with UTF8-encoded strings into a CLR\n", | |
| " /// string.\n", | |
| " let toString (bs: byte []) =\n", | |
| " utf8.GetString bs\n", | |
| "\n", | |
| " /// Convert the byte array to a string, by indexing into the passed buffer `b`\n", | |
| " /// and taking `count` bytes from it.\n", | |
| " let toStringAtOffset (b: byte []) (index: int) (count: int) =\n", | |
| " utf8.GetString(b, index, count)\n", | |
| "\n", | |
| " /// Get the UTF8-encoding of the string.\n", | |
| " let bytes (s: string) =\n", | |
| " utf8.GetBytes s\n", | |
| "\n", | |
| " /// Convert the passed string `s` to UTF8 and then encode the buffer with\n", | |
| " /// base64.\n", | |
| " let encodeBase64: string -> Base64String =\n", | |
| " bytes >> Convert.ToBase64String\n", | |
| "\n", | |
| " /// Convert the passed string `s`, assumed to be a valid Base64 encoding, to a\n", | |
| " /// CLR string, going through UTF8.\n", | |
| " let decodeBase64: Base64String -> string =\n", | |
| " Convert.FromBase64String >> toString\n", | |
| "\n", | |
| " let sha1 =\n", | |
| " bytes >> Bytes.sha1\n", | |
| "\n", | |
| " let sha1Hex =\n", | |
| " bytes >> Bytes.sha1 >> Bytes.toHex\n", | |
| "\n", | |
| " let sha256 =\n", | |
| " bytes >> Bytes.sha256\n", | |
| "\n", | |
| " let sha256Hex =\n", | |
| " bytes >> Bytes.sha256 >> Bytes.toHex\n", | |
| "\n", | |
| " let sha512 =\n", | |
| " bytes >> Bytes.sha512\n", | |
| "\n", | |
| " let sha512Hex =\n", | |
| " bytes >> Bytes.sha512 >> Bytes.toHex\n", | |
| "\n", | |
| " module Comparisons =\n", | |
| "\n", | |
| " /// compare x to yobj mapped on selected value from function f\n", | |
| " let compareOn f x (yobj: obj) =\n", | |
| " match yobj with\n", | |
| " | :? 'T as y -> compare (f x) (f y)\n", | |
| " | _ -> invalidArg \"yobj\" \"cannot compare values of different types\"\n", | |
| "\n", | |
| " /// check equality on x and y mapped on selected value from function f\n", | |
| " let equalsOn f x (yobj:obj) =\n", | |
| " match yobj with\n", | |
| " | :? 'T as y -> (f x = f y)\n", | |
| " | _ -> false\n", | |
| "\n", | |
| " /// hash x on the selected value from f\n", | |
| " let hashOn f x = hash (f x)\n", | |
| "\n", | |
| " type Random with\n", | |
| " /// generate a new random ulong64 value\n", | |
| " member x.NextUInt64() =\n", | |
| " let buffer = Array.zeroCreate<byte> sizeof<UInt64>\n", | |
| " x.NextBytes buffer\n", | |
| " BitConverter.ToUInt64(buffer, 0)\n", | |
| "\n", | |
| " module Array =\n", | |
| "\n", | |
| " /// Ordinally compare two arrays in constant time, bounded by the length of the\n", | |
| " /// longest array. This function uses the F# language equality.\n", | |
| " let equalsConstantTime (arr1: 'a []) (arr2: 'a []) =\n", | |
| " if arr1.Length <> arr2.Length then false else\n", | |
| " let mutable b = true\n", | |
| " for i in 0 .. arr1.Length - 1 do\n", | |
| " b <- b && (arr1.[i] = arr2.[i])\n", | |
| " b\n", | |
| "\n", | |
| " /// Returns a sequence that yields chunks of length n.\n", | |
| " /// Each chunk is returned as an array.\n", | |
| " /// Thanks to\n", | |
| " /// https://nbevans.wordpress.com/2014/03/13/really-simple-way-to-split-a-f-sequence-into-chunks-partitions/\n", | |
| " let chunk (n: uint32) (s: seq<'t>) = seq {\n", | |
| " let n = int n\n", | |
| " let pos = ref 0\n", | |
| " let buffer = Array.zeroCreate<'t> n\n", | |
| "\n", | |
| " for x in s do\n", | |
| " buffer.[!pos] <- x\n", | |
| " if !pos = n - 1 then\n", | |
| " yield buffer |> Array.copy\n", | |
| " pos := 0\n", | |
| " else\n", | |
| " incr pos\n", | |
| "\n", | |
| " if !pos > 0 then\n", | |
| " yield Array.sub buffer 0 !pos\n", | |
| " }\n", | |
| "\n", | |
| " module Regex =\n", | |
| " open System.Text.RegularExpressions\n", | |
| "\n", | |
| " type RegexMatch = Match\n", | |
| "\n", | |
| " let escape input =\n", | |
| " Regex.Escape input\n", | |
| "\n", | |
| " let split pattern input =\n", | |
| " Regex.Split(input, pattern)\n", | |
| " |> List.ofArray\n", | |
| "\n", | |
| " let replace pattern replacement input =\n", | |
| " Regex.Replace(input, pattern, (replacement: string))\n", | |
| "\n", | |
| " let replaceWithFunction pattern (replaceFunc: RegexMatch -> string) input =\n", | |
| " Regex.Replace(input, pattern, replaceFunc)\n", | |
| "\n", | |
| " /// Match the `input` against the regex `pattern`. You can do a\n", | |
| " /// `Seq.cast<Group>` on the result to get it as a sequence\n", | |
| " /// and also index with `.[\"name\"]` into the result if you have\n", | |
| " /// named capture groups.\n", | |
| " let ``match`` pattern input =\n", | |
| " match Regex.Matches(input, pattern) with\n", | |
| " | x when x.Count > 0 ->\n", | |
| " x\n", | |
| " |> Seq.cast<Match>\n", | |
| " |> Seq.head\n", | |
| " |> fun x -> x.Groups\n", | |
| " |> Some\n", | |
| " | _ -> None\n", | |
| "\n", | |
| " type Microsoft.FSharp.Control.Async with\n", | |
| " /// Raise an exception on the async computation/workflow.\n", | |
| " static member AsyncRaise (e: exn) =\n", | |
| " Async.FromContinuations(fun (_,econt,_) -> econt e)\n", | |
| "\n", | |
| " /// Await a task asynchronously\n", | |
| " static member AwaitTask (t: Task) =\n", | |
| " let flattenExns (e: AggregateException) = e.Flatten().InnerExceptions.[0]\n", | |
| " let rewrapAsyncExn (it: Async<unit>) =\n", | |
| " async { try do! it with :? AggregateException as ae -> do! Async.AsyncRaise (flattenExns ae) }\n", | |
| " let tcs = new TaskCompletionSource<unit>(TaskCreationOptions.None)\n", | |
| " t.ContinueWith((fun t' ->\n", | |
| " if t.IsFaulted then tcs.SetException(t.Exception |> flattenExns)\n", | |
| " elif t.IsCanceled then tcs.SetCanceled ()\n", | |
| " else tcs.SetResult(())), TaskContinuationOptions.ExecuteSynchronously)\n", | |
| " |> ignore\n", | |
| " tcs.Task |> Async.AwaitTask |> rewrapAsyncExn\n", | |
| "\n", | |
| " type Microsoft.FSharp.Control.AsyncBuilder with\n", | |
| " /// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on\n", | |
| " /// a standard .NET task\n", | |
| " member x.Bind(t: Task<'T>, f:'T -> Async<'R>): Async<'R> =\n", | |
| " async.Bind(Async.AwaitTask t, f)\n", | |
| "\n", | |
| " /// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on\n", | |
| " /// a standard .NET task which does not commpute a value\n", | |
| " member x.Bind(t: Task, f: unit -> Async<'R>): Async<'R> =\n", | |
| " async.Bind(Async.AwaitTask t, f)\n", | |
| "\n", | |
| " module Async =\n", | |
| "\n", | |
| " let result = async.Return\n", | |
| "\n", | |
| " let map f value = async {\n", | |
| " let! v = value\n", | |
| " return f v\n", | |
| " }\n", | |
| "\n", | |
| " let bind f xAsync = async {\n", | |
| " let! x = xAsync\n", | |
| " return! f x\n", | |
| " }\n", | |
| "\n", | |
| " let withTimeout timeoutMillis operation =\n", | |
| " async {\n", | |
| " let! child = Async.StartChild(operation, timeoutMillis)\n", | |
| " try\n", | |
| " let! result = child\n", | |
| " return Some result\n", | |
| " with :? TimeoutException ->\n", | |
| " return None\n", | |
| " }\n", | |
| "\n", | |
| " let apply fAsync xAsync = async {\n", | |
| " // start the two asyncs in parallel\n", | |
| " let! fChild = Async.StartChild fAsync\n", | |
| " let! xChild = Async.StartChild xAsync\n", | |
| "\n", | |
| " // wait for the results\n", | |
| " let! f = fChild\n", | |
| " let! x = xChild\n", | |
| "\n", | |
| " // apply the function to the results\n", | |
| " return f x\n", | |
| " }\n", | |
| "\n", | |
| " let lift2 f x y =\n", | |
| " apply (apply (result f) x) y\n", | |
| "\n", | |
| " let lift3 f x y z =\n", | |
| " apply (apply (apply (result f) x) y) z\n", | |
| "\n", | |
| " let lift4 f x y z a =\n", | |
| " apply (apply (apply (apply (result f) x) y) z) a\n", | |
| "\n", | |
| " let lift5 f x y z a b =\n", | |
| " apply (apply (apply (apply (apply (result f) x) y) z) a) b\n", | |
| "\n", | |
| " module Operators =\n", | |
| "\n", | |
| " let inline (>>=) m f =\n", | |
| " bind f m\n", | |
| "\n", | |
| " let inline (=<<) f m =\n", | |
| " bind f m\n", | |
| "\n", | |
| " let inline (<*>) f m =\n", | |
| " apply f m\n", | |
| "\n", | |
| " let inline (<!>) f m =\n", | |
| " map f m\n", | |
| "\n", | |
| " let inline ( *>) m1 m2 =\n", | |
| " lift2 (fun _ x -> x) m1 m2\n", | |
| "\n", | |
| " let inline ( <*) m1 m2 =\n", | |
| " lift2 (fun x _ -> x) m1 m2\n", | |
| "\n", | |
| " /// Stacks the Async Result monad.\n", | |
| " /// All functions from Yolo's Result are implemented, so it's easy to replace it.\n", | |
| " module AsyncResult =\n", | |
| "\n", | |
| " // https://fsharpforfunandprofit.com/posts/elevated-world-5/#asynclist\n", | |
| "\n", | |
| " let ok v = v |> Ok |> Async.result\n", | |
| "\n", | |
| " let error v = v |> Error |> Async.result\n", | |
| "\n", | |
| " let map f x =\n", | |
| " f |> Result.map |> Async.map <| x\n", | |
| "\n", | |
| " let map2 f1 f2 x =\n", | |
| " Result.map2 f1 f2 |> Async.map <| x\n", | |
| "\n", | |
| " let mapError f x =\n", | |
| " Result.mapError f |> Async.map <| x\n", | |
| "\n", | |
| " let fold f g x =\n", | |
| " Result.fold f g |> Async.map <| x\n", | |
| "\n", | |
| " let bind f xAR = async {\n", | |
| " let! x = xAR\n", | |
| " match x with\n", | |
| " | Ok x -> return! f x\n", | |
| " | Error e -> return Error e\n", | |
| " }\n", | |
| "\n", | |
| " let bindError f xAR = async {\n", | |
| " let! x = xAR\n", | |
| " match x with\n", | |
| " | Ok x -> return Ok x\n", | |
| " | Error e -> return! f e\n", | |
| " }\n", | |
| "\n", | |
| " let bindResult f xAR = async {\n", | |
| " let! x = xAR\n", | |
| " match x with\n", | |
| " | Ok x -> return f x\n", | |
| " | Error e -> return e |> Error\n", | |
| " }\n", | |
| "\n", | |
| " let bindAsync f xAR = async {\n", | |
| " let! x = xAR\n", | |
| " match x with\n", | |
| " | Ok x -> return! f >> Async.Catch >> Async.map Result.ofChoice <| x\n", | |
| " | Error e -> return e |> Error\n", | |
| " }\n", | |
| "\n", | |
| " let apply f x =\n", | |
| " f |> Async.bind (fun fR ->\n", | |
| " x |> Async.map (fun xR ->\n", | |
| " Result.apply fR xR))\n", | |
| "\n", | |
| " let applyResult f x =\n", | |
| " f |> Result.apply |> Async.map <| x\n", | |
| "\n", | |
| " let lift2 f v1 v2 =\n", | |
| " apply (apply (ok f) v1) v2\n", | |
| "\n", | |
| " let lift3 f v1 v2 v3 =\n", | |
| " apply (apply (apply (ok f) v1) v2) v3\n", | |
| "\n", | |
| " let lift4 f v1 v2 v3 v4 =\n", | |
| " apply (apply (apply (apply (ok f) v1) v2) v3) v4\n", | |
| "\n", | |
| " let lift5 f v1 v2 v3 v4 v5 =\n", | |
| " apply (apply (apply (apply (apply (ok f) v1) v2) v3) v4) v5\n", | |
| "\n", | |
| " let ofOption onMissing = function\n", | |
| " | Some x -> ok x\n", | |
| " | None -> error onMissing\n", | |
| "\n", | |
| " let ofAsyncOption onMissing x =\n", | |
| " onMissing |> ofOption |> Async.bind <| x\n", | |
| "\n", | |
| " let toChoice x =\n", | |
| " fold Choice1Of2 Choice2Of2 <| x\n", | |
| "\n", | |
| " let ofChoice = function\n", | |
| " | Choice1Of2 x -> ok x\n", | |
| " | Choice2Of2 x -> error x\n", | |
| "\n", | |
| " let ofAsyncChoice x =\n", | |
| " ofChoice |> Async.bind <| x\n", | |
| "\n", | |
| " let inject f x =\n", | |
| " map ( fun x -> f x; x ) x\n", | |
| "\n", | |
| " let injectError f x =\n", | |
| " map2 id ( fun x -> f x; x ) x\n", | |
| "\n", | |
| " // we love syntatic suggar.\n", | |
| " module Operators =\n", | |
| "\n", | |
| " let inline (>>=) m f =\n", | |
| " bind f m\n", | |
| "\n", | |
| " let inline (>>-) m f = // snd\n", | |
| " bindError f m\n", | |
| "\n", | |
| " let inline (=<<) f m =\n", | |
| " bind f m\n", | |
| "\n", | |
| " //vscode/ionide has a highlighter bug, so we use comment (***) to fix it\n", | |
| " let inline (-<< (***) ) f m = // snd\n", | |
| " bindError f m\n", | |
| "\n", | |
| " let inline (>>*) m f =\n", | |
| " inject f m\n", | |
| "\n", | |
| " let inline (>>@) m f = // snd\n", | |
| " injectError f m\n", | |
| "\n", | |
| " let inline (<*>) f m =\n", | |
| " apply f m\n", | |
| "\n", | |
| " let inline (<!>) f m =\n", | |
| " map f m\n", | |
| "\n", | |
| " let inline (>!>) m f =\n", | |
| " map f m\n", | |
| "\n", | |
| " let inline (<@>) f m = // snd\n", | |
| " mapError f m\n", | |
| "\n", | |
| " let inline (>@>) m f = // snd\n", | |
| " mapError f m\n", | |
| "\n", | |
| " let inline ( *>) m1 m2 =\n", | |
| " lift2 (fun _ x -> x) m1 m2\n", | |
| "\n", | |
| " let inline ( <*) m1 m2 =\n", | |
| " lift2 (fun x _ -> x) m1 m2\n", | |
| "\n", | |
| " module List =\n", | |
| "\n", | |
| " /// Split xs at n, into two lists, or where xs ends if xs.Length < n.\n", | |
| " let split n xs =\n", | |
| " let rec splitUtil n xs acc =\n", | |
| " match xs with\n", | |
| " | [] -> List.rev acc, []\n", | |
| " | _ when n = 0u -> List.rev acc, xs\n", | |
| " | x::xs' -> splitUtil (n - 1u) xs' (x::acc)\n", | |
| " splitUtil n xs []\n", | |
| "\n", | |
| " /// Chunk a list into pageSize large chunks\n", | |
| " let chunk pageSize = function\n", | |
| " | [] -> None\n", | |
| " | l -> let h, t = l |> split pageSize in Some(h, t)\n", | |
| "\n", | |
| " let first = function\n", | |
| " | [] -> None\n", | |
| " | x :: _ -> Some x\n", | |
| "\n", | |
| " // Description of the below functions:\n", | |
| " // http://fsharpforfunandprofit.com/posts/elevated-world-5/#asynclist\n", | |
| "\n", | |
| " /// Map a Async producing function over a list to get a new Async using\n", | |
| " /// applicative style. ('a -> Async<'b>) -> 'a list -> Async<'b list>\n", | |
| " let rec traverseAsyncA f list =\n", | |
| " let (<*>) = Async.apply\n", | |
| " let cons head tail = head :: tail\n", | |
| " let initState = Async.result []\n", | |
| " let folder head tail =\n", | |
| " Async.result cons <*> (f head) <*> tail\n", | |
| "\n", | |
| " List.foldBack folder list initState\n", | |
| "\n", | |
| " /// Transform a \"list<Async>\" into a \"Async<list>\" and collect the results\n", | |
| " /// using apply.\n", | |
| " let sequenceAsyncA x = traverseAsyncA id x\n", | |
| "\n", | |
| " /// Map a Choice-producing function over a list to get a new Choice using\n", | |
| " /// applicative style. ('a -> Choice<'b, 'c>) -> 'a list -> Choice<'b list, 'c>\n", | |
| " let rec traverseChoiceA f list =\n", | |
| " let (<*>) = Choice.apply\n", | |
| " let cons head tail = head :: tail\n", | |
| "\n", | |
| " // right fold over the list\n", | |
| " let initState = Choice.create []\n", | |
| " let folder head tail =\n", | |
| " Choice.create cons <*> (f head) <*> tail\n", | |
| "\n", | |
| " List.foldBack folder list initState\n", | |
| "\n", | |
| " /// Transform a \"list<Choice>\" into a \"Choice<list>\" and collect the results\n", | |
| " /// using apply.\n", | |
| " let sequenceChoiceA x = traverseChoiceA id x\n", | |
| "\n", | |
| " /// Map a Result-producing function over a list to get a new Result using\n", | |
| " /// applicative style. ('a -> Result<'b, 'c>) -> 'a list -> Result<'b list, 'c>\n", | |
| " let rec traverseResultA f list =\n", | |
| " let (<*>) = Result.apply\n", | |
| " let cons head tail = head :: tail\n", | |
| "\n", | |
| " // right fold over the list\n", | |
| " let initState = Result.Ok []\n", | |
| " let folder head tail =\n", | |
| " Result.Ok cons <*> (f head) <*> tail\n", | |
| "\n", | |
| " List.foldBack folder list initState\n", | |
| "\n", | |
| " /// Transform a \"list<Result>\" into a \"Result<list>\" and collect the results\n", | |
| " /// using apply.\n", | |
| " let sequenceResultA x = traverseResultA id x\n", | |
| "\n", | |
| " /// Map an AsyncResult producing function over a list to get a new AsyncResult using\n", | |
| " /// applicative style. ('a -> Async<Result<'b, 'c>>) -> 'a list -> Async<Result<'b list, 'c>>\n", | |
| " let rec traverseAsyncResultA f list =\n", | |
| " let (<*>) = AsyncResult.apply\n", | |
| " let cons head tail = head :: tail\n", | |
| "\n", | |
| " // right fold over the list\n", | |
| " let initState = AsyncResult.ok []\n", | |
| " let folder head tail =\n", | |
| " AsyncResult.ok cons <*> (f head) <*> tail\n", | |
| "\n", | |
| " List.foldBack folder list initState\n", | |
| "\n", | |
| " /// Transform a \"list<Async<Result<'a, 'b>>\" into a \"Async<Result<'a list, 'b>>\" and collect the results\n", | |
| " /// using apply.\n", | |
| " let sequenceAsyncResultA x = traverseAsyncResultA id x\n", | |
| "\n", | |
| " /// Map an AsyncResult producing function over a list to get a new AsyncResult\n", | |
| " /// using monadic style. ('a -> Async<Result<'b, 'c>>) -> 'a list -> Async<Result<'b list, 'c>>\n", | |
| " let rec traverseAsyncResultM f list =\n", | |
| " let (<*>) = AsyncResult.apply\n", | |
| " let (>>=) m f = AsyncResult.bind f m\n", | |
| " let cons head tail = head :: tail\n", | |
| "\n", | |
| " let initState = AsyncResult.ok []\n", | |
| " let folder head tail =\n", | |
| " f head >>= (fun h ->\n", | |
| " tail >>= (fun t ->\n", | |
| " AsyncResult.ok (cons h t) ))\n", | |
| " List.foldBack folder list initState\n", | |
| "\n", | |
| " /// Transform a \"list<Async<Result<'a, 'b>>\" into a \"Async<Result<'a list, 'b>>\" and collect the results\n", | |
| " /// using bind.\n", | |
| " let sequenceAsyncResultM x = traverseAsyncResultM id x\n", | |
| "\n", | |
| " module Seq =\n", | |
| "\n", | |
| " let combinations size set =\n", | |
| " let rec combinations' acc size set =\n", | |
| " seq {\n", | |
| " match size, set with\n", | |
| " | n, x::xs ->\n", | |
| " if n > 0 then yield! combinations' (x::acc) (n - 1) xs\n", | |
| " if n >= 0 then yield! combinations' acc n xs\n", | |
| " | 0, [] -> yield acc\n", | |
| " | _, [] -> ()\n", | |
| " }\n", | |
| " combinations' [] size set\n", | |
| "\n", | |
| " let first (xs: _ seq): _ option =\n", | |
| " if Seq.isEmpty xs then None else Seq.head xs |> Some\n", | |
| "\n", | |
| " module Env =\n", | |
| "\n", | |
| " let var (k: string) =\n", | |
| " let v = Environment.GetEnvironmentVariable k\n", | |
| " if isNull v then None else Some v\n", | |
| "\n", | |
| " let varParse parse (k: string) =\n", | |
| " var k |> Option.map parse\n", | |
| "\n", | |
| " let varDefault (key: String) (getDefault: unit -> string) =\n", | |
| " match var key with\n", | |
| " | Some v -> v\n", | |
| " | None -> getDefault ()\n", | |
| "\n", | |
| " let varDefaultParse parse (key: string) getDefault =\n", | |
| " varDefault key getDefault |> parse\n", | |
| "\n", | |
| " let varRequired (k: String) =\n", | |
| " match var k with\n", | |
| " | Some v -> v\n", | |
| " | None -> failwithf \"The environment variable '%s' is missing.\" k\n", | |
| "\n", | |
| " module App =\n", | |
| "\n", | |
| " open System.IO\n", | |
| " open System.Reflection\n", | |
| "\n", | |
| " /// Gets the calling assembly's informational version number as a string\n", | |
| " let getVersion () =\n", | |
| " #if DNXCORE50\n", | |
| " (typeof<Random>.GetTypeInfo().Assembly)\n", | |
| " #else\n", | |
| " Assembly.GetCallingAssembly()\n", | |
| " #endif\n", | |
| " .GetCustomAttribute<AssemblyInformationalVersionAttribute>()\n", | |
| " .InformationalVersion\n", | |
| "\n", | |
| " /// Get the assembly resource\n", | |
| " let resourceIn (assembly: Assembly) name =\n", | |
| " use stream = assembly.GetManifestResourceStream name\n", | |
| " if stream = null then\n", | |
| " assembly.GetManifestResourceNames()\n", | |
| " |> Array.fold (fun s t -> sprintf \"%s\\n - %s\" s t) \"\"\n", | |
| " |> sprintf \"couldn't find resource named '%s', from: %s\" name\n", | |
| " |> Choice2Of2\n", | |
| " else\n", | |
| " use reader = new StreamReader(stream)\n", | |
| " reader.ReadToEnd ()\n", | |
| " |> Choice1Of2\n", | |
| "\n", | |
| " /// Get the current assembly resource\n", | |
| " let resource =\n", | |
| " #if DNXCORE50\n", | |
| " let assembly = typeof<Random>.GetTypeInfo().Assembly\n", | |
| " #else\n", | |
| " let assembly = Assembly.GetExecutingAssembly ()\n", | |
| " #endif\n", | |
| " resourceIn assembly\n", | |
| "\n", | |
| " module Dictionary =\n", | |
| " open System.Collections.Generic\n", | |
| " \n", | |
| " /// Attempts to retrieve a value as an option from a dictionary using the provided key\n", | |
| " let tryFind key (dict: Dictionary<_, _>) =\n", | |
| " match dict.TryGetValue key with\n", | |
| " | true, value -> Some value\n", | |
| " | _ -> None\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "# Either Monad variants" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "fsharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "fsharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "type Option<'a> =\n", | |
| " | Some of 'a\n", | |
| " | None\n", | |
| "\n", | |
| "type Result<'a,'b> =\n", | |
| " | Ok of 'a\n", | |
| " | Error of 'b\n", | |
| "\n", | |
| "type Choice1of2<'a,'b> =\n", | |
| " | Choice1 of 'a\n", | |
| " | Choice2 of 'b\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "# Translating LINQ verbs to traditional functional names\n", | |
| "\n", | |
| "| Linq or Async Operation in C# | Functional Operation | Functional Signature |\n", | |
| "|---|---|---|\n", | |
| "| `Select` | `map` | `('a -> 'b) -> Option<'a> -> Option<'b>` |\n", | |
| "| ? | apply | Option< a' -> b'> -> Option<'a> -> Option<'b> |\n", | |
| "| task.ContinueWith | bind | (a' -> Task<'a>) -> Task<'a> -> Task<'b> |\n", | |
| "| await | let! | (a' -> Task<'a>) -> Task<'a> -> Task<'b> |" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "## Map and Apply" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "fsharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "fsharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "let a = Some 1\n", | |
| "let b = Some 2\n", | |
| "let a2 = a |> Option.map (fun x -> x + 1)\n", | |
| "let add2 = Option.map (fun x -> x + 2)\n", | |
| "let add = Option.map2 (+)\n", | |
| "let addOption = Some (fun x -> x + 3)\n", | |
| "let add3 = Option.apply addOption\n", | |
| "let c = add a b\n", | |
| "printfn \"c = %O\" c\n", | |
| "\n", | |
| "\n", | |
| "let addToOption x = x + 4 |> Some\n", | |
| "let add4 = Option.bind addToOption\n", | |
| "\n", | |
| "let d = c |> Option.defaultValue 0\n", | |
| "d\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "using System.Collections.Generic;\n", | |
| "using System.Linq;\n", | |
| "\n", | |
| "var intArray = new [] { 1, 2, 3 };\n", | |
| "\n", | |
| "IEnumerable<int> Map (Func<int,int> f, IEnumerable<int> xs)\n", | |
| "{\n", | |
| " return xs.Select(f);\n", | |
| "}\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "Experimenting with Option" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "## Bind" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "using System;\n", | |
| "using System.Threading.Tasks;\n", | |
| "\n", | |
| "// this is a \"bind\" function created similar to the YoLo Async.bind\n", | |
| "async Task<U> bind<T,U> (Func<T,Task<U>> f, Task<T> x)\n", | |
| "{\n", | |
| " var x2 = await x;\n", | |
| " var y = await f(x2);\n", | |
| " return y;\n", | |
| "}\n", | |
| "\n", | |
| "// overriding the \"bind\" function, as we can't use partial application like we do in F# later\n", | |
| "Func<Task<T>,Task<U>> bind<T,U> (Func<T,Task<U>> f)\n", | |
| "{\n", | |
| " Func<Task<T>,Task<U>> f2 = default(Func<Task<T>,Task<U>>);\n", | |
| " f2 = async x =>\n", | |
| " {\n", | |
| " var x2 = await x;\n", | |
| " var y = await f(x2); \n", | |
| " return y;\n", | |
| " };\n", | |
| " return f2;\n", | |
| "}\n", | |
| "\n", | |
| "// this is an \"apply\" function created similar to the YoLo Async.apply\n", | |
| "async Task<U> apply<T,U> (Task<Func<T,U>> fAsync, Task<T> xAsync)\n", | |
| "{\n", | |
| " var f = await fAsync;\n", | |
| " var x = await xAsync;\n", | |
| " return f(x);\n", | |
| "}\n", | |
| "\n", | |
| "// overriding the \"apply\" function, as we can't use partial application like we do in F# later\n", | |
| "Func<Task<T>,Task<U>> apply<T,U> (Task<Func<T,U>> fAsync)\n", | |
| "{\n", | |
| " Func<Task<T>,Task<U>> f2 = default(Func<Task<T>,Task<U>>);\n", | |
| " f2 = async x =>\n", | |
| " {\n", | |
| " var f = await fAsync;\n", | |
| " var x2 = await x;\n", | |
| " var y = f(x2); \n", | |
| " return y;\n", | |
| " };\n", | |
| " return f2;\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "string add (int x)\n", | |
| "{\n", | |
| " return (x + 1).ToString();\n", | |
| "}\n", | |
| "\n", | |
| "Func<int,Task<string>> addAsync = x =>\n", | |
| "{\n", | |
| " var y = add(x);\n", | |
| " return Task.FromResult(y);\n", | |
| "};\n", | |
| "\n", | |
| "int mult (string x)\n", | |
| "{\n", | |
| " return Int32.Parse(x) * 2;\n", | |
| "}\n", | |
| "\n", | |
| "Func<string,Task<int>> multAsync = y =>\n", | |
| "{\n", | |
| " var z = mult(y);\n", | |
| " return Task.FromResult(z);\n", | |
| "};\n", | |
| "\n", | |
| "// using the \"bind\" function\n", | |
| "Func<int,Task<int>> addAndMultAsync = x =>\n", | |
| "{\n", | |
| " var y = addAsync(x);\n", | |
| " return bind(multAsync,y);\n", | |
| "};\n", | |
| "\n", | |
| "var boundAddAsync = bind(addAsync);\n", | |
| "var boundMultAsync = bind(multAsync);\n", | |
| "\n", | |
| "Func<int,Task<int>> addAndMultAsync2 = x =>\n", | |
| "{\n", | |
| " var y = addAsync(x);\n", | |
| " return boundMultAsync(y);\n", | |
| "};\n", | |
| "\n", | |
| "Func<int,Task<int>> addAndMultAsync3 = x =>\n", | |
| "{\n", | |
| " return boundMultAsync(addAsync(x));\n", | |
| "};\n", | |
| "\n", | |
| "async Task<int> addAndMultAsync4 (int x)\n", | |
| "{\n", | |
| " var y = await\n", | |
| " addAsync(x)\n", | |
| " .ContinueWith(y => multAsync(y.Result)).Unwrap();\n", | |
| " return y;\n", | |
| "}\n", | |
| "\n", | |
| "int addAndMult (int x)\n", | |
| "{\n", | |
| " return mult(add(x));\n", | |
| "}\n", | |
| "\n", | |
| "Func<int, int> addAndMultFunc = x => addAndMult(x);\n", | |
| "var addAndMultFuncAsync = Task.FromResult(addAndMultFunc);\n", | |
| "var liftedAddAndMult = apply(addAndMultFuncAsync);\n", | |
| "\n", | |
| "async Task<(int,int,int,int,int)> checkAll ()\n", | |
| "{\n", | |
| " var one = await addAndMultAsync(4);\n", | |
| " var two = await addAndMultAsync2(4);\n", | |
| " var three = await addAndMultAsync3(4);\n", | |
| " var four = await addAndMultAsync4(4);\n", | |
| " var five = await liftedAddAndMult(Task.FromResult(4));\n", | |
| " return (one,two,three,four,five);\n", | |
| "}\n", | |
| "\n", | |
| "var mainTask = checkAll();\n", | |
| "\n", | |
| "mainTask.Wait();\n", | |
| "\n", | |
| "return mainTask.Result;" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "fsharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "fsharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "let add (a: int) = a + 1 |> string\n", | |
| "let addAsync = add >> Async.result\n", | |
| "\n", | |
| "let mult (a: string) = a |> System.Int32.Parse |> (*) 2\n", | |
| "let multAsync = mult >> Async.result\n", | |
| "\n", | |
| "let addAndMultAsync (a: int) =\n", | |
| " let x = addAsync a\n", | |
| " x |> Async.bind (fun x' -> multAsync x')\n", | |
| "\n", | |
| "let boundAddAsync = Async.bind addAsync\n", | |
| "let boundMultAsync = Async.bind multAsync\n", | |
| "let addAndMultAsync2 (a:int) = \n", | |
| " let x = addAsync a\n", | |
| " boundMultAsync x\n", | |
| "\n", | |
| "let addAndMultAsync3 = addAsync >> boundMultAsync\n", | |
| "\n", | |
| "let addAndMult = add >> mult\n", | |
| "\n", | |
| "let liftedAddAndMult = addAndMult |> Async.result |> Async.apply\n", | |
| "\n", | |
| "let liftedAddAndMult = Async.map addAndMult\n", | |
| "\n", | |
| "async {\n", | |
| " let! one = 4 |> addAndMultAsync\n", | |
| " let! two = 4 |> addAndMultAsync2\n", | |
| " let! three = 4 |> addAndMultAsync3\n", | |
| " let! four = 4 |> Async.result |> liftedAddAndMult\n", | |
| " return (one, two, three, four)\n", | |
| "}\n", | |
| "|> Async.RunSynchronously\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "csharp" | |
| } | |
| }, | |
| "source": [ | |
| "# Serialization" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "dotnet_interactive": { | |
| "language": "fsharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelName": "fsharp" | |
| }, | |
| "vscode": { | |
| "languageId": "polyglot-notebook" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "#!fsharp\n", | |
| "#r \"nuget: Newtonsoft.Json\"\n", | |
| "open Newtonsoft.Json\n", | |
| "\n", | |
| "type Foo =\n", | |
| " { F1: string\n", | |
| " F2: int }\n", | |
| "\n", | |
| "type Bar =\n", | |
| " { B1: int\n", | |
| " B2: string }\n", | |
| "\n", | |
| "type Baz =\n", | |
| " | Fooey of Foo\n", | |
| " | Bart of Bar\n", | |
| "\n", | |
| "let data =\n", | |
| " [ Fooey { F1 = \"bleh\"; F2 = 1 }\n", | |
| " Bart { B1 = 0; B2 = \"meh\" } ]\n", | |
| "JsonConvert.SerializeObject(data)" | |
| ] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": ".NET (C#)", | |
| "language": "C#", | |
| "name": ".net-csharp" | |
| }, | |
| "polyglot_notebook": { | |
| "kernelInfo": { | |
| "defaultKernelName": "csharp", | |
| "items": [ | |
| { | |
| "aliases": [ | |
| "c#", | |
| "C#" | |
| ], | |
| "languageName": "C#", | |
| "name": "csharp" | |
| }, | |
| { | |
| "aliases": [ | |
| "frontend" | |
| ], | |
| "languageName": null, | |
| "name": "vscode" | |
| }, | |
| { | |
| "aliases": [ | |
| "js" | |
| ], | |
| "languageName": "JavaScript", | |
| "name": "javascript" | |
| }, | |
| { | |
| "aliases": [], | |
| "name": "webview" | |
| }, | |
| { | |
| "aliases": [], | |
| "languageName": null, | |
| "name": ".NET" | |
| } | |
| ] | |
| } | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 2 | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO:
Task<T>andtask {}or add some information aboutAsyncbeing cold/lazy vsTask<T>being hot start.