Skip to content

Latest commit

 

History

History
182 lines (145 loc) · 6.1 KB

README.md

File metadata and controls

182 lines (145 loc) · 6.1 KB

MicroElements.Functional

Small subset of functional methodology in C#

Statuses

License NuGetVersion NuGetDownloads MyGetVersion

Travis AppVeyor Coverage Status

Gitter

Summary

MicroElements.Functional is another one try to use functional paradigm in C#. Functional approach can lead error elimination and more explicit logic.

Why I wrote one more lib? I tryed to use many monadic libs: louthy/language-ext, nlkl/Optional, la-yumba/functional-csharp-code, vkhorikov/CSharpFunctionalExtensions but there was some limitations or discomfort. LanguageExt is full featured but but it is very fat, Optional is very verbose and has only Option and Option with error, functional-csharp-code is cool but is a sort of book examples, all libs had not use new performance features of C#. And I decided to replace all functional libs with new... %)

Main features

  • Option
  • Result<A, Error> (Either)
  • Result<A, Error, Message>
  • ValueObject
  • Memoise
  • Try monad
  • Message and MessageList
  • Extensions for parsing, collections

Installation

Package Reference:

dotnet add package MicroElements.Functional

Build

Windows: Run build.ps1

Linux: Run build.sh

License

This project is licensed under the MIT license. See the LICENSE file for more info.

Usage

Option is value that can be in one of two states: Some(a) or None.

Creating Optional values

// Create some option.
var someInt = Some(123);

// Implicit convert to option.
Option<string> name = "Bill";

// None.
Option<string> noneString = None;
Option<int> noneInt = None;

// Extension method to create some value.
var someString = "Sample".ToSome();

Match

// Match returns (123+1).
int plusOne = someInt.Match(i => i + 1, () => 0);
Console.WriteLine($"plusOne: {plusOne}");

// Match returns 0.
int nonePlusOne = noneInt.Match(i => i + 1, () => 0);
Console.WriteLine($"nonePlusOne: {nonePlusOne}");

ValueObject

ValueObject is concept from DDD. see: https://martinfowler.com/bliki/ValueObject.html You need two main principle to implement ValueObject: Structural Equality and Immutability.

To implement ValueObject you need:

  • implement Object.Equals
  • implement Object.GetHashCode
  • implement Equality operator
  • implement Inequality operator
  • implement IEquatable interface (optional)
  • implement IComparable interface (optional)

Or you can just inherit your class from MicroElements.Functional.ValueObject and override GetEqualityComponents method.

MicroElements.Functional.ValueObject implements all routine for you. see Example.

Example

    /// <summary>
    /// Address for delivery.
    /// </summary>
    [ImmutableObject(true)]
    public class Address : ValueObject
    {
        public string Country { get; }
        public string City { get; }
        public string State { get; }
        public string ZipCode { get; }

        public string StreetLine1 { get; }
        public string StreetLine2 { get; }

        public string FullName { get; }
        public string PhoneNumber { get; }

        public Address(string country, string city, string state, string zipCode, string streetLine1, string streetLine2, string fullName, string phoneNumber)
        {
            //TODO: Domain checks
            Country = country;
            City = city;
            State = state;
            ZipCode = zipCode;
            StreetLine1 = streetLine1;
            StreetLine2 = streetLine2;
            FullName = fullName;
            PhoneNumber = phoneNumber;
        }

        /// <inheritdoc />
        public override IEnumerable<object> GetEqualityComponents()
        {
            yield return Country;
            yield return City;
            yield return State;
            yield return ZipCode;
            yield return StreetLine1;
            yield return StreetLine2;
            yield return FullName;
            yield return PhoneNumber;
        }
    }

Option

TODO

Either

TODO

Result

TODO

Design Notes

  • Most of types are structs so they can not be null
  • Don't use monadic values for persistence because structs have default constructors and can be in uninitialized state
  • Don't use monadic values in other data structures
  • Main monad type is A, result type is B or Res

Monad checklist

  • Readonly struct
  • Internal constructors, all factory checks in prelude
  • Monadic methods: Match, Map, Bind(FlatMap), Filter
    • inputs should be not null
    • result types named Res
  • LINQ operations: AsEnumerable, Select(Map), Where(Filter), SelectMany
  • Equality methods
  • Implicit conversions
  • Intermonad conversions
  • Uninitialized checks or bottom state?
  • Check Monad laws
  • Async interoparability or async version

Monadic operations

- B Match(Func<A, B> map);
- Monad<B> Map(Func<A, B> map);
- Monad<B> Select(Func<A, B> select);
- Monad<B> Bind(Func<A, Monad<B>> bind)
- Monad<C> SelectMany<B, C>(
     Func<A, Monad<B>> bind,
     Func<A, B, C> project)