Giter Club home page Giter Club logo

succinct's Introduction

Succinc<T>

Discriminated unions, pattern matching and partial applications for C#

Current project status

Status
Master branch Build Status (master)
Current release NuGet

Overview

Succinc<T> is a .NET library that adds a number of functional features to C#:

  • Discriminated unions,
  • Pattern matching,
  • Partial applications,
  • "Implicitly" typed lambdas,
  • The ability to treat void methods as Unit functions,
  • Replacements for TryParse methods that return an Option<T> (or Maybe<T>, if you prefer), rather than using the out parameter anti-pattern.
  • "cons" support for IEnumerable<T> (add elements to the head of an enumeration, or split an enumeration into its head element and an enumeration containing the remaining items, all without repeatedly enumerating that enumerable).
  • Cycle() methods that endlessly repeat an enumeration or array, again without repeatedly enumerating that enumerable).
  • Replacements for IEnumerable<T>'s XXXOrDefault methods that return an Option<T> (or Maybe<T>, if you prefer), avoiding null and the "did it return a value, or the default?" problem,
  • And finally, as an experimental feature at this stage, forward pipe support.

For more details of each of these feature, please refer to the wiki.

Serialization

V2.2.0 saw the introduction of of JSON.Net serialization support for Succinc<T>. Using the Succinc<T> serializers, all types can now be correctly serialized/deserialized to JSON.

For details, see the Serializing to JSON wiki page.

Current Release

The current release of Succinc<T> is v4.0.0, which is available as a nuget package that supports .NET 4.6.1+, .NET Core v2.0+ and numerous other frameworks (.NET Standard 2.0).

This release also includes SuccincT.Json v4.0.0, which is available as a separate nuget package that supports .NET 4.6.1+, .NET Core v2.0+ and numerous other frameworks (.NET Standard 2.0). SuccincT.Json is dependent on Succinc<T>, so will pull that package in as part of the install. Also, please note that this nuget package is also dependent on the Newtonsoft.JSON v12.0+ nuget package.

What's planned?

The next release is expected to be v4.1.0. For details of what planned, please see the SuccincT Roadmap for details.

Forking and Contributing

If you wish to fork this repo and build it on your own machine, please refer to the Branches page for details of the currently active branches.

If you wish to contribute in any way (from saying "hi" to submitting a PR), please refer to the Contributing page.

succinct's People

Contributors

davidarno avatar deaflight avatar gregory-bell avatar kgreen24 avatar martijnhoekstra avatar odonno avatar peter-majeed avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

succinct's Issues

Outstanding work needed for the v2.3.0 release

