{"id":22148396,"url":"https://github.com/dburriss/elevatedexamples","last_synced_at":"2025-10-29T05:17:47.650Z","repository":{"id":77370715,"uuid":"114535257","full_name":"dburriss/ElevatedExamples","owner":"dburriss","description":"I contain examples in C# and F# of functional programming.","archived":false,"fork":false,"pushed_at":"2018-08-07T21:40:06.000Z","size":20,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T12:48:44.155Z","etag":null,"topics":["csharp","fsharp","functional-programming","language-ext"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dburriss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-17T12:43:02.000Z","updated_at":"2019-04-12T01:10:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"d633e147-dae0-4fe6-9cd7-9c965ea8b591","html_url":"https://github.com/dburriss/ElevatedExamples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dburriss/ElevatedExamples","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburriss%2FElevatedExamples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburriss%2FElevatedExamples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburriss%2FElevatedExamples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburriss%2FElevatedExamples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dburriss","download_url":"https://codeload.github.com/dburriss/ElevatedExamples/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dburriss%2FElevatedExamples/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281563815,"owners_count":26522710,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-29T02:00:06.901Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","fsharp","functional-programming","language-ext"],"created_at":"2024-12-01T23:27:36.243Z","updated_at":"2025-10-29T05:17:47.619Z","avatar_url":"https://github.com/dburriss.png","language":"C#","readme":"# Elevated Examples\n\nI contain examples in C# and F# of functional programming. I wrote some example code in F# and then tried to use the same patterns to write the C# code. The C# code makes use of [LanguageExt](https://github.com/louthy/language-ext), a functional helper library for C#.\n\n## Background\n\nEach test case has a workflow that goes something like *Get data* -\u003e *Mutate data* -\u003e *Validate* -\u003e *Log* -\u003e *Map to different type*. The idea is that there are different [elevated types](https://fsharpforfunandprofit.com/posts/elevated-world/) that need to be mapped between to allow for chaining. This is meant to be very practical with only as much theory as needed to understand the code.\n\n- [F# Code](https://github.com/dburriss/ElevatedExamples/blob/master/FExamples/Tests.fs)\n- [C# Code](https://github.com/dburriss/ElevatedExamples/blob/master/LanguageExtExamples/Tests.cs)\n\n### Why use C# for functional programming(FP)\n\nFirst of all FP is not an all or nothing choice. [Michael Feathers](http://michaelfeathers.typepad.com/michael_feathers_blog/2012/03/tell-above-and-ask-below-hybridizing-oo-and-functional-design.html) wrote about using a hybrid approach back in 2012 and others have even further back. Some would even argue that Actor model coupled with FP may be closer to what Alan Kay initially envisaged anyway.\n\nFor me the main benefit is the minimization of moving parts, and so an increase in predictability of the system. This is done in a few ways. The first one is purity of functions. By minimizing the functions that mutate state, and pushing those to the outside, your system becomes both predictable and testable.\n\nNext an emphasis on [honest argument](http://devonburriss.me/honest-arguments/) and [return types](http://devonburriss.me/honest-return-types/) makes functions very explicit about what they do. OOP values encapsulation but often that encapsulation means information on intent and consequence is lost. This makes it hard to reason and predict the behavior of a system. A return result of `Result\u003cMyType option,string\u003e` tells me this function could throw an error, so it is probably an IO type of function. It also tells me the data is optional so it might return no data. Finally the error case will give me a string describing the error. That is explicit, and I can code accordingly without knowing the internal details. With a C# method that throws exceptions I need to know about the internal details to handle the failure modes. That is actually poor encapsulation.\n\nRelated to this but not mentioned explicitly is the idea of immutability. Getting a new instance back rather than mutating state avoids many weird unintended side-effects. Unfortunately this is fairly painful in C# currently.\n\n```csharp\nclass MyType\n{\n    public int Nr { get; private set; }\n    public MyType(int nr) =\u003e Nr = nr;\n    public MyType With(int nr) =\u003e new MyType(nr);\n}\n```\n\n## Examples\n\n- [x] Example of an immutable C# type\n- [ ] Example of an immutable C# list\n- [x] `flip` function parameter order to allow currying\n- [x] `curry` (partial application) functions so only a single parameter needed so the can be chained together\n- [x] Composing a workflow with `Apply` or  pipe `|\u003e` (Note I don't mean composing as F# `\u003e\u003e` but as a concept)\n- [x] Usage of `Tap` (or `tee`) to change a `unit` returning function into a pass-through function\n- [ ] mapError throwing an exception and LangExt Try\n\n## Overview\n\nAlthough not necessary, I defined some function types in F# that formed the definition of the main functions chained together in my use cases. These were useful even when defining the C# implementations.\n\n```fsharp\ntype GetMyTypeFn = int -\u003e Result\u003cMyType option,string\u003e\ntype SetFn = Result\u003cMyType option,string\u003e -\u003e int -\u003e Result\u003cMyType,string\u003e\ntype ValidatePositiveFn = Result\u003cMyType,string\u003e -\u003e Result\u003cMyType,string\u003e\ntype LogFn = Result\u003cMyType,string\u003e -\u003e Result\u003cMyType,string\u003e\ntype ConvertFn = Result\u003cMyType,string\u003e -\u003e Result\u003cMyTypeDescriptor,string\u003e\n```\n\nThe final happy-path workflow in F# then looks like this:\n\n```fsharp\nlet setTo2 = (set |\u003e flip) 2\nlet r = get(1) |\u003e setTo2 |\u003e validate |\u003e convert |\u003e tapLog\n```\n\nand in C#:\n\n```csharp\nFunc\u003cint, Func\u003cOptionalResult\u003cMyType\u003e, Result\u003cMyType\u003e\u003e\u003e currySet = curry(flip(Set));\nvar set2 = currySet(2);\nvar result =\n    Get(1)\n    .Apply(set2)\n    .Apply(Validate)\n    .Apply(Convert);\n```\n\nAs you can see from the definition of `SetFn`, the `int` parameter comes after the elevated `Result` type which would be passed through when chaining. So to use partial application we need to flip the parameter order.\n\n## A note on Elevated types (Return)\n\nReturn functions elevate normal values to the elevated world.\n\nSo the first thing to point out is that Elevated Types is not an official thing. Scott introduces the idea on [fsharpforfunandprofit.com](https://fsharpforfunandprofit.com/posts/elevated-world/) to avoid using functional programming terms that can initially be quite overwhelming.\n\nBasically they are types that have some sort of state. The 2 we deal with here are `Option` and `Result`.\n\n- `Option` represents something where data might not be present. Return functions for `Option` are `Some` and `None`.\n- `Result` represents a return type that might be data but instead could also be an error. In F# to lift a value you use `Ok` and `Error`. *Note: in the examples I will often shorten `Result\u003cMyType,string\u003e` to `Result\u003cMyType\u003e` to keep things concise.*\n\nHere is an example in F# of a function that takes in `Result\u003cMyType option,string\u003e` and returns `Result\u003cMyType,string\u003e`\n\n```fsharp\n//Result\u003cMyType option,string\u003e -\u003e Result\u003cMyType,string\u003e\nlet errorIfNone r =\n    match r with\n    | Ok (Some x) -\u003e Ok x\n    | Ok None -\u003e Error \"Not found\"\n    | Error s -\u003e Error s\n```\n\nSo it checks if there is no data and converts that `None` case to an `Error`.\n\nA final note on elevated types in general. It is best to stay in the elevated world as much as possible within your application. If you keep dropping back to normal values to work with them you will experience a lot of friction. Instead of getting values out and working with them it is better to use the techniques outlined in this example to use functions to manipulate the wrapped values within the elevated world. This will not always be possible but it is more often than you initially would think. It does require a change in mindset. Instead of get a piece of data and issuing imperative commands that manipulate it you use functions to declare what you would like to happen and then hand those functions off appropriately.\n\n[See here for further reading on return](https://fsharpforfunandprofit.com/posts/elevated-world/#return)\n\n## Using Map\n\n`map` allows you to apply a function to the normal value inside the elevated type. In the example below we define a function `f` that take a `MyType` and transforms it into `MyTypeDescriptor`. \n\n```fsharp\n//Result\u003cMyType\u003e -\u003e Result\u003cMyTypeDescriptor\u003e\nlet convert : ConvertFn = fun r -\u003e\n    //MyType -\u003e MyTypeDescriptor\n    let f (x:MyType) = x.Nr |\u003e toString |\u003e MyTypeDescriptor.create\n    Result.map f r\n```\n\nSo the function signature is `MyType` -\u003e `MyTypeDescriptor`. Calling `Result.map` with this function and a data value with type `Result\u003cMyType\u003e` will return a value with type `Result\u003cMyTypeDescriptor\u003e`. Thus we have mapped a value from one type to another while staying in the same elevated world.\n\n\u003e Do you see `Map` is the same as LINQ `Select`?\n\n[See here for further reading on map](https://fsharpforfunandprofit.com/posts/elevated-world/#map)\n\n## Using Apply\n\n`apply` is a little different. Apply is used when you have an elevated value and an elevated function. Applying the function which is in terms of elevated types yields an elevated value out. So in the snippet:\n\n```csharp\n...\n.Apply(Validate)\n.Apply(Convert)\n```\n\n`Validate` has the signature `Result\u003cMyType\u003e -\u003e Result\u003cMyType\u003e` and as we saw earlier `Convert` has `Result\u003cMyType\u003e -\u003e Result\u003cMyTypeDescriptor\u003e`.\n\n**`Validate`**\ninput: `Result\u003cMyType\u003e`\noutput: `Result\u003cMyType\u003e`\n\n**`Convert`**\ninput: `Result\u003cMyType\u003e`\noutput: `Result\u003cMyTypeDescriptor\u003e`\n\nAs you can see, the *output* of `Validate` matches the *input* of `Convert`. So `Convert` is a function in elevated `Result` that will take the output value of `Validate` and output the result of that function when applied. All this in the elevated world of `Result`.\n\n[See here for further reading on apply](https://fsharpforfunandprofit.com/posts/elevated-world/#apply)\n\n## Using Bind\n\n`bind` allows us to cross between worlds, moving from normal world to elevated. Where `map` used a function that operated in the normal world like in the `Convert` example of `MyType -\u003e MyTypeDescriptor`, `bind` uses a function that crosses worlds eg. `MyType -\u003e Result\u003cMyType\u003e`.\n\n```fsharp\n//MyType -\u003e Result\u003cMyType\u003e\nlet validateMyTypeIsPositiveR x = if validateMyTypeIsPositive x then Ok x else Error \"Number should not be negative\"\n\n//Result\u003cMyType\u003e -\u003e Result\u003cMyType\u003e\nlet validate r = Result.bind validateMyTypeIsPositiveR r\n```\n\nSo here `validateMyTypeIsPositiveR` is a function that lifts a normal type to an elevated one\n\n\u003e Do you see `Bind` is the same as LINQ `SelectMany`?\n\n[See here for further reading on bind](https://fsharpforfunandprofit.com/posts/elevated-world-2/#bind)\n\n## Summary of Map, Apply, and Bind\n\n**Map**: map internal value to another value  \n**Bind**: For function that takes normal value and returns an elevated type  \n**Apply**: For function that takes an elevated value and can return anything  \n\nExample from [ComplexTests.cs](https://github.com/dburriss/ElevatedExamples/blob/master/LanguageExtExamples/ComplexTests.cs)\n\n```csharp\n[Fact]\npublic void Use_Bind_Instead_Of_ToTry_To_Return_A_Different_Elevated_Type()\n{\n    var toTry = fun\u003cTryOption\u003cPerson\u003e, Try\u003cPerson\u003e\u003e(opt =\u003e opt.Match(\n            Some: x =\u003e Try(x),\n            None: () =\u003e Try\u003cPerson\u003e(new ArgumentNullException()),\n            Fail: ex =\u003e Try\u003cPerson\u003e(ex)\n        ));\n\n    var updatedPerson =\n        people\n        .Fetch(\"Bob\")\n        // map: on an E(x) takes in the normal wrapped value x and returns a normal value y. Result is transformed value E(y).\n        .Map(p =\u003e Person.UpdateName(p, \"Bobby\"))\n        // apply: on an E(x) takes in E(x) and returns whatever. Useful for changing elevated types or passing to function that accepts E(x)\n        .Apply(toTry)\n        //bind: on an E(x) takes in the normal wrapped value x and returns a E(y). Useful when function takes in normal value but returns same elevated value.\n        .Bind(p =\u003e people.Update(\"Bob\", p))\n        .Try();\n    var updated = ElevatedTypesUnsafeHelpers.ExtractUnsafe(people.Fetch(\"Bobby\").Try());\n    Assert.Equal(\"Bobby\", updated.Name);\n}\n```\n\n## Curry, Adapters, Tee/Tap, and Error handling\n\nOften the input and output of function calls don't line up and you need to do some extra work to get types to match up.\n\n### Partial Application and Currying\n\nIf functions have more than 1 input you can use partial application to apply values to the function and get a new function back with that value baked in. Earlier we had the example of the set function. The set function has the signature `Result\u003cMyType option\u003e -\u003e int -\u003e Result\u003cMyType\u003e`. Partial application works from the first parameter so we first call `flip` on `set`. Note that partial application works automatically with F# if not all parameters are supplied.\n\n```fsharp\n//set:Result\u003cMyType option\u003e -\u003e int -\u003e Result\u003cMyType\u003e\nlet flippedSet = flip set //int -\u003e Result\u003cMyType option\u003e -\u003e Result\u003cMyType\u003e\nlet set2 = flippedSet 2//Result\u003cMyType option\u003e -\u003e Result\u003cMyType\u003e with the 2 now baked in\n```\n\nIn the C# example things are a little busier because C# does not support it so we use some functions from the LanguageExt library to help\n\n```csharp\n//flip so int is in correct place to curry, then curry the function with value of 2\nFunc\u003cint, Func\u003cOptionalResult\u003cMyType\u003e, Result\u003cMyType\u003e\u003e\u003e currySet = curry(flip(Set));\nvar set2 = currySet(2);\n```\n\n### Adapters\n\nIn the examples above I chose to use `Set`, one of the main workflow parts to map from `Result\u003cMyType option\u003e` to change to `Result\u003cMyType\u003e`. That probably isn't the best but luckily internally it just uses an adapter function. So I can reuse that. A workflow that does not use set could then look something like this:\n\n```fsharp\n//adapters\nlet errorIfNone r =\n    match r with\n    | Ok (Some x) -\u003e Ok x\n    | Ok None -\u003e Error \"Not found\"\n    | Error s -\u003e Error s\n\n//workflow\nlet r = get(1) |\u003e errorIfNone |\u003e validate |\u003e convert |\u003e tapLog\n```\n\n`errorIfNone` is an adapter from the optional result to the non-optional result.\n\n### Tee/Tap\n\nWhen chaining these workflows having a function that returns `unit` (think of it as a functional `void` except it is a value) isn't very useful. `void` functions are usually something that changes state. You can continue to chain calls together though by defining a function that does what needs to be done and then just returns the input parameter. That function is often called `tee` or `tap`.\n\n```fsharp\nlet tap f x = //or tee\n    f x |\u003e ignore\n    x\n\n//normal logging function\nlet logObj a = printf \"%A\" a\n\n//pass-through logging\nlet tapLog a = tap logObj a\n```\n\n```csharp\nstatic T1 Tap\u003cT1\u003e(Action\u003cT1\u003e f, T1 x) { f(x); return x; }\n```\n\nAs you can see we can use `tap` as an adapter that gives us a function that can be chained.\n\n### Exception handling\n\nTODO\n\n## Lists\n\nTODO","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdburriss%2Felevatedexamples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdburriss%2Felevatedexamples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdburriss%2Felevatedexamples/lists"}