davidarno / succinct Goto Github PK
View Code? Open in Web Editor NEWDiscriminated unions, pattern matching and partial applications for C#
License: MIT License
Discriminated unions, pattern matching and partial applications for C#
License: MIT License
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.
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)
.T1
... T4
to Union<...>
types as per the above.Either<TLeft,TRight>
and Success<T>
.Option<T>
IDictionary<,>
Option<T>
and Maybe<T>
docs to link to the above features.Success<T>
type supports implicit conversions from T
and add if not.Success<T>
type.Either<TLeft,TRight>
type.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)?
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.'
When I try to update SuccincT from 2.3.0 to 3.0.0, I get like 15 new packages as dependencies. Is there a way to avoid that (apart from waiting for NetStandard 2.0 which, I assume, should bring a single package as dependency)?
My app is WPF desktop application that targets .Net Framework.
Maybe this is relevant:
https://stackoverflow.com/questions/42855180/how-to-create-net-standard-nuget-package-with-minimal-dependencies-in-vs-2017
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?
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.
As per pos pattern matching for Option but using Variant
. Experiment with CaseOf<T>
too, though I suspect that won't work.
Add the means to do something like:
var result = ValueOrError switch {
(Value, x, _) => $"value-{x}",
(Error, _, x) => $"error-{x}"
};
to the ValueOrError
and ValueOrError<TV,TE>
types via deconstructors.
The docs for 1.3.0 claim value equality works for Union<T1, T2, T3> and Union<T1, T2, T3>. Neither has yet been implemented though.
The merged enhancement of adding HasValue to unions is something my team would like to utilize as soon as possible.
Thanks.
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.
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" });
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);
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.
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()
.
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:
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.
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:
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.
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.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:
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.
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.
List still WIP. Just noting things down as I think of them:
Maybe<T>
. See #57Option<T>
and the Union<>
types to readonly structs. See #57Succinc<T>
interacts with null
. See #51.ValueOrError
and Success
types, readonly (currently just structs).null
equality is now handled by option and unions is documentedIgnore
(use discards instead)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.
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.
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
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 ?
Based on a suggestion from @pkanavos:
Option<T>
(and Maybe<T>
if it doesn't just work through implicit casting):public void Deconstruct<T>(out bool hasValue, out T value) => ...
Success<T>
, Either<T1, T2>
and union types.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.
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.
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
Consider this code.
Option<string> a = null;
var isNull = a == null;
isNull
equals false
due to some type conversion magic:
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).
"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)
Equality operator returns false
since first operand is null
.
As per pos pattern matching for Option but using Success and Failure somehow.
The following changes have been made to v3.0.0, which may break your existing code:
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.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.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.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.
The following tasks need completing, in order to release v3.1:
Coding
Copy()
With
and Copy
with @Odonno and add extra methods as requiredDocumentation
Deconstruct
for Option
, Maybe
and Success
typesValueOrDefault
for Option
and Maybe
typesSuccess
typeWith
and Copy
extensions (@Odonno contribution)Release
Copy
and With
from the v3.1 branchmaster
master
; package and publish nuget packagesCurrently, 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 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.
Currently that extension is defined for concrete Dictionary<,> type only. It would be nice if it works with all IDictionary<,> types.
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:
Case1
should be Left
,Case2
should be Right
,CaseOf<T>
is not needed.As per pos pattern matching for Option but using Left and Right somehow.
The various pattern matchers currently use tuples internally. Modify them to use the new syntactic/value tuples.
Additionally, added type-specific matchers to support pattern matching on (T1, T2) through (T1, T2, T3, T4).
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}");
}
@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:
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
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.Union
, ValueOrError
and Success
types with readonly structs.With
features.Enum
generic constraint to tidy up the code around the enum parser.Version 5 - For when C# 8 is released
Match
methods as deprecated,Version 6 - Maybe release around the time of v8.1
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.
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.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.
Lambda
etc methods.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.
Option<T> GetValue<T>()
which returns Some if the union is of type T, else Nonebool TryGetValue<T>(out value)
which returns a bool as to whether it succeeded and the actual value in the out parambool HasValue<T>()
which just returns a bool for whether this union is of type TDo you have a preferred approach?
Thank you,
Gavin Steyn
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
.
This is a major breaking change:
Maybe<T>
will need to change to use Option<T>
.Option<T>
handles null
changes:Option<string>.Some(null)
will throw an ArgumentNullException
.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
I can't find the framework async oper.
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.
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.
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
).
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.