Due in no small part to the contributions from @megafinz, v2.3.0 has turned into a significant release with many new features that all need documenting. This issue is my checklist of things that need doing before I can release this version.

  • Add implicit conversion from T to Option<T> (ie, should be possible to create an option of int using Option<int> x = 1; as an alternative to using Option<int>.Some(1).
  • Add implicit conversion of T1 ... T4 to Union<...> types as per the above.
  • Add JSON converters for Either<TLeft,TRight> and Success<T>.
  • Document the new Map, Or, Flatten and Choose, ToOption, AsNullable and TryCast extensions to Option<T>
  • Document the option-based TryGetValue extension to IDictionary<,>
  • Improve the Option<T> and Maybe<T> docs to link to the above features.
  • Check that the Success<T> type supports implicit conversions from T and add if not.
  • Document the Success<T> type.
  • Document the Either<TLeft,TRight> type.
  • Document the wildcard matching additions to tuple patterns.
  • Document the type matching feature.

Add support for C# 7

  • On the assumption that "pattern matching" in C# 7 will only consist of basic type-switching within the switch statement. Succinc<T> will need to continue supporting it's own pattern matching. With the new tuple features being added to the language, Succinc<T> should support the new ValueTuple types.
  • The Cons method, for splitting an enumeration into a head and tail should be replaced with a tuple-based one:
public (T head, IConsEnumerable<T> tail) Cons<T>(this IEnumerable<T> collection); 

See #15 for details of this change.

  • Test local functions against the various Lambda etc methods.
  • Investigate whether local functions, tuples or type-switching could be used with the source to improve the code/performance.

Preparation for v3.1 release

The following tasks need completing, in order to release v3.1:

Coding

  • Add missing tests for Copy()
  • Agree naming/behaviour of With and Copy with @Odonno and add extra methods as required
  • Check code against R#'s code inspections and implement any improvements

Documentation

  • Deconstruct for Option, Maybe and Success types
  • ValueOrDefault for Option and Maybe types
  • Pattern matching for Success type
  • Cons pattern matching
  • Accessing Union values directly (@peter-majeed contribution)
  • With and Copy extensions (@Odonno contribution)
  • Add @Odonno and @peter-majeed to the contributor list
  • Update release notes, version history and readme as required

Release

  • Create v4.0 branch from v3.1.
  • Remove Copy and With from the v3.1 branch
  • Merge to master
  • Add performance testing project back into solution (on v4.0 branch only!)
  • Push master; package and publish nuget packages
  • Push wiki changes
  • And relax...

Add a new Success<T> type

Success<T> would effectively be the reverse of Option<T>, in that it would either contain success (effectively, true) or a value of T.

This type should be implicitly castable to bool (assuming that's possible), such that success casts to true and a value of T casts to false.

SuccincT Roadmap

@xlecoustillier, @gavinSteyn, @Odonno, @Opiumtm, @megafinz, I'm name checking you all here as you've previously contributed bug reports, suggestions or PRs and so may wish to join in this discussion. Apologies if you are no longer interested.

I've been very quiet regarding SuccincT now for a few months as I've been watching what has happened to C# with the v7.x releases and what's planned for v8, and trying to work out what to do with SuccincT as many of those new language features impact on the reasons for this library existing in the first place.

The key features that I feel have a big impact are:

  1. Readonly refs and readonly structs (C# 7.2)
  2. Nullable reference types (NRT) (C# 8)
  3. Recursive and parameter pattern additions to pattern matching (C# 8)

With the work around readonly structs and refs, I think it sensible to look again at the various option/union types and likely redo all of them as readonly structs.

Since the NRT will mean that reference types become non-nullable by default, the whole need for option/maybe types is called into question. If I can have string for when I definitely have a value, and string? for when I may not, what need is there for Option<string>? T? can be thought of as (an admittedly poor-man's) maybe type.

However, it's not exactly uncommon for null to be used as a value, so it's quite possible that people would want to be able to do something like Option<string?>. There is no T?? (whatever that would mean), so it probably makes sense to keep an option type in SuccincT.

The new features for pattern matching in C# 8 can be experimented with today using this prototype extension for VS2017. From the experiments I've done, I'm not sure there will be any need for SuccincT to have its own pattern matching features when C# 8 is released. So maybe they should be removed at that stage?

So my thoughts on a road map are:

Version 4 - Work can start on this straight away

  1. Remove either Option<T> or Maybe<T> and replace with a single readonly struct. I don't mind which name it has. I'm even open to calling it Optional<T> since that name has become popular too since Java added it.
  2. Replace the Union, ValueOrError and Success types with readonly structs.
  3. Switch the supported framework to netstandard 2 to allow us to add @Odonno's With features.
  4. Address issues/PRs #48 (@Odonno), #49 (@gavinSteyn), #50 (@xlecoustillier) and #51 (@xlecoustillier). Care needs taking with #49 though to ensure it doesn't cause problems with C# 8's pattern matching later (I don't think it will, but want to check).
  5. Make use of the new Enum generic constraint to tidy up the code around the enum parser.

Version 5 - For when C# 8 is released

  1. Ensure the code is compliant with NRTs,
  2. Mark the Match methods as deprecated,
  3. Create Rosylyn analyzer-based "code fixes" to automate rewriting SuccincT patterns into C# patterns. This is likely a lot of work, so this really is aspirational.

Version 6 - Maybe release around the time of v8.1

  1. Remove SuccincT's pattern matching support.

What are your thoughts on the above? Anyone have strong views on any of this? Or is it a case of "Whatever! You disappeared for so long, we've lost interest in SuccincT"? Am I being naive in thinking there's even a need for this library after v8 is released?

Please share your thoughts and don't spare my blushes. I've enjoyed working on SuccincT and I'm happy to put more work in if folk still want it. But I'm also happy to walk away and work on other things if it's coming to the end of its useful life.

Remove Maybe type and change Option to be a readonly struct

This is a major breaking change:

  1. Any code using Maybe<T> will need to change to use Option<T>.
  2. In line with the conversation in #51, the way Option<T> handles null changes:
    i. Option<string>.Some(null) will throw an ArgumentNullException.
    ii. As the type supports implicit casting of T to Option<T>, casting null in this way will result in a None:
Option<string> x = null`
AreEqual(Option<int>.None(), result); // test passes

Checklist for the v4 release

List still WIP. Just noting things down as I think of them:

  • Remove Maybe<T>. See #57
  • Convert Option<T> and the Union<> types to readonly structs. See #57
  • Change the way Succinc<T> interacts with null. See #51.
  • Remove Ignore. See #59
  • Make ValueOrError and Success types, readonly (currently just structs).
  • Switch the supported framework to netstandard 2
  • Add @Odonno's With features (See #44).
  • Address issue/PR #48 (@Odonno)
  • Address issue/PR #49 (@gavinSteyn) Care needs taking to ensure it doesn't cause problems with C# 8's pattern matching later (I don't think it will, but want to check).
  • Address issue/PR #50 (@xlecoustillier)
  • Address issue/PR #51 (@xlecoustillier).
  • Make use of the new Enum generic constraint to tidy up the code around the enum parser (#64)
  • Check docs for the types that have been changed to structs to see if they need updating to mention this
  • Ensure the way that null equality is now handled by option and unions is documented
  • Document removal of Ignore (use discards instead)
  • Check whether the enum parser docs mention anything about throwing exceptions if passed a non-enum and remove if they do
  • document the new features (this point needs expanding to cover each of them

Generic ValueOrError

Is it possible to use ValueOrError<TValue> or even ValueOrError<TValue, TError>?

I mean, what if you want to get a value of a POCO object (as value) and return an Exception (as error)?

Add pattern matching to the Either type

The Either<TLeft, TRight> type doesn't currently support pattern matching.

Add pattern matching support, along the lines of what's available for Union<T1, T2>, except that:

  1. Case1 should be Left,
  2. Case2 should be Right,
  3. CaseOf<T> is not needed.

Hard dependency on System.Runtime 4.1.0

I was trying to use it in a WPF application targeting .Net Framework 4.6.1, but it crashed when code reached usage of SuccincT. Exception details imply that it wants System.Runtime.dll 4.1.0.0 but I had only 4.0.0.0 available in GAC. I had to install .Net 4.6.2 Developer Pack and retarget all my assemblies to 4.6.2 instead of 4.6.1, so I am able to install System.Runtime package from NuGet. The latest version is 4.3.0, but unfortunately app still crashed when I tried to use that. I downgraded the package to 4.1.0 and that fixed the issue for me.

It appears that SuccincT is bound strictly to 4.1.0.0.

TryGetValue for IDictionary<,>

Currently that extension is defined for concrete Dictionary<,> type only. It would be nice if it works with all IDictionary<,> types.

Add positional pattern matching support to Option<T>

Add the means to do something like:

var result = option switch {
    (Some, x) => x,
    (None, _) => 0
};

to the Option type via a deconstructor.

Note this will be a breaking change as the current (hasValue, value) deconstructor will have to be removed.

Investigate adding cons support for async streams

Async streams are a core 3.0-only feature. So in order to support this, I need to experiment with whether I can use something like:

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'netstandard1.0'">
    <DefaultItemExcludes>$(DefaultItemExcludes);src\Succinct\Core3.0\*.cs</DefaultItemExcludes>
</ItemGroup>

to hide files that are core 3.0-only from the compiler when targeting other targets.

A couple of handy links for the csproj stuff for my reference:
https://markheath.net/post/csproj-conditional-references
https://stackoverflow.com/questions/43173811/how-do-i-exclude-files-folders-from-a-net-core-standard-project

Make use of C# 7 deconstruct feature to "super charge" IEnumerable<T> cons support

Currently, Succinc<T> can be used to split an IEnumerable<T> into a head and tail via the following method:

var consResult = someEnumeration.ToConsEnumerable().Cons();
// consResult contains Head and Tail

With C# 7 deconstruction though, a simple Deconstruct extension method on IEnumerable<T> would not only allow the above to be written as:

var (head, tail) = someEnumeration;

But also, a deconstruction can be recursive, so to extract the first three items from a list and have tail represent the fourth onwards elements, the code would simply be:

var (head1, (head2, (head3, tail))) = someList;

Modify Succinc<T> to support this.

Maybe.Some(null) does not throw and has a different behavior than Maybe.None()

I'm currently testing SuccincT, quite happily so far, but encountered a problem when trying to integrate it in a part of a project I'm working on.
Why is Maybe<string>.Some(null) allowed ?

var someNull = Maybe<string>.Some(null);

in this case, someValue.HasValue returns true and someNull.Value returns null, where I would have expected this to behave the way Maybe<string>.None() does (after all, it's seems to be the same thing semantically speaking), or at least to throw.

Of course this is not senseless, but in my mind it defeats the objective of Maybe, which is to provide a way of avoiding nulls. That means that if a legacy part of the project passes an unchecked value which turns to be null, Maybe will happily live with it, propagating it without complaining, meaning that I still need to rely on defensive code even in a Maybe enabled codebase.

Is there a reason I don't get behind this behavior ?

If not, which behavior would you recommend / prefer ?

Remove the use of <T> at the end of Succinc<T>

It's only me that ever uses Succinc<T> as the name of the page. It's difficult to type and its really highly affected nonsense when "SuccincT" works just as well.

So remove all refs to Succinc<T> from the wiki etc and replace with Succinct.

Feature request: pattern mathcing and transformation on object trees

I am using functional approach already in the code which analyze and transform tree-like hierarchical collections (it's transformation of source HTML DOM into Word document paragraphs or internal markup-like representation to display it later on screen).

Naturally, pattern matching is the solution for this class of problems.
There are two basic functional algorithms I use:

  1. "Subtree template" pattern matching to detect particular patterns on subtree and then do some action on it if "shape requirements" are satisfied.
  2. Recursive "tree scan" with transformation of input read-only object tree into output read-only object tree. Scanned tree is "pattern matched" (often with "subtree template" check) and appropriate action is chosen on subtree to output result transformed subtree to insert into result tree (and then passed children of root transformed subtree node again recursively pattern matched) or ignore this subtree completely if needed so.

When I have a look on Succinc<T> code it was interesting how my own implementation of this functional algorithms is very similar to Succinc<T> approach to pattern matching. So it would be good to extend Succinc<T> pattern matching for recursive object tree scan, matching and transformation.

Basic idea is the "tree scan context" which contain original subtree nodes collection from same tree level, result collection of nodes (to insert transformed tree result) and function to get children from subtree node from context.

Example code looks like

            result = html.DocumentNode.ChildNodes.TreeWalk(new ParseContext() { Nodes = result, BaseLink = baseLink })
                .GetChildren(node => node.ChildNodes) // assign default "get children func" to parse context
                .If(node => node.NodeType == typeof(IHtmlTextNode), (node, res) => AddToResult(res, new TextPostNode() { Text = WebUtility.HtmlDecode(node.InnerText) }), node => null)
                .If(IsPostLink, AddPostLink, node => null)
                .If(node => node.Name.EqualsNc("br"), (node, res) => AddToResult(res, new LineBreakPostNode()))
                .If(node => node.Name.EqualsNc("p"), (node, res) => CreateAttribute(res, PostBasicAttributes.Paragraph))
                .If(node => node.Name.EqualsNc("em"), (node, res) => CreateAttribute(res, PostBasicAttributes.Italic))
                .If(node => node.Name.EqualsNc("strong"), (node, res) => CreateAttribute(res, PostBasicAttributes.Bold))
                .If(node => GetPreformatNode(node) != null, (node, res) => CreateAttribute(res, PostBasicAttributes.Monospace), node => GetPreformatNode(node).ChildNodes)
                .If(node => CheckSpan(node, "u"), (node, res) => CreateAttribute(res, PostBasicAttributes.Underscore))
                .If(node => CheckSpan(node, "o"), (node, res) => CreateAttribute(res, PostBasicAttributes.Overscore))
                .If(node => CheckSpan(node, "spoiler"), (node, res) => CreateAttribute(res, PostBasicAttributes.Spoiler))
                .If(node => CheckSpan(node, "s"), (node, res) => CreateAttribute(res, PostBasicAttributes.Strikeout))
                .If(node => node.Name.EqualsNc("sub"), (node, res) => CreateAttribute(res, PostBasicAttributes.Sub))
                .If(node => node.Name.EqualsNc("sup"), (node, res) => CreateAttribute(res, PostBasicAttributes.Sup))
                .If(node => CheckSpan(node, "unkfunc"), (node, res) => CreateAttribute(res, PostBasicAttributes.Quote))
                .If(node => node.Name.EqualsNc("a") && !string.IsNullOrWhiteSpace(node.GetAttributeValue("href", null)), CreateLinkAttrNode)
                .Else((node, res) => res)
                .Run()
                .Nodes;

Particularly compact representation of tree matching and transformation, I think.

Tree scan context looks like this:

    /// <summary>
    /// Tree scan context.
    /// </summary>
    /// <typeparam name="T">Type of source tree node.</typeparam>
    /// <typeparam name="TApp">Type of result.</typeparam>
    public class TreeScanContext<T, TApp> : ITreeWalkContextBreak
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        public TreeScanContext()
        {
            Functions = new List<TreeApplyFunc<T, TApp>>();
        }

        /// <summary>
        /// Source nodes collection.
        /// </summary>
        internal IEnumerable<T> Source { get; set; }

        /// <summary>
        /// Aggregated result for this source nodes collection.
        /// </summary>
        internal TApp Result { get; set; }

        /// <summary>
        /// Apply function.
        /// </summary>
        internal List<TreeApplyFunc<T, TApp>> Functions { get; private set; }

        /// <summary>
        /// Default apply function.
        /// </summary>
        internal Func<T, TApp, TApp> DefaultApply { get; set; }

        /// <summary>
        /// Default function to get children.
        /// </summary>
        internal Func<T, IEnumerable<T>> DefaultGetChildren { get; set; }

        /// <summary>
        /// True if tree scan should globally stop now.
        /// </summary>
        public bool IsBreak { get; set; }
    }

    /// <summary>
    /// Tree apply function description.
    /// </summary>
    /// <typeparam name="T">Type of source tree node.</typeparam>
    /// <typeparam name="TApp">Type of result.</typeparam>
    public class TreeApplyFunc<T, TApp>
    {
        /// <summary>
        /// Pattern match function.
        /// </summary>
        internal Func<T, bool> If { get; set; }

        /// <summary>
        /// Apply func. Aggregate result with passed information from original subtree node.
        /// </summary>
        internal Func<T, TApp, TApp> Apply { get; set; }

        /// <summary>
        /// Get children func. If it returns null or empty enumerable, subtree scan is stopped.
        /// </summary>
        internal Func<T, IEnumerable<T>> GetChildren { get; set; }

        /// <summary>
        /// True if "If" pattern matching function should be execute after
        /// all other matches and only if no other pattern apply.
        /// </summary>
        internal bool IsElse { get; set; }
    }

Code above is provided as the idea. I don't feel it's perfect. I'm just reinvented it to solve particular and quite common problem of tree scan and transformation using functional approach and recursive pattern matching. It would be good if Succinc<T> do support similar feature, but more systematically and consistently implemented.

docs: add comparison list to existing features in C#

It may be interesting to add a grid comparison of features available in functional vs C# in the readme file. Here is a preview (not sure of the correct version) :

Functional features C# equivalent C# version SuccincT equivalent SuccincT version
Pattern matching
is and switch / when C# 7
Discriminated Unions Planned for C# 8
Option N/A
Partial applications N/A

What do you think?

Add cons pattern matching for IEnumerable<T>

var result = someEmptyList.Match().To<int>()
                          .Empty().Do(0)
                          .Result();
// result == 0

var list = new List<int>{1};
var result = list.Match().To<int>()
                 .Empty().Do(0)
                 .Single().Do(x => x)
                 .Result();
// result == 1

var list2 = new List<int>{0};
var result = list2.Match().To<int>()
                  .Empty().Do(0)
                  .Single().Where(x => x > 1).Do(1)
                  .Single().Do(2)
                  .Result();
// result == 2

var list3 = new List<int>{1, 2};
var result = list3.Match().To<int>()
                  .Empty().Do(0)
                  .Single().Do(x => x)
                  .Cons().Do((_, t) => t)
                  .Result();
// result == 2

Requiring Cons().Do() to recursively process the collection would be very inefficient. So RecurseTail method will be available to allow the collection to be iterated over:

var list4 = new List<int>{1, 2, 3, 4};
var result = list4.Match().To<int>()
                  .Single().Do(x => x)
                  .Cons().RecurseTail().Do((head, result) => head + result)
                  .Result();
// result == 1 + 2 + 3 + 4 == 10

My initial thoughts on how to best handle this, if RecurseTail is used, then we iterate over the elements, storing them in eg a stack and then pop them off one at a time, applying the Single match to the last item, and Cons matches to everything else to arrive at a final result.

Not sure yet if an Action version is even needed.

Json Serializer for Union Type

Hello i'am using your great library but
now i'am coming to a problem when i need to serialize my objects.
My objects use the the Union<T0,T1> type.
Is there a example how to serialize with JSON.Net for example?

Michael

Runtime Exception of type System.IO.FileNotFoundException

Working on a .Net project targeting framework netstandard2.0 using SuccincT V3.2.0 library as a reference. At the time of build, I see no issues. But encountering the below runtime exception when code reached the SuccincT usage.

System.IO.FileNotFoundException : 'Could not load file or assembly 'SuccincT, Version=3.2.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.'

Breaking changes in v3.0.0

The following changes have been made to v3.0.0, which may break your existing code:

  1. Union<,>.Creator, Union<,,>.Creator and Union<,,,>.Creator methods now return an instance of IUnionCreator<...>, rather than UnionCreator<...>. If you are referring to your creators via a var, then no change should be needed. But any explicit references to Creator classes will have to be changed to the equivalent interface.
  2. The ITupleMatchable types have been changed to return a value tuple, rather than the older style tuples used previously. See Restructure pattern matchers to use/support new ValueTuples for details.
  3. public static ConsResult<T> Cons<T>(this IEnumerable<T> collection) has been removed and replaced with a deconstructor. See Make use of C# 7 deconstruct feature to "super charge" IEnumerable cons support for details.

Change enum parsers to use Enum generic constraint

Currently, TryParseEnum<T>(this string source) and TryParseEnumIgnoringCase<T>(this string source) use the constraint of struct and then test to see if an enum is supplied.

Update these to use the struct, Enum constraint instead to remove the need for a check.

CaseOf<T> support for arbitrary types

Is it possible to make General Pattern Matcher to be able to match objects on types apart from values just like Union Pattern Matcher does with it's union cases (via CaseOf)?

abstract class ItemBase { }
class ItemA : ItemBase { }
class ItemB : ItemBase { }
class ItemC : ItemBase { }

ItemBase GetItem() { … }

var item = GetItem();

item.Match()
    .WithType<ItemA>().Do(a => …)
    .WithType<ItemB>().Do(b => …)
    .WithType<ItemC>().Do(c => …)
    .Exec();

I'm not really sure this is constructive, though, because C# 7.0 can pattern match types with the same purpose (via swtich).

Would be nice to have a CaseOfType<T> override on unions

The syntax of matching Case1, Case2, etc., could be more readable. It would be nice if there were a way to make it clear in the match which type was which. So instead of:

return result.Match<IHttpActionResult>()
    .Case1().Do(Ok)
    .Case2().Do(InternalServerError())
    .Result();

it might allow something like:

return result.Match<IHttpActionResult>()
    .CaseOfType<ResultObject().Do(Ok)
    .CaseOfType<Error>().Do(InternalServerError())
    .Result();

I'm working on a PR for this as well, but thought I would at least create the issue first, in case you think it's a terrible idea.

Fix need to expose UnionCreator<..> classes as public by using interfaces

The following types:

UnionCreator<T1, T2>
UnionCreator<T1, T2, T3,>
UnionCreator<T1, T2, T3, T4>

All contain the weasel words:

Factory class created by Union{...}.Creator(). Whilst this is a public class (as the user needs access to Create()), it has an internal constructor as it's intended for union creation internal usage only.

In all three cases, these should be able to be made internal, with a public facing interface, which is the type returned by Union<...>.Creator().

Comparing Option<T> to null doesn't work as expected

Consider this code.

Option<string> a = null;
var isNull = a == null;

isNull equals false due to some type conversion magic:

  1. Without the explicit type cast, compiler chooses public static bool operator ==(Option<T> a, Maybe<T> b) => (object) a != null && a.EqualsMaybe(b); overload for equality operator (Option{T}.cs).

  2. "Free" null is cast first to Option<string> and then is wrapped into Maybe<string>.
    public static implicit operator Maybe<T>(Option<T> option) => new Maybe<T>(option); (Maybe{T}.cs)

  3. Equality operator returns false since first operand is null.

As C# 7 now supports discards, the Ignore function is redundant and should be removed

Currently, SuccincT supplies the Ignore function for ignore the return value from an expression, eg

Ignore(someTest ? value1 : value2);

However, as of C# 7, there is now support for using discards to achieve this without the overhead of a method call:

_ = someTest ? value1 : value2;

As a result, Ignore is no longer useful or needed. Therefore it should be removed from v4.

A non-exception Value<T> alternative for unions

I'd like to be able to get the Value of a union using the syntax of Value() (i.e. using the class name), but without the risk of throwing an exception. I have 3 approaches in mind, and before I start coding I was hoping you could weigh in on which way I should go.

  1. Add a Option<T> GetValue<T>() which returns Some if the union is of type T, else None
  2. Add a bool TryGetValue<T>(out value) which returns a bool as to whether it succeeded and the actual value in the out param
  3. Add a bool HasValue<T>() which just returns a bool for whether this union is of type T

Do you have a preferred approach?

Thank you,
Gavin Steyn

Add a new `Indexed` method to IEnumerable<T> that returns IEnumerable<(int index, T item)>

For C# 7, as it'll use the new tuple syntax, or add earlier and make a breaking-change conversion to the tuple syntax in v3.

Add a new method to IEnumerable<T>:

public IEnumerable<(int index, T item)> Indexed(this IEnumerable<T> enumeration);

This can then be used in situations where the index, as well as the value, is needed when enumerating a collection:

for (var (index, item) in someCollection.Indexed())
{
    Console.WriteLIne($"Item {index}: {item}");
}

Restructor the entire project to take advantage of VS2017

  • Move the SuccincT and SuccincT.JSON projects to src and change them to use new csproj files and both to target .net standard 1.1
  • Create new build projects for SuccincT that target .net standard 1.45 and 1.6
  • Create new build projects for SuccincT.JSON that target .net standard 1.45 and 1.6
  • Create appropriate build project to generate all csproj files at build time from metadata
  • Create appropriate build project to generate nuget packages containing all target dlls at build time
  • Merge @megafinz's PR's into these new projects.
  • Merge everything back to master.

Union<T1, T2> comparisons are broken

Comparing two Union<int, string>, where one is an int and the other is a string can result in equality being incorrectly reported, as exhibited via this code snippet:

var a = new Union<int, string>(0);
var b = new Union<int, string>("1234");
Assert.IsFalse(a.Equals(b));

The problem is that Union<int, string>("1234") will internally store the default value (0) for the int value and so the two match.

Tuple pattern matching is a bit rubbish and needs improving to handle C#7+ value tuples

Currently, pattern matching on eg (1, 2) just uses a Matcher<T1, T2, TResult> matcher, forcing the tuple to be decomposed and supplying the two elements as separate parameters to Where and Do.

With C# 7.1, and its Inferred tuple names feature this becomes even more of an issue as it ought to be possible to do, eg:

(a:1, b:1).Match().To<bool>
          .Where(t => t.a < 0).Do(...

The pattern matching of value tuples needs a rewrite to use a tuple-specific pattern matcher to enable such functionality.

As C# 7 now supports discards, the Ignore function is redundant and should be removed

Currently, SuccincT supplis the Ignore function for ignore the return value from an expression, eg

Ignore(someTest ? value1 : value2);

However, as of C# 7, there is now support for using discards to achieve this without the overhead of a method call:

_ = someTest ? value1 : value2;

As a result, Ignore is no longer useful or needed. It should therefore be removed from v4.

Add "any" option to tuple matching

Currently, if we have a tuple, (x, y), which we want to match on, and we are only interested in testing x, a Where must be used:

var result = someTuple.Match().To<bool>()
                      .Where((x, y) => x == 1).Do(true)
                      .Else().Do(false);

Add support for an Any type (which exposes a value singleton, _), that can be used with With:

var result = someTuple.Match().To<bool>()
                      .With(1, _).Do(true)
                      .Else().Do(false);

Maybe<T> have reference to Option<T> instance

https://github.com/DavidArno/SuccincT/blob/master/src/SuccincT/Options/Maybe%7BT%7D.cs

Although Maybe<T> appears as struct, it have internal reference to instance of reference type

internal Option<T> Option { get; }

/// <summary>
/// The value held (if created by Some()). Will throw an InvalidOperationException if created via None().
/// </summary>
public T Value => Option.Value;

Major purpose of structs isn't to avoid "Null reference exceptions". Major purpose of structs is to avoid memory allocations on heap which can be critical under heavy load.

Option<T> and Maybe<T> are small enough to be structs as heap allocations and then GC to release references are more performance-impacting than copy of such small structs around.

Maybe<T> retain unnecessary GC handle (internal reference-type instance of Option<T>), so it don't provide performance benefits of structs.

Succinc<T> should support an option-based Dictionary.TryGetValue

The Dictionary class supports TryGetValue using an out parameter and bool return type.

Succinc<T> should provide a the following extension method:

Option<TValue> Dictionary<TKey, TValue>.TryGetValue(TKey key)

It should return none if the key doesn't exist and Option<TValue>.Some if it does.

feat: add With() method based on with keywork from F#

It would be interesting to add immutable syntax using the with keyword from F#. Here is a proposal :

var oldComputer = new Computer
{
    Os = "Windows 7",
    Cpu = "Intel Core i5"
};

var computerWithNewOs = oldComputer.With(new { Os = "Windows 10" });
var computerWithNewCpu = computerWithNewOs.With(new { Cpu = "Intel Core i7" });

Succinct<T> v2 will introduce breaking changes to provide speed improvements and naming consistency

Introduction

v2.0.0 of Succinc will introduce a number of changes, some of which are breaking changes for anyone using v1.x. The changes fall into four distinct categories detailed below. Please take the time to read the notes below before upgrading so you can:

  1. understand what might break,
  2. plan the fixing process and
  3. decide if you can be bothered to make the changes to gain the benefits v2 brings.

Pattern matching

Risk of code breakages: very low
Difficulty in fixing breakages: very hard

The entire pattern-matching code base has been rewritten. The previous 60 classes that handled pattern matching have been replaced with just 15 classes and around 40 new interfaces. This has two benefits: the code is easier to understand, and more importantly, it is now significantly faster than before. However, a significant number of those 60 classes were public, though most had internal constructors.

  • If you have simply written pattern-matching expressions using these classes, you will not be affected.
  • There was a design flaw in the original matchers for "normal" types (ie, not options, unions) that allowed expressions like x.Match.With(1).Do(...).To<string>().... In other words a .To could be introduced after using With or Where. The expressions before To were then unreachable. This design flaw has now been fixed, so if you've written code like this, it'll no longer compile.
  • If you are doing anything "clever" with these classes, then your code will break horribly, as most have now gone and what's left has different names. If this applies to you, please create an issue here on Github explaining what you were doing and I'll try and assist you in reworking things to work with v2.

The Succinct.PartialApplications namespace has moved to Succinct.Functional and the methods that used the ActionWithOptionalParameter delegates have been removed.

Risk of code breakages: high
Difficulty in fixing breakages: very easy (for methods in new namespace)/ medium (for removed methods)

Most of the partial application support methods (the Compose and TailCompose method groups) have been moved to a new namespace. In addition, those TailCompose methods that used the ActionWithOptionalParameter delegates have been removed (as have the delegates themselves). The reason for this is that:

  1. I've started introducing other functional helper methods and it made sense to group them all in the one namespace.
  2. The new Action methods, (for typing action lambdas) remove the need for the ActionWithOptionalParameter delegates.

As of v2.0.0, the old methods still exist (except as stated above), they have simply been marked as obsolete, which will yield warnings for any code that uses these methods. The chances are though, that you are following the good practice of treating warnings as errors, so this will break your code.

The fix for methods that still exist is simple: replace any occurrence of using Succinct.PartialApplications with using Succinct.Functional.

The fix for the removed methods is to wrap the method being tail composed inside an Action() call to convert it to an action lambda.

Methods that return Option<T> have been renamed to TryXXX

Risk of code breakages: very high
Difficulty in fixing breakages: easy
Succinc provides extension methods to string that attempt to parse a value within that string and return an Option<T> of Some (with the parsed value) or None. The common convention in F# is to add a Try suffix to functions that return an option. I have therefore renamed the parse methods to TryXXX to follow this convention.

Likewise, Succinc provides extension methods to IEnumerable<> that offer alternatives to the XXXOrDefault methods. These were called XXXOrNone. As of v2.0.0, they too have being renamed to TryXXX.

The methods affected are:

BooleanParser

  • ParseBoolean -> TryParseBoolean

EnumParser

  • ParseEnum -> TryParseEnum

FloatParsers

  • ParseFloat -> TryParseFloat
  • ParseDouble -> TryParseDouble
  • ParseDecimal -> TryParseDecimal

IntParsers

  • ParseSignedByte -> TryParseSignedByte
  • ParseUnsignedByte -> TryParseUnsignedByte
  • ParseShort -> TryParseShort
  • ParseUnsignedShort -> TryParseUnsignedShort
  • ParseInt -> TryParseInt
  • ParseUnsignedInt -> TryParseUnsignedInt
  • ParseLong -> TryParseLong
  • ParseUnsignedLong -> TryParseUnsignedLong

OptionExtensionsForIEnumerable

  • FirstOrNone -> TryFirst
  • LastOrNone -> TryLast
  • SingleOrNone -> TrySingle
  • ElementAtOrNone -> TryElementAt

Again, as of v2.0.0, the old methods still exist, they have simply been marked as obsolete, which will yield warnings for any code that uses these methods. Once more, because you are likely following the good practice of treating warnings as errors, these changes will break your code.

The fix is fairly simple: replace any occurrence of the old method name with the new one in the list above.

None.Value becomes None.none

Risk of code breakages: very low
Difficulty in fixing breakages: easy

Since C# 6 introduced static imports, using None.Value becomes clunky. Therefore Value has been renamed to none, allowing a static import of None and the use of none directly.

Again, as of v2.0.0, the old property still exists and has been marked as obsolete, which will yield warnings for any code that uses None.Value. Once more, because you are likely following the good practice of treating warnings as errors, this change will break your code if you are using None.Value directly (which seems unlikely).

The fix is fairly simple: replace any occurrence of None.Value with a using static SuccincT.Unions.None; line and reference none directly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.