Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Signatures

Functional programming mainly deals with functions. Function signatures are denoted as follows in the FP community:

f: int -> string

In this case, we have a function called f that takes an int as an input and returns a string.

More examples:

  • int -> () - returns void
  • (int, string) -> int - takes two parameters
  • (string, (IConnection -> T)) -> T - one of the parameters is a function that takes IConnection and returns T.

Signatures of functions (name + parameters + return type) are very important, they should be as expressive as possible.

Functions that return void or Task (or “unit”) are side-effect functions. They do something with the “outside world”.

Arguments order

For the functions to be usable with Patrial Application, it makes sense to order the arguments from the most generic ones to the most specific ones.

// GOOD - Apply can be easily used
Person Query(string connectionString, string sql);
// BAD - we'd have to apply something like ApplyR to have common connection string
Person Query(string sql, string connectionString);

Custom Types

It is OK to specify new types for specific purposes, like using Age instead of int (it also adds to the expressiveness of the signature).

Honesty

A function is considered honest if it truly specifies its work in te signature. The function Risk CalculateRisk(int age) is dishonest, because it will most likely fail if negative age is provided. However, its signature states that any int is OK.

The function Risk CalculateRisk(Age age) is honest, because it clearly says that it maps Age -> Risk. Assuming that the Age type has proper validation and cannot be invalid, the function will work with any value of Age.

Unit type

The absence of data can be represented with Unit instead of void. It’s similar to Rust’s () type.

A problem with void is that it makes it impossible to treat functions that do return data similarly as those that don’t. If ther is some HOF like this one T ExeuteAndLog(Func<T> func) we will not be able to invoke it with a func that is an Action. We’d have to create an overload that accepts Action. It’s duplication and it sucks.

.NET provides its own “Unit” type - System.ValueTuple (which makes it even more similar to Rust, since an empty tuple would look like this - ()).

ValueTuple DoSomething()
{
// ...
return default; // an empty tuple
}

Aliasing

using Unit = System.ValueType; allows for Unit name to be used.

Option

In FP we never use null. Instead, Option<T> is used. It makes signatures honest.

Option<T> is a union of Some(T) and None.

.NET does not have a standard Option<T> type. Here’s a simple implementation:

interface IOption<T> {}
record None<T> : Option<T>; // None shouldn't need a T, but the compiler requires it
record Some<T>(T value) : Option<T>;

Maybe

Sometimes the “Option” concept is referred to as “Maybe”.

Option pattern is useful for functions that are Partial. Such functions are able to return valid data only for some subset of their domain. It’s often not clear what a function should do if input is outside of that subset - throw an exception? Return null? Returning None is a better way. An example of such a function could be a function that parses a string into an int. Not every string is a valid integer.

Implicit Type Conversions

C# has a feature that allows one type to be converted to another implicitly. If a given Option implementation uses that, it allows null to be treated as None and "data" as Option<string>.

Constructors can also return Option<T> in case when an object might need some validation of inputs before creation.

Nullable references in C# 8

C# 8 brought a way to specify if a reference value can be nullable. It’s a bit similar to the Option<T>, however, it has major drawbacks, the biggest of them being the fact that it’s opt-in and can be entirely skipped. However, it still makes sense to enable it since it gives good warnings when null might be encountered.

Using Option

Rust-like Match function can be implemented for consuming Option<T>.

Additionally, it’s useful to have a Map (C#‘s Select) function on Option<T>. That way, we can do the following:

var someOption = FnThatReturnsOption();
someOption.Map(CalculateData);

Without option, we’d do:

var someValue = FnThatReturnsValue();
if (someValue is not null) {
CalculateData(someValue);
}

Option can be seen as a specialized container and treated like an IEnumerable. That way we can use the Map (and ForEach) patterns with them the same way.

More about Map and ForEach

←  .NET Functional Features
Function Composition  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.