louthy / language-ext Goto Github PK
View Code? Open in Web Editor NEWC# functional language extensions - a base class library for functional programming
License: MIT License
C# functional language extensions - a base class library for functional programming
License: MIT License
I am direly missing these functions from Haskell (http://hackage.haskell.org/package/base-4.8.1.0/docs/src/Data.Either.html#lefts).
I’m not sure how to implement them here, since Haskell uses some kind of partial pattern match magic predicates.
I’m generally not too convinced by the asymmetry of Either. In particular the IfLeft/IfRight functions are preferring Right so much that there is practically no chance to extract a left value. Maybe I’m missing the obvious way of doing it.
The following is as far as I came; it doesn’t compile though, because lout
might not be instanced (as far as the compiler is concerned).
public static class EitherExtensions<L, R>
{
private static IEnumerable<L> Lefts(this IEnumerable<Either<L, R>> eis)
{
return eis.Filter(ei => ei.IsLeft).Map(ei =>
{
L lout;
ei.Match(
Right: r => { return; },
Left: l => { lout = l; });
return lout;
});
}
}
Hello! First of all, great work in this library.
I had some problems when I used async/await with options. I managed to solve it, but I don't feel confortable enough to make a pull request, so here is the idea.
I'm using ASP.NET MVC, and I have an action that can return a Redirect result or a View result. This result would be returned from inside a lambda function passed to the Match function. Inside this lambda, I also made some async work. I needed async/await only in the Some handler.
In short, I had some type errors, the compiler thought that the lambda should return void (probably was using the Action overloads of the Match function). Also, the code got very ugly because sometimes I had to return await Task.FromResult(actionResult)
to make it work.
So I added these extension methods:
public static async Task<R> Match<T, R>(this Option<T> self, Func<T, Task<R>> Some, Func<R> None)
=> await self.Match(Some: async (t) => await Some(t), None: async () => await Task.FromResult(None()));
public static async Task<R> Match<T, R>(this Option<T> self, Func<T, Task<R>> Some, Func<Task<R>> None)
=> await self.Match(Some: async (t) => await Some(t), None: async () => await None());
Now my code looks a little bit better, and it also compiles. I haven't tested it yet, but seems like those extra extension methods could at least make things simpler, because if your Some
handler needs to await
something but your None
doesn't, with those functions you don't need to make the None
handler async.
I think two changes should be made to Some.cs
First change: The overrides which call CheckInitialized should call their methods on the return value of CheckInitialized, rather than the value they pass in:
public override string ToString() =>
CheckInitialised(Value.ToString());
public override int GetHashCode() =>
CheckInitialised(Value.GetHashCode());
public override bool Equals(object obj) =>
CheckInitialised(Value.Equals(obj));
Should be
public override string ToString() =>
CheckInitialised(Value).ToString();
public override int GetHashCode() =>
CheckInitialised(Value).GetHashCode();
public override bool Equals(object obj) =>
CheckInitialised(Value).Equals(obj);
Reason:
If Value
is not initialized it could have another exception thrown (such as NPE) because it's calling a method on Value
before checking whether Value
is initialized.
Second change: It's not enough to only check (and throw) in the default constructor because the CLR will not call the default constructor on arrays of structs.
Example:
var someThings = new Some<object>[100];
Console.WriteLine(someThings[0].Value == null); // Prints true, no exception raised
Reason:
For performance reasons, the CLR will not call the default constructor on struct arrays. It will only zero out the memory associated with them. You'll have to raise an error in the getter property to catch this case.
I’m not sure how to use it, how to create a Try, what implicitly converts to them and how exactly they work.
e.g. will this create a Try or break out of it:
catch (Exception e)
{
if (e is MailAddressException || e is MailServiceException)
{
throw e;
}
}
To save some heapallocs it might make sense to reimplement tuples as value types (this might happen in the next CLR as well, but hasn't been done yet).
It's possible to reuse MIT-licensed https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Tuple.cs to save time.
You could detect and protect from unititialized Either
embedded in structs by replacing the IsRight bool with a
enum LeftRightOther : byte
{
IsUninitialized: 0,
IsLeft: 1,
IsRight: 2
}
Which would take advantage of the fact that structs are initialized by the runtime to all zero bytes... so the enum's can ONLY be 0 if they failed to init with one constructor or the other... thus you can throw any time someone calls anything on the Either
if the enum is 0
Is there a way to implicitly match on a function that can throw an error with Try?
I tried:
match( toMailAdress(address-string),
Succ m => …,
Fail e => …);
But that doesn’t seem to work.
I ask since there is asp.net rc, and this would allow to run it on linux/mac.
Hi.
I've started writing some unit tests in F# for my C# code which uses the language-ext Some and Option structs.
Ironically, the tests in F# are often more verbose than the equivalent in C# because the F# compiler seems to insist I explicitly wrap values with Some or Option rather than implicitly converting them.
e.g. in C# I can call Foo's constructor by new Foo(bar)
where-as in F# I seem to have to write new Foo(new Some<IBar>(bar))
Is there any way around this?
Since version 1.5.0 Language-Ext is depended on 5 Rx packages, which makes me confused as I really need just Option, Either and tuples and list helpers.
I wonder if it will be an effort to minimize dependencies of the Lang-Ext.Core?
For people not used to functional programming things like Fold, Reduce sound strange and it would be of great help to play with the methods and their overloads without the need to search online docs or decompile the code.
Mentioning linq equivalents in a consistent pattern would be nice:
MethodName - what it does (Linq equivalent or No Linq equivalent)
By the way, nice work. I got into this by trying to reduce cyclomatic complexity => search for option/maybe C# implementation.
http://www.codinghelmet.com/?path=howto/reduce-cyclomatic-complexity-option-functional-type
Another aspect regarding documentation, maybe add a link in docs to your comments regarding how this project relates to akka.net. My first thought when reading about implementing actor model was why not use akka instead, and I later found the git issue discussing this.
Hi.
I'm getting a stack overflow error when NCrunch runs the 2 above tests in https://github.com/louthy/language-ext/blob/master/LanguageExt.Tests/QueryTests.cs
The error message for ReduceTest1() is:
nCrunch.TestRuntime.StackOverflowDetectedException : NCrunch has detected a stack overflow
at nCrunch.TestRuntime.TestCoverageEventListener.NCrunchEnsureStackSpaceExists()
at __QueryExt.Zip[T,U,V](IQueryable1 list, IQueryable
1 other, Expression1 zipper) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 0 at LanguageExt.Query.zip[T,U,V](IQueryable
1 list, IQueryable1 other, Expression
1 zipper) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 116
at __QueryExt.Zip[T,U,V](IQueryable1 list, IQueryable
1 other, Expression1 zipper) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 215 ... at LanguageExt.Query.map[T,R](IQueryable
1 list, Expression`1 map) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 39
at LanguageExtTests.QueryTests.ReduceTest1() in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Tests\QueryTests.cs:line 32
This is certainly not the most concise way to create a list in C# 5:
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
can be written as
var list = new List<int>{1, 2, 3, 4, 5}
From docs:
// Invokes an Action if in the Some state.
optional.IfSome(Console.WriteLine);
This won't work probably after IfSome(Func<int, Unit> x) overload was added, maybe you can update docs:
optional.IfSome(i => WriteLine(i));
Or maybe change the overload? the docs version looks much better with static imports:
optional.IfSome(WriteLine);
Hi.
I'm wondering if the comments in https://github.com/louthy/language-ext/blob/master/LanguageExt.FSharp/Prelude.cs should be changed around as follows?
/// <summary>
/// Convert a LanguageExt Option into a F# Option ### change to: Convert an F# Option into a LanguageExt Option
/// </summary>
public static Option<T> fs<T>(FSharpOption<T> fsOption)
/// <summary>
/// Convert an F# Option into a LanguageExt Option ### change to: Convert a LanguageExt Option into a F# Option
/// </summary>
public static FSharpOption<T> fs<T>(this Option<T> option)
/// <summary>
/// Convert an F# Option into a LanguageExt Option ### change to Convert a LanguageExt Unsafe Option into a F# Option
/// </summary>
public static FSharpOption<T> fs<T>(this OptionUnsafe<T> option)
memo<T, R>() is a memory leak.
A cache with a bad policy is another name for a memory leak. That oldnewthing article deals with C++, but the fundamental principle is the same: if you cache data, and you have no eviction policy, your cache will just grow and grow and grow; it's effectively a memory leak.
Thus, consider:
var m = memo( (int x) => x + fix );
for (int i = 0; i < int.MaxValue; ++i)
m(i);
The above will add ~2 billion entries to the cache referenced by m
, which may or may not result in OutOfMemoryException
s, and is almost certainly not what you want.
The "cache policy"/"eviction policy" should be changeable. It shouldn't be possible to consume all RAM just because you want to cache the results of an expensive computation.
https://github.com/louthy/language-ext/blob/master/LanguageExt.Core/Option.cs#L212
OptionExtensions Where method extends UnsafeOption - but this would be a duplicate, right?
Big fan of the library!
I already use it for my WP8.1 projects.
I tried to include it withing my Windows 10 project, but with no success. Still I cannot reference anything from the library.
As I know, creating the proper NuGet package is usually a hassle. Is the current one (1.0.0) supporting UWP applications?
Cheers,
Tomasz
First off, this is an awesome library.
I do have a question about why an option's Value
property or internal method is accesible for use in the following pattern...
And you are totally welcome to disagree since saying you this library to only represent what you think is the best pattern
private Option<string> GetStringNone()
{
string nullStr = null;
return nullStr;
}
Then for me to access the return value, I can only use the Some()
, None()
or Match()
operators (or the match
function)
Why did you choose to restrict the Nullable
-esque pattern of
var x = GetStringNone()
if (x.IsSome){
Console.WriteLine("The value is " + x.Value)
}
I totally understand that when accessing the value inside an optional you essentially want to force the user to account for the Some
case and the None
case using match, however converting an existing code base to use options feels like working with async/await
in the sense that it propogates all the way through my existing code.
This is both a good thing (especially for new codebases) and a bad thing obviously but working with an existing codebase and introducing Option<>
without the ability to just unsafely (throw an exception when trying to access) would make this library (which is awesome) play alot better with existing codebases.
Just curious :)
Equals method of Either throws an exception when you try to compare eithers in the Bottom state.
Seems like Bottom Either must be equal to other Bottom Either which is returned from FirstOrDefault method for example. But i have got:
LanguageExt.BottomException: Either is in a bottom state and therefore not valid. This can happen when the value was filterd and the predicate returned false and there was no valid state the value could be in. If you are going to use the type in a filter you should check if the IsBottom flag is set before use. This can also happen if the struct wasn't initialised properly and then used.
Here is the code from which i am getting the error:
public foo()
{
List<Either<Error, T>> results = ...;
var firsterror = results.FirstOrDefault(i => i.IsLeft);
if (!firsterror.IsDefault()) // <-- here i get exception
{
...
}
}
public static bool IsDefault<T>(this T obj) =>
EqualityComparer<T>.Default.Equals(obj, default(T));
I know i may use IsBottom for this case, but comparing with default is more safety for a FirstOrDefault method result, is it? I mean someday Bottom state may become not default state of Either and i will get ultra strange behaviour of my program.
Serialization and Derialization is a common activity in the .NET world. As it is IO and hence more susceptible to faulty, invalid or null input, it would be nice if LanguageExt's Option, Some and other classes all supported serialization, which is as simple as adding an attribute to each class.
I do not mind modifying the code and submitting the pull request myself, but I thought I'd check with you first, as I'd like this ideally to be available on Nuget.
Thank you
Hi Paul,
I have been also writing a library that brings many functional concepts to C#. It is called Flop (Functional Library for Object Programs) https://github.com/johtela/Flop. Like yourself I have drawn inspiration from Haskell and F# and implemented some of their core libraries in C#. I set out to write a full functional language in .NET but ended up writing a library instead :-). The library is not using C# 6.0, but compiles with .NET 4.0, so the code is not syntactically as close to functional languages as in language-ext.
There are quite a lot of similarities to language-ext though, like Option, Either and Unit types, but I haven't polished them as far as you have. Instead, I have been focusing on implementing immutable data structures like list (both strict and lazy), set, map, and sequence, which is an adaptation of the Haskell collection type with the same name. It is implemented as a finger tree which is quite versatile data structure.
In addition to collections, there is an implementation of Parsec style parser combinator. The difference to your implementation is that the input type is also parameterized. It works on top of a stream abstraction allowing thus to parse arbitrary data like binary or tokenized inputs. Of course, the monad abstraction is implemented as Linq extension methods, so that parsers can be constructed with Linq expressions.
Lastly I have implemented property based testing library a'la QuickCheck. This library has the same concepts like generators for random data, Arbitrary type which allows composing complex test data from simpler ones, and the testable properties themselves. The properties are also monads in Haskell which means that we can again use the syntactic sugaring that Linq provides to build them.
I have been on and off with my project for over a year now, and would like to make something truly usable out of it. I see that we have the same goal to write clean functional code in C#, so I thought it would make sense to merge the libraries into one comprehensive "Prelude" library. It could be a toolkit that would make it little easier to sneak in functional concepts to mainstream C# programming. There are lot of good ideas to steal from there, and I think the C# language is kind of converging into a functional language anyway. I see it being kind of hybrid language like Scala that has the OO heritage, but tries to root out the bad habits of imperative programming.
I started to write documentation for the library (https://github.com/johtela/Flop/blob/master/Docs/Introduction.md) but I have only come up with couple of chapters. As far as hobbies go, documenting code is not my favorite one :-) Having said that, there are a lot of really interesting papers that have guided my implementation, and I would be happy to write about the inner workings of finger trees, parser combinators, and property based testing. These were really enjoyable and challenging to write, and you can learn a thing or two just by walking through their implementation.
I was also planning to check out C# 6.0 and refactor the API using the new features. I was waiting for the official launch, since I had bad experiences of uninstalling preview versions of Visual Studio in the past. But this is definitively in my todo list.
Let me know, if you are interested in collaborating, or if you have other plans/visions for language-ext.
Regards,
Tommi
I'm not sure if you'll want to do this, because it gives up some of the nice familiarity of Some
, but you can avoid the runtime error from:
string s = null;
var opt = Some(s); // Runtime error
By instead using lowercase option
, which just performs the implicit cast to Option
. This gives up a little bit of the expressiveness (using Some indicates I really do mean for there to be a value here), but you gain complete runtime safety, which is nice.
string s = null;
var opt = option(s); // Gives None
Hi.
I'm new to functional programming and I really appreciate your efforts to bring it to C#.
I'm wondering in the current implementation of Either<R, L> why the right type is specified on the left and the left type is specified on the right?
Would it make more sense to specify the types the other way around?
Kind regards,
Craig.
For the case where you want to handle the result of the try test after the execution, there is no way to run functions on the success and failure cases of the TryResult.
Psuedo-code for desired behaviour
var result: TryResult = doRiskyFunction();
result.match(success: result => handleResult(result), failure: exception => handleException(exception));
Hi,
The following doesn't compile:
Action< int > actionint = v => v = v * 2;
Option< int > optional1 = Some(123);
optional1
.Some(actionint) //Compiler tries to call: public static Option<T> Some(T value)
.None(() => 0);
so I need to change to: ".Some < int >(actionint)"
The problem is that this is very confusing since when you call .Some you only see 2 signatures in visual studio:
Action< T > someHandler
Func< T, R > someHandler
It took me while to understand what I was doing wrong, so I can imagine others will have same problem.
Another aspect: maybe group in code overloads of .Some because it's hard to find them otherwise. (i only decompiled the code, but i assume method order is the same in source)
I'm interested in feedback on modifying the behaviour of the automatic conversion to Option. It's triggered by this thread on Hacker News [1]. At the moment: Some(null) becomes None. Which isn't necessarily obvious and may hide bugs in your system.
So I propose that this becomes the Option conversion logic:
x = Some(x)
null = None
Some(x) = Some(x)
None = None
Some(null) = Exception
Some(None) = Some(None)
Some(Some(x)) = Some(Some(x))
Some(Nullable null) = Exception
Some(Nullable x) = Some(x)
Please let me know your thoughts.
Great work!
I'm playing around with the Process + Redis, I'm on Mac using Mono.
The RedisPublishSample given by @louthy (#25), the line "subscribe(pid, Console.WriteLine)" didn't seem to work for me. I did see lots of PUBLISH commands in redid (e.g. 1446114839.593337 [0 127.0.0.1:51072] "PUBLISH" "/redis-test/user/redis-pubsub-random-test-pubsub" "1550928543") but absolutely nothing was printed in the terminal. The subscriber part didn't work.
What could be wrong? StackExchange.Redis? I did need to manually recompile StackExchange.Redis under mono, the nugget package didn't work.
If I change ProcessFlags.RemotePublish to ProcessFlags.ListenRemoteAndLocal then the subscribe part will work. But I presume they are different things / behaviour?
Actually, for RedisStateSample, it's the same, however in mac terminal, the number 32 was printed but that was it. So observeState(pid).Subscribe(Console.WriteLine) worked once only. Not sure why.
I will try tomorrow on Windows and see whether the two examples will work as expected.
Update
I've tested below code in windows and same problem persisted. In console, nothing was printed for RedisPublishSample and RedisStateSample only printed the first number. It was exactly the same as running under mono.
Would definitely need some suggestions. Thanks.
I figured I better copy paste my test code here
using System;
using LanguageExt;
namespace ConsoleApp1
{
class MainClass
{
public static void Main(string[] args)
{
RedisCluster.register();
Cluster.connect("redis", "redis-test", "localhost", "0");
PubSubSample();
StateSample();
Console.Read();
}
public static void PubSubSample()
{
Func<Random> setup = () => new Random();
Func<Random, int, Random> inbox = (rnd, value) =>
{
Process.publish(value);
Process.tellSelf(rnd.Next());
return rnd;
};
var pid = Process.spawn<Random, int>(
"redis-pubsub-random-test",
setup,
inbox,
ProcessFlags.RemotePublish);//change to ProcessFlags.ListenRemoteAndLocal, then the subscriber part will work, strange?
Process.subscribe<int>(pid, x =>
{
Console.WriteLine(x);
});
//also try using observe without any luck
Process.observe<int>(pid).Subscribe(x =>
{
Console.WriteLine(x);
});
Process.tell(pid, 0);
}
public static void StateSample()
{
Func<int> setup = () => 0;
Func<int, int, int> inbox = (state, value) =>
{
state += value;
Process.tellSelf(value, TimeSpan.FromSeconds(1));
return state;
};
var pid = Process.spawn<int, int>(
"redis-state-sample",
setup,
inbox,
ProcessFlags.PersistState);
Process.observeState<int>(pid).Subscribe(x => {
Console.WriteLine(x);
});
Process.tell(pid, 1);
}
}
}
Would be great if someone can help or shed some light. Thanks.
This issue I originally posted in
#25
The samples can also be found here https://github.com/louthy/language-ext/tree/master/Samples
When interleaving side-effecting (aka void
returning) code with Map
and Bind
, strange double-evaluation happens. For example:
static void Main(string[] args)
{
var l = new List<Either<Unit,string>>();
var a = Prelude.Right<Unit,string>("foo");
var b = Prelude.Left<Unit,string>(Prelude.unit);
l.Add(a);
l.Add(b);
//a.Map<Unit, string, Unit>(s => { Console.WriteLine(s); return Prelude.unit; });
var nl = l.Map(ei => ei.Bind(s => { Console.WriteLine(s); return Prelude.Right<Unit, string>(s); }));
Console.WriteLine(nl);
Console.WriteLine(nl.First());
Console.WriteLine(nl.Tail().First());
System.Console.ReadLine();
}
}
This will print
System.Linq.Enumerable+WhereSelectListIterator`2[LanguageExt.Either`2[LanguageEx
t.Unit,System.String],LanguageExt.Either`2[LanguageExt.Unit,System.String]]
foo
Right(foo)
foo
Left(())
I’m not sure how exacly sure how that happens, but I suspect thunks are evaluated twice.
Is there a way to circumvent that?
Looking at the State.SelectMany
code:
public static State<S, V> SelectMany<S, T, U, V>(
this State<S, T> self,
Func<T, State<S, U>> bind,
Func<T, U, V> project
)
{
if (bind == null) throw new ArgumentNullException(nameof(bind));
if (project == null) throw new ArgumentNullException(nameof(project));
return (S state) =>
{
var resT = self(state);
if (resT.IsBottom) StateResult.Bottom<S, V>(state);
var resU = bind(resT.Value)(resT.State);
if (resU.IsBottom) StateResult.Bottom<S, V>(resT.State);
var resV = project(resT.Value, resU.Value);
return StateResult.Return(resU.State, resV);
};
}
I'd say the two bottoming-out cases are missing a return
statement. It should probably read like this:
var resT = self(state);
if (resT.IsBottom) return StateResult.Bottom<S, V>(state);
var resU = bind(resT.Value)(resT.State);
if (resU.IsBottom) return StateResult.Bottom<S, V>(resT.State);
var resV = project(resT.Value, resU.Value);
return StateResult.Return(resU.State, resV);
I'll admit though I'm fairly new to this all - was just reading through the code to assess if we should use the Process
stuff which looks really promising. :)
Regards,
Niek.
Add, Subtract and Divide are all actions.
Whereas Product is the result of an action.
A name that would be consistent with the others would be Multiply.
I can't help but wonder if this naming was copied from elsewhere perhaps? Thoughts?
If you'd like me to tidy it up I'm more than happy to. I'd keep the old signatures and obsolete them for backwards compatibility.
Hi.
I am occasionally running into an issue when I need to pass Some<TSuper>
into a method/constructor as a parameter but what I have at hand is Some<T>
where T : TSuper
.
The C# 5 compiler doesn't seem to implicitly convert between the wrapped types, but makes me manually unwrap and then re-wrap...
e.g. new Foo(new Some((TSuperType)bar.Value))
Is there a nicer way to do this?
the concurrent dictionary implementation is licensed as GPL
cant use the library for work purposes as a consequence - its there not an alternative implementation that could be used?
I have a super simple partial function execution example written in C# 5:
public static Func<TOut> fun<TIn, TOut>(Func<TIn, TOut> func, TIn arg)
{
return () => func(arg);
}
public static Func<TIn, TOut> fun<TIn, TOut>(Func<Func<TIn, TOut>, TIn, TOut> func, Func<TIn, TOut> arg)
{
return x => func(arg, x);
}
public static Func<TIn2, TOut> fun<TIn1, TIn2, TOut>(Func<TIn1, TIn2, TOut> func, TIn1 arg)
{
return x => func(arg, x);
}
static Func<int, int, Tuple<int, int>> tuple = (x, y) => new Tuple<int, int>(x, y);
void Example()
{
var add = fun((f, x) => f(x), (Tuple<int, int> x) => x.Item1 + x.Item2);
var addthree = fun((Func<Tuple<int, int>, int> f, int x) => f(tuple(x, 3)), add);
var five = add(tuple(2, 3));
var alsofive = addthree(2);
}
Do you reckon it's worth cleaning up (I'd like to try and imply those generic args as much as possible) and including in the library?
Hi. I haven't seen anything like compose function in your lib. I think we could use something like that:
public static Func<T1, T3> Compose<T1, T2, T3>(this Func<T2, T3> b, Func<T1, T2> a)
{
return t1 => b(a(t1));
}
public static Func<T1, T3> BackCompose<T1, T2, T3>(this Func<T1, T2> a, Func<T2, T3> b)
{
return t1 => b(a(t1));
}
Looking at the two implementations, I can't seem to find the benefits of memo
vs Lazy<T>
. The only differences I see are:
Hello again!
I know very little about parsers, but what I know is that they look good in functional programming, but not so much in imperative programming (like most things do). I have adventured myself into compiler theory, and sometimes I go back to that subject. While it's interesting, I have little time (and sometimes i'm just lazy) and have to learn it all again, from scratch.
Enough of me, let's talk about this parser thing.
Since LanguageExt is to become a functional hub for C#, do you think about creating a parser combinator library?
I know that there are C# parser combinators out there, but the real fun is to reinvent the weel 😄
I took a look at this link: http://www.codeproject.com/Articles/235221/Monadic-Parsing-in-Fsharp
And tried to convert some of those to C#.
Here is some of what I've achieved. I'm using some LanguageExt functions.
//could be made more general by providing a stream type, not only chars. But...
public delegate IEnumerable<Tuple<T, IEnumerable<char>>> Parser<T>(IEnumerable<char> str);
public static Parser<T> MReturn<T>(T result) => new Parser<T>(str => list(tuple(result, str)));
public static Parser<R> Bind<T, R>(this Parser<T> p, Func<T, Parser<R>> f) =>
new Parser<R>(str =>
p(str).SelectMany(tuple =>
tuple.Map((result, restChars) =>
f(result)(restChars))));
public static Parser<T> Lambda<T>() => new Parser<T>(str => empty<Tuple<T, IEnumerable<char>>>());
public static Parser<char> Satisfies(Predicate<char> condition) =>
Bind(Item, (char c) => condition(c) ? MReturn(c) : Lambda<char>());
public static Parser<char> IsDigit => Satisfies(c => Char.IsDigit(c));
public static Parser<IEnumerable<T>> Many0<T>(this Parser<T> parser) => Choose(Many1(parser), MReturn(empty<T>()));
public static Parser<IEnumerable<T>> Many1<T>(this Parser<T> parser) =>
Bind(parser, (r1) => Bind(Many0(parser), (r2) => MReturn(r1.cons(r2))));
public static Parser<Int64> Integer => Many1(IsDigit)
.Bind((digits) =>
MReturn(
Convert.ToInt64( //could use parseInt and match the option value, but if it reaches this point with a wrong string, the parser is wrong.
new String(digits.ToArray()))));
For now it looks really confusing. I'll try implement Select and SelectMany later to make it a monad, like you did with Option and see if I can make it work with this parser type.
I know you have other things planned to LanguageExt, but do you think about creating parser combinators?
A quick comment: The lambda property declaration (=>) is the coolest thing in this C# release.
Another quick comment: It's incredible how much you can learn if you convert "complicated" code from a language to a more familiar one.
Why do Option and Either need both AsEnumerable and AsEnumerableOne? It seems like one or the other is unnecessary given a repeat and/or take function to convert from one to the other.
so if AsEnumerable just yields at most once, then to get an infinite list of that you would say:
repeat(optionalValue.AsEnumerable())
or maybe 'list' would overload on Option and Either<R, L>:
repeat(list(optionalValue))
What are the intended uses of AsEnumerable[One]?
Very nice work.
I read the justification for Option<> and it occurred to me that it if you simply look at the basic Option class, without the extensions, it smells a lot like Lazy<>. I would curious what disadvantage there would be using Lazy<> instead of Option<> and making the rest of the framework base on that?
I think I'll be using this for some time to see how this fits with my hobby projects. Can you please create a NuGet package with all the dependencies (Mutable collection etc.) ?
This could go nowhere, so I thought I'd ask before spending any time on it.
Currently we have IAppendable
, which doesn't gel for adding numbers. 1 append 2 is 12, not 3. Append has a very specific meaning in our field, ie concatenate.
So perhaps IAddable
is more generic, being clearer for numbers, and also fits for lists etc too. And it fits very nicely in with the other related interfaces.
For consistency all Append/append methods could be renamed to Add/add too.
Thoughts?
OK, I lie, I have a PR ready, just holding off submitting it until I know if it's something you'd like =)
Hey,
Why the With function is called like this?
Wouldn't Map (in case returning non Unit) and Iter (in case Unit) be more intuitive?
Nonetheless, bunch of great ideas!
Re OptionT
Some Match methods are letting nulls slip thru. Except for MatchUnsafe, are all other Match functions supposed to never return null? Also, why does MatchUntyped allow nulls?
Edit: Also ToString and GetHashCode are redundantly checking for nulls when Some, which should be impossible for OptionT. Or is my understanding of the documentation incorrect and nulls are permitted, even when the state of OptionT is Some?
Re OptionUnsafeT
MatchObservable is failing when null is found. Shouldn't it instead allow nulls, as this is the whole point of OptionUnsafe?
I can supply a PR if you're happy with the above
Cheers
I'm not sure what this code does, but the use of a ternary operator makes it appear you're meaning to do one of two things, but in this case both true and false expressions are identical.
private Option<ActorItem> GetItem(ProcessId pid) =>
pid.IsValid
? pid.Head() == RootProcess.Id
? GetItem(pid.Tail(), root) //identical
: GetItem(pid.Tail(), root) //identical
: None;
(I'm using RefactoringEssentials extension to help spot and tidy up warnings etc before doing anything else, so kudos to them for being able to pick this up!)
As well as spending an inordinate amount of time writing Language-Ext, I am also owner and CTO of a software house in Camden (London, UK). Currently I'm in the market for two new developers (one junior, one senior). I'm not sure if it's the 'done thing' by putting this on github; but I figured I'd try to initially avoid the recruiter world and see if there are any followers on here that might be interested?
We develop a very comprehensive healthcare web-application that runs all aspects of a medical practice (from GP practices up to small hospitals). Our application includes:
A lot of the reason I developed language-ext was to try and bring some control to the extremely large application that we have developed. The recent development of the Process system is to help us break up some of the monolithic parts of the application into services in a controlled and robust way.
We have a very relaxed working environment that is more akin to a start-up than a company that's been running for 11 years. We have a team of 4 developers (including me) who are very capable and committed (I've never had a developer leave). There's no office politics or any of that nonsense, it's a very collaborative environment. I try to keep quite a 'flat' structure where I expect my developers to take responsibility for dealing with everything in their purview (with me there as advisor if needed). This has allowed me to keep the team small and efficient - which if you think about the list of features above is an incredible level of features to developers.
Obviously with web-app development there's a reasonable amount of CRUD work, that's a given. But we have also developed interesting in-house tech of our own (distributed databases for document storage, natural language processing for clinical text parsing, UI frameworks, distributed config systems with PAXOS, to name a few). We run on our own machines in high-security data-centres rather than on AWS/Azure, and so we needed our own in-house solutions for deployment, etc.
The people I need are 'self starters' (hate that term, but you know what I mean) and are happy to work on any part of the stack, from UI, to back-end, to data-centre automation, services etc. I need creatively minded individuals who can solve unique problems; not just with code problems, but in improving healthcare too. Healthcare professionals are often not very tech-savvy, and are therefore either scared or incapable of communicating with tech-people. So I need programmers than can hear a real world problem and think creatively.
For the senior developer role you should have significant C# and web-development experience (the web experience doesn't have to be with .NET, we use ASP.NET for our transport mechanism only - we don't use any of the MS frameworks (other than the .NET BCL) -- they change so often, and when I started writing the app 11 years ago they were mostly awful). F# experience is useful but not essential. If you don't have F# experience, you will be expected to learn it and use it: we've been writing some of our satellite components in F#, and I want to use it more (the core application is C#, so that will give you time to get up to speed anyway). If you know Language-Ext (and understand why it exists) then that will help, as we're using it more and more in the app to remove the common OO/C# errors. SQL Server is our database solution, so experience with that is essential. If you have any Postgres experience that would also be valuable.
For the junior developer role Some C# experience, or any more extensive experience in other languages; I really need somebody to help with getting new customers on-board. Often that means writing data-migration tools to help get customer's data into a decent format to go into our app. Often competitor systems have quite poorly structured data, whereas we have a very strong data-schema. You will also work on the core application, but with guidance where necessary. You will have a real opportunity to learn a lot, very quickly. For example my current 'junior' programmer wrote our analytics system - which would have been way beyond his skills when he joined. You will be very much coming in to take over his junior role as he moves up the experience scale.
I don't care about what degree you have (or don't have), but I do want to see experience of the area you're applying for and a passion for what you do. Github projects are the most helpful for that. Written and spoken English must be good.
We offer an attractive salary and a relaxed working environment, working on proper technological solutions. You will not be forced/expected to work long hours as so often happens in software (in fact I actively discourage it). Remote working is also an option (but this must be after an initial in-office trial period).
The healthcare software world is expanding at an exponential rate and is a great environment where your code can make a difference to the outcomes of real people's healthcare problems. If this sounds interesting, then please drop me an email [email protected] (btw, I know our website is terrible, a new one is being worked on!)
You must be able to legally work in the EU. Please don't contact me if you are not an EU national or have an EU work visa
Please, no solicitation
I get the following exception when using a 'backoff' strategy, because Prelude.delay throws an exception:
Error
Strategy exception in [process id]
Start may not be called on a continuation task.
at System.Threading.Tasks.Task.Start(TaskScheduler scheduler) at LanguageExt.Prelude.delay(Action f, TimeSpan delayFor) at LanguageExt.Actor
2.RunStrategy(ProcessId pid, ProcessId parent, ProcessId sender, IEnumerable
1 siblings, Exception ex, Object message, State`2 strategy)
I am currently re-writing the Process.register(...)
and Process.deregister(...)
behaviour. If you rely on this then be aware of what's coming in the next nuget release (it's already on the master branch).
Just to re-cap, this is how it worked previously:
Process.register(processId, name)
would create a new proxy Process at the address /registered/<name>
Process.register(...)
would return the proxy's process ID/registered
is outside of the scope of the node's address space (/node-name/...
) it essentially acted as a DNS service for any process to find any other process by a known name.Process.find(name)
, which didn't actually do any searching, it just built a ProcessId
in the format /registered/<name>
Problems with that solution are:
Now that the Process system supports dispatchers we can implement a more advanced and robust system. Dispatchers put control on the sender's side. Here's an example:
ProcessId pid1 = spawn("proc1", ... );
ProcessId pid2 = spawn("proc2", ... );
ProcessId pid3 = spawn("proc3", ... );
ProcessId pid = Dispatch.broadcast(pid1,pid2,pid3);
tell(pid, msg);
In that example 3 processes are grouped into one ProcessId. You can then tell
, ask
, subscribe
, etc. because it's just a regular ProcessId
. The Process system itself can spot that there are multiple processes referenced and it deals with the dispatch, without a router or proxy Process.
In the above example pid
looks like this:
/disp/broadcast/[/node-name/user/proc1,/node-name/user/proc2,/node-name/user/proc3]
The disp
part tells the system to use a named dispatcher, the broadcast
part is the name of the dispatcher (you can register your own dispatchers via Dispatch.register(...)
). There are several built in dispatchers: broadcast
, random
, round-robin
, random
, first
. The name of the dispatcher decides the bespoke behaviour to run on: [/node-name/user/proc1,/node-name/user/proc2,/node-name/user/proc3]
When you register
a Process it now does one of two things:
ProcessIds
ProcessIds
The result of calling Process.register(name)
is still a ProcessId
, but instead of it looking like this: /registered/<name>
it will now look like this: /disp/reg/<name>
. As you can probably see there is now a dispatcher for registered Processes called reg
.
The default behaviour of this dispatcher is to get the full list of Processes that have been registered with a specific name, and dispatch to all of them (broadcast). This behaviour is more consistent overall, because it doesn't pass any judgement on who registered what when. It simply realises there are multiple processes registered with the same name, and you're trying to communicate with a Process by name, and therefore that's all of them.
When you call Process.find(name)
the system does a similar thing as before of not actually doing any searching at that point, it merely returns /disp/reg/<name>
- so as processes register or deregister the number of possible destinations for a message increases and decreases dynamically.
The keen eyed amongst you may realise that if you can get n
processes registering themselves as 'a named thing', then you could implement high-availability strategies. And to that end, you can combine a registered ProcessId
with other dispatcher behaviour. i.e.
ProcessId pid = Disptach.leastBusy(find("mail-server"));
tell(pid, msg);
The pid
variable above would look like this:
/disp/least-busy/disp/reg/mail-server
This is actually a general feature of dispatchers that they can be combined. You can imagine that the reg
dispatcher returns a list of registered 'mail-server' ProcessIds and then the least-busy
dispatcher finds out which of those mail-server processes has the smallest queue before dispatching the message.
You can take this even further and register a dispatcher. Remember when you call register(name, pid)
the pid
is a ProcessId
and so are the special dispatcher ProcessIds
. So you could do this:
var pid1 = spawn("proc1", ... );
var pid2 = spawn("proc2", ... );
var pid3 = spawn("proc3", ... );
ProcessId parcelPassing = Dispatch.roundRobin(pid1,pid2,pid3);
var reg = register("pass-parcel", parcelPassing);
The value of reg
would be:
/disp/reg/pass-parcel
If you then did a series of tell
calls against reg
then the messages would be sent round-robin to pid1
, pid2
and pid3
. This has very similar functionality to routers without the need for a router Process.
If you think the implications of that through further, let's say you had two data-centres and you wanted an 'eventually consistent' system by sending the same message to both data-centres, but you wanted the least-busy of 3 nodes in each centre to receive the message. A node in each centre could register a least-busy dispatcher ProcessId
under the same registered name, and because the default behaviour of the registered dispatcher is to broadcast, you'd get the exact behaviour you wanted.
Things to note are that this isn't an aliveness system (see the roles section later for that). A registered Process is registered until:
deregisterById(pid)
kill(pid)
shutdown(pid)
.Eventually consistent isn't always the desired behaviour, often you want to just find a Process that does 'a thing' and you want it to do that thing now. Roles facilitate that behaviour. Each node in the cluster must have a role name. Roles use the Process.ClusterNodes
property to work out which member nodes are actually available (it's at most 3 seconds out of date, if a node has died, otherwise it's updated every second).
If you had 10 mail-servers, you could find the least-busy SMTP process by doing something like this:
ProcessId pid = Role.LeastBusy["mail-server"]["user"]["outbound"]["smtp"]
tell(pid, email);
The first child mail-server
is the role name (which you specify when you call Cluster.register(...)
at the start of your app), the rest of it is a relative leaf /user/outboud/smtp
that will refer to N processes in the mail-server
role.
The problem with that ProcessId
is that you need to know about the inner workings of the mail-server node to know that the smtp
Process is on the leaf /user/outbound/smtp
, and that means that the Process hierarchy for the mail-server can't ever change. However because pid
is just a ProcessId the mail-server node itself could register it instead:
register("smtp", Role.LeastBusy["mail-server"]["user"]["outbound"]["smtp"]);
Then any other node that wanted to send a message to the least-busy smtp Process could call:
tell(find("smtp"), msg);
You'll notice also that the mail-server nodes themselves have the control over how to route messages, whether it's least-busy, round-robin, etc. They can change their strategy without it affecting the sender applications.
Although this SMTP example isn't a great one, it should indicate how you can use registered names to represent a dynamically changing set of nodes and processes it the cluster.
You can also wipe all registrations for by name:
deregisterByName(name);
That will clear all registrations for the name specified. This is pretty brutal behaviour, because you don't know who else in the cluster has registered a Process and you're basically wiping their decision. You could use it as a type of leader election system (by deregistering everyone else and registering yourself); but one thing to note is the process wouldn't be atomic, and is therefore not particularly bulletproof.
So how will this affect existing code?
register
has changed - there is no need for the flags or mailbox size argument any more (because the proxy has gone)Process.Registered
has gone, so if you're using that to build registered ProcessIds then you will need to use Process.find(...)
Process.deregister(...)
is now Process.deregisterById(...)
and Process.deregisterByName(...)
. Try to avoid using deregisterByName
This system is significantly more robust and powerful, so I hopefully you'll find that the breaking changes are worth it.
Hi,
I would like to know if you have plans to fill this part ? :-)
I already checked the sample, test and code, but it seems that there are missing some more parts?!
For example, how to send a completed event to all subscribers?
thanks!
Hi,
i would like to use your framework for some WPF desktop applications, but i would need binding support for Option and Some.
As a test I have written my own TypeConverter for Option, unfortunately I had to use some dirty reflection to get the correct types.
I would add A pull request, but the project isn't working with VS 2013 and i don't want to install VS 2015 on the same pc.
Of course I could wrap the option/some types in the view model to "bindable" types, but that would require a lot of boilerplate code.
public class OptionalValueClassTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return true;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return true;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value == null)
return Prelude.None;
var valueType = value.GetType();
var methods = typeof (Prelude).GetMethods().Where(info => info.Name.Equals("Optional")).ToList();
var method = methods.First().MakeGenericMethod(valueType);
var result = method.Invoke(null, new[] { value });
return result;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var optionValue = value as IOptionalValue;
if (optionValue == null)
return value;
if (optionValue.IsNone)
return null;
dynamic dynamicOption = value;
return dynamicOption.ToList()[0];
}
}
It adds tail-calls to the IL automatically during build time, allowing you to write recursive C# code functionally without worrying about blowing the stack: https://github.com/hazzik/Tail.Fody
There's another for adding null checks to all methods, except for where you specify, eg. OptionUnsafe.
https://github.com/Fody/NullGuard
It's quite interesting what Fody (and related addons) can do!
Just food for thought...
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.