Functional programming in c#

By Tomรกลก Juล™iฤka

What is it?

    Separating functions and Data

    Expressions over statements

    Immutable state

Why Functional in c# ?

    It's already there

    You're already using it

    Not hard at all

    More power

    Cleaner code

It's already there & you're using it

LINQ

			
			list
				.Select(number => number + " - Chickens")
				.Where(word => word.Contains('1'))
				.Where(string.IsNullOrEmpty)
				.GroupBy(word => word.Length)
				.ToList();
				
			

It's already there & you're using it

Func type

			
				private static bool IsValid(int input)

				...

				list
            .Where(IsValid)
            .ToList();
			
		  
  
  private bool Validate(string input, params Func<string, bool>[] validations)	
  
  ...

  var isValid = Validate(value, IsNotEmpty, IsAllUppercase);
  
  

It's already there & you're using it

Expressions over statements

			
				return a ? b : c
			
		  
			
				var message = number switch
        {
            > 5 => "Too much",
            < 0 => "Cannot be negative",
            < 1 => "Too low",
            _ => "It's ok"
        };
			
		  

It's already there & you're using it

Immutability

			
                      int, string, Guid, DateTime, Tuple<> ...
			
		  

It's already there & maybe you're using it

Immutability

			
        public record Response
        {
          public DateOnly From { get; init; }
          
          public DateOnly To { get; init; }
          
          public int Duration { get; init; }
        }
			
		  

It's already there & maybe you're using it

Immutability

			
				var x = response with
			  {
				  Duration = 20
			  };
			
		  

So what should we do more ?

Embrace your inner lambda

Use More LINQ

Start focusing on expressions and chaining

From this ...
            
  public static Risk CalculateRisk(int age, Gender gender)
  {
      var treshold = 0;
      if (gender == Gender.Female)
          treshold = 62;
      else
          treshold = 60;

      if (treshold > age)
          return Risk.Low;
      
      return Risk.Medium;
  }
            
            
... to this.
            
  public static Risk CalculateRisk(int age, Gender gender) => 
    Treshold(age, gender) > age ? Risk.Low : Risk.Medium;

  private static int Treshold(int age, Gender gender) =>
      gender == Gender.Female ? 62 : 60;
            
            

Start writing your own "LINQ"

From this ...
            
    public static int? CalculateSalary()
    {
        var employee = GetEmployee();
        if (employee == null)
            return null;

        return employee.Name == "Tom" 
            ? 0 
            : int.MaxValue;
    }
            
            
... to this ...
            
    public static int? CalculateSalary() =>
      GetEmployee()
        .Map(employee => employee.Name == "Tom"
            ? 0
            : int.MaxValue);

    public static TOut? Map<TIn, TOut>(this TIn? input, Func<TIn, TOut> map) => 
        input == null ? null : map(input);
            
            
... up to this.
            
    public static int? CalculateSalary() =>
        GetEmployee().Map(ToSalary);

    public static int[] CalculateSalaries() =>
        GetEmployee().Select(ToSalary).ToArray();

    private static int ToSalary(Employee employee) => 
        employee.Name == "Tom" ? 0 : int.MaxValue;

    public static TOut? Map<TIn, TOut>(this TIn? input, Func<TIn, TOut> map) => 
        input == null ? null : map(input);
            
            
๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ

Will this compile?

            
    public static TOut? Map<TIn, TOut>(this TIn? input, Func<TIn, TOut> map) => 
        input == null ? null : map(input);
            
            
๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก๐Ÿ˜ก

It won't

            
    public static TOut? Map<TIn, TOut>(this TIn? input, Func<TIn, TOut> map)
    
      where TOut : struct

        => input == null ? null : map(input);
            
            

Hey, but data fetching is async

From this ...
            
    public static async Task<int?> CalculateSalary()
    {
        var employee = await GetEmployee();
        if (employee == null)
            return null;

        return employee.Name == "Tom"
            ? 0
            : int.MaxValue;
    }
            
            
... to this ??? ...
            
    public static async Task<int?> CalculateSalary()
    {
        var employee = await GetEmployee();
        return employee.Map(employee => employee.Name == "Tom" ? 0 : int.MaxValue);
    }
            
            
... no, like this ! ...
            
    public static async Task<int?> CalculateSalary() =>
        await GetEmployee().Map(ToSalary);

    public static async Task<TOut?> Map<TIn, TOut>(this Task<TIn?> input, 
        Func<TIn, TOut> map)
      await input switch
      {
          null => null,
          var value => map(value)
      };
            
            
... but what if i want something else ...
            
    public static async Task<(int, string)?> CalculateSalary()
    {
        var employee = await GetEmployee();
        return employee == null 
            ? null 
            : (ToSalary(employee), ToSomethingElse(employee));
    }
            
            
... fasten your seatbelts ...
... ๐Ÿช„ Tada!
            
    public static async Task<(int, string)?> CalculateSalary() =>
      await
          from employee in GetEmployee()
          from salary in ToSalary(employee)
          from somethingElse in ToSomethingElse(employee)
          select (salary, somethingElse);
            
            
๐Ÿ”ฎ Magic behind:
            
    public static async Task<(int, string)?> CalculateSalary() =>
      await
          from employee in GetEmployee()
          from salary in ToSalary(employee)
          from somethingElse in ToSomethingElse(employee)
          select (salary, somethingElse);
            
            
            
    public static async Task<T3> SelectMany<T1, T2, T3>(this Task<T1> source, 
        Func<T1, T2> bind, 
        Func<T1, T2, T3> project) =>
        await source switch
        {
            var t1 => project(t1, bind(t1))
        };
            
            

Data is nice
but what if there's no data

Introducing
            
    void
            
            
From this ...
            
    public static async Task Store(Employee employee)
    {
        if (employee.Id > 10)
            await SaveToDatabase(employee);
        else
            await SaveToMemory(employee);
    }

    public static async Task SaveToDatabase(Employee employee)

    public static async Task SaveToMemory(Employee employee)
            
            
... to this ? ...
            
    public static async Task Store(Employee employee) =>
      employee.Id > 10
          ? await SaveToDatabase(employee)
          : await SaveToMemory(employee);

    public static async Task SaveToDatabase(Employee employee)

    public static async Task SaveToMemory(Employee employee)
            
            
... using this!
            
    global using Unit = System.ValueTuple;

    public static async Task<Unit> Store(Employee employee) =>
      employee.Id > 10
          ? await SaveToDatabase(employee)
          : await SaveToMemory(employee);

    public static async Task<Unit> SaveToDatabase(Employee employee)
        // Do something
        return new Unit();

    public static async Task<Unit> SaveToMemory(Employee employee)
        // Do something
        return new Unit();
            
            

Unit is extremely usefull later
when writing more functional code.

Conclusion

Be more functional

Any Questions?

https://github.com/Tomas-Juri/Functional-programming-in-csharp