Giter VIP home page Giter VIP logo

language-ext's People

Contributors

andrewbanchich avatar blakesaucier avatar bmazzarol avatar bmazzarol-bunnings avatar braincrumbz avatar brunojuchli avatar ck-linopro avatar colethecoder avatar dependabot[bot] avatar gregberns avatar iamim avatar jagged avatar jmojiwat avatar jvitkauskas avatar la-yumba avatar louthy avatar masaeedu avatar maximusya avatar michael-wolfenden avatar mryossu avatar ode2code95 avatar pauledrupt avatar rammba avatar rantri avatar stanjav avatar stefanbertels avatar tomopensource avatar twaddell avatar tysonmn avatar wdolek 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  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

language-ext's Issues

lefts and rights helpers for Either

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;
                });
            }
        }

Option<T> with async/await

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.

Issues with Some.cs

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.

Documentation on how to use Try<T>

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;
                    }
                }

Support for Either in struct uninitialized

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

Match on implicit Try<T>

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.

Implicit conversion of Some / Option in F# Code

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?

Dependency of Rx

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?

Would be great if code would be documented :)

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.

Stack overflow error on ReduceTest1() and ReduceTest2()

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, IQueryable1 other, Expression1 zipper) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 0 at LanguageExt.Query.zip[T,U,V](IQueryable1 list, IQueryable1 other, Expression1 zipper) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 116
at __QueryExt.Zip[T,U,V](IQueryable1 list, IQueryable1 other, Expression1 zipper) in C:\Users\azuretools\Documents\GitHub\language-ext\LanguageExt.Core\Query.cs:line 215 ... at LanguageExt.Query.map[T,R](IQueryable1 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

README: List creation

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}

Compiler can't choose method group for "IfSome"

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);

Comments on FSharp to/from LanguageExt possibly mixed up

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

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 OutOfMemoryExceptions, 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.

Windows 10 support

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

Question about Optional.Value Internal

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 :)

Either.Equals throws exception when trying to compare Bottom eithers

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

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

A chance to collaborate?

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

Better null safety for Option

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

Would Either<L, R> be better?

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.

Add functionality to TryResult to allow functions to handle success and failure cases

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));

.Some overloads conflinct

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)

Feedback needed on Some(null)

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.

[1] https://news.ycombinator.com/item?id=8634000

subscribe<T>, observe<T> don't work for ProcessFlags.ListenRemoteAndLocal and ProcessFlags.PersistState when using Process + Redis

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

Strange lazyness evaluation behaviour of Bind and Map

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?

The State.SelectMany implementation looks incorrect to me.

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.

Discussion: Naming for Add, Subtract, Divide and, um, Product?

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.

Implicit conversion of Some / Option SubTypes

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?

License is MIT but really GPL

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?

Currying / Partial Evaluation

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?

compose function

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));
}

Lazy<T> vs memo

Looking at the two implementations, I can't seem to find the benefits of memo vs Lazy<T>. The only differences I see are:

  • Lazy can also have non-threadsafe initializations.
  • Lazy is a bigger wrapper, it also tells you more data about the value creation.

Request: Parser Combinators

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 both AsEnumerable and AsEnumerableOne?

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]?

Question about Option<int>

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?

NuGet Package

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.) ?

Append, or Add?

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 =)

Question regarding new tuple

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!

Option and OptionUnsafe null handling clarification

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

  • Will

Bug in ActorSystemState?

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!)

Work with the language-ext author?

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:

  • Patient medical record system
  • Comprehensive consultation suite
  • Document management
  • Scheduling
  • Accounting
  • Email
  • Clinician social network
  • Task management
  • Referral management
  • Reporting and query system for analytics
  • Clinical text analysis using NLP
  • Collaboration system
  • Patient portal
  • Occupational health portal
  • PACS system (MRI/X-ray/Ultrasound)
  • And much more...

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

Prelude.delay throws exception

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.Actor2.RunStrategy(ProcessId pid, ProcessId parent, ProcessId sender, IEnumerable1 siblings, Exception ex, Object message, State`2 strategy)

Breaking changes warning! Process.register(...)

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:

  • Calling Process.register(processId, name) would create a new proxy Process at the address /registered/<name>
  • Process.register(...) would return the proxy's process ID
  • Because /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.
  • This was achieved by calling Process.find(name), which didn't actually do any searching, it just built a ProcessId in the format /registered/<name>
  • The proxy at that address would deal with the routing of the messages to the registered Process

Problems with that solution are:

  • There is no cluster wide coordination of registered processes
  • That means if two separate nodes register the same process name then the two proxy processes would be fighting over the shared inbox. This would lead to undefined behaviour.
  • Proxy processes aren't great for subscriptions. You can't subscribe to a proxy and have it auto-subscribe to the thing it's proxying (this may change, but right now it's a limitation).

Dispatchers

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]

Back to registered processes

When you register a Process it now does one of two things:

  • If the Process is local-only, then it gets registered in an in-memory map of names to ProcessIds
  • If the Process is visible to the cluster, then it gets registered in a Redis map of names to 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:

  • You call deregisterById(pid)
  • You call kill(pid)
    Killing a process wipes its state, inbox and registrations. If you want to kill a process but maintain its cluster state then call: shutdown(pid).
    So if a registered Process is offline then its inbox will keep filling up until it comes back online - so that facilitates eventually consistent behaviours.

Roles

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.

De-registering by name

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.

Existing code

So how will this affect existing code?

  • The signature to register has changed - there is no need for the flags or mailbox size argument any more (because the proxy has gone)
  • Any previous attempt to use this system for either broadcast or leader election would need to be reviewed. If you were doing this it was probably buggy anyway, but now it will be broadcast by default.
  • 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.

Process system dispatch documenation

LanguageExt.Process (more on those later)

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!

Binding support for Option and Some

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];
        }
    }

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.