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

Modularity

Dependency Injection

In a typical C# program, we’d have something like this:

public interface ITimeProvider
{
DateTime GetTime();
}
public class DefaultTimeProvider : ITimeProvider
{
public DateTime GetTime() =>
DateTime.UtcNow;
}
public class DateValidator
{
private readonly ITimeProvider _timeProvider;
public DateValidator(ITimeProvider timeProvider)
{
_timeProvider = timeProvider;
}
public bool Validate(Order order) =>
order.Time <= _timeProvider.GetTime();
}
// DI
services.AddSingleton<ITimeProvider, DefaultTimeProvider>();
services.AddSingleton<DateValidator>();

We need to contact the “outside world” to get the time. In order to make the code testable, the time-getting functionality was extracted to an external class. There’s also an interface that makes it easy to create a mock and inject it. We end up with lots of interfaces with just one implementation - that’s not the purpose of interfaces. It brings much boiler-plate code.

We could use function-injection to achieve the same behavior:

public delegate DateTime Clock();
public class DateValidator
{
private readonly Clock _clock;
public DateValidator(Clock clock)
{
_clock = clock;
}
public bool Validate(Order order) =>
order.Time <= _clock();
}
// DI
services.AddSingleton<Clock>(_ => () => DateTime.UtcNow);
services.AddSingleton<DateValidator>();

Records

We could make the code even shorter with a record.

public record DateValidator(Clock Clock)
{
public bool Validate(Order order) =>
order.Time <= Clock();
}

Delegate

Usage of a new delegate type was not necessary. We could just use Func<DateTime>. A Clock type makes it a bit more readable.

The presented example is still a bit semi-functional. Ideally, we should transform the DateValidator class into a function.

Modularity in FP

In OOP, we use interfaces and their implementations that are injected whenever an interface is required.

In FP, we don’t use interfaces. The function’s signature is the interface itself. Additionally, instead of creating classes that contain some logic, FP promotes the idea of creating functions with that logic. Then, if some other component requires that logic, a delegate should be used to inform about that (instead of an interface).

Here’s a practical example:

OOP:

public class MakeTransferController : ControllerBase
{
IValidator<MakeRequest> _validator;
TransferHandler _handler;
[HttpPost, Route("api/transfers/book")]
public IActionResult MakeTransfer([FromBody] MakeTransfer cmd) =>
_validator.Validate(cmd).Map(_handler.Save).Match(
Invalid => BadRequest,
Valid => Ok()
)
}
// + some typical IoC dependencies registration

FP:

public delegate Validation<T> Validator(T t);
// Returns handler
static Func<MakeRequest, IResult> HandleSaveTransfer(
Validator<MakeTransfer> validate,
Func<MakeRequest, Exceptional<Unit>> save) =>
transfer =>
validate(transfer).Map(save).Match(
Invalid => BadRequest,
Valid => Ok()
)
// Invoked during bootstrap
static Func<MakeRequest, IResult> ConfgureSaveTransferHandler(IConfiguration config)
{
var connString = config.GetSection("ConnectionString").Value;
var save = connString.CreateInserter("INSERT ..."); // some factory function
var validate = DateNotPast(); // some factory function
return HandleSaveTransfer(validate, save);
}
var app = WebApplication.Create();
var handleSaveTransfer(ConfgureSaveTransferHandler(app.Configuration));
app.MapPost("/api/transfers/book", handleSaveTransfer);
await app.RunAsync();

Dependecies are not stored in any fields, they are passed as parameters to the function.

Partial Application

The code above is an example of Partial Application. In order to save the transfer, three parameters are required:

  • validator
  • saver
  • request details

We Have a HOF that accepts the first two parameters to create a function which only needs the last remaining parameter.

.NET 6

The last code block uses .NET 6’s Minimal APIs feature that enables functional approach. Older SDKs may use the Feather HTTP framework.

The functional approach still has decoupled components (functions). Testing should become a bit easier, creating mock functions is easier than creating mock objects.

←  Partial Application
Overview  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.