question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Either and type inferencing issue

See original GitHub issue

I’m using the language-ext library in C# and trying to get Tasks returning Either values to compose properly and having a tough time with the type inferencing. It just isn’t working out the way I would expect.

There are three methods involved here:

  • Initialization: Returns a Task<Either<Exception, ADUser>>
  • createUserMapping: Takes an ADUser and returns an Either<Exception, UserMapping>
  • AddUser: takes an UserMapping and returns a Task<Either<Exception, int>>

I would like to return the results of AddUser. It seems like a pretty simple application of Bind operations. Somehow, though, the types are just not lining up.

What I have now is this:

    return Initialization
        .Bind(ead => ead.Bind(createUserMapping).AsTask())
        .Bind(eum => eum.Bind(async u => await AddUser(u)));

In the first operation, the outer Bind takes the Either and calls the (nested) Bind which takes the Right value (an ADUser) and returns an Either<Exception, UserMapping> which is wrapped up in a Task (as a result of the call to AsTask()).

Likewise, in the second case the outer Bind takes the Either and calls the (again, nested) Bind which takes the Right value (a UserMapping) and returns a Task<Either<Exception, int>>.

That is the theory, anyway.

However, while Intellisense is telling me that the u in the last async lambda is of type UserMapping (at the point of declaration), when it gets passed into the AddUser call I get the error:

Argument 1: cannot convert from 'System.Exception' to 'UserMapping'

Why is the compiler getting confused as to the type of the parameter when it isn’t confused about it at the start of the lambda? Is there a better way of doing this?

One thing that does work, but seems a bit of a hack to me, is to not call AddUser directly. Instead I have a new local method, addUser: of type Either<Exception, UserMapping> -> Task<Either<Exception, int>>. It is implemented like this:

    private async Task<Either<Exception, int>> addUser(Either<Exception, UserMapping> eu)
    {
        var nestedTask = eu.Map(u => uow.ControllerRepository.AddUser(u));

        var rv = nestedTask.Match(
            Right: t => identity(t),
            Left: e => Left<Exception, int>(e).AsTask());
        return await rv;
    }

Here, nestedTask is of type Either<Exception, Task<Either<Exception, int>>>. So I take that apart in the Match, returning just the Right value directly or the Left (Exception) wrapped appropriately - as a Task<Either<Exception, int>>.

I also changed createUserMapping to take the Either instead of taking a UserMapping.

Now my top-level sequence of transformations looks like:

        return await Initialization
            .Bind(eadu => createUserMapping(eadu).AsTask())  // eadu: Either<Exception, ADUser>
            .Bind(eum => addUser(eum));                      // eum:  Either<Exception, UserMapping>

This makes my top-level code simpler and works as I expect it to but still leaves me confused as to why the original code just wouldn’t work.

I know that C#'s type inferencing is limited compared to other languages but this has me baffled. Why would the u parameter in the lambda be recognized correctly at the point of declaration but the point that it is used find it switched to another type?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:17 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
melstoncommented, Apr 4, 2017

Thanks. I was getting errors on the second BindT so I abandoned that approach. 😃 It works now, thanks.

0reactions
louthycommented, Jul 25, 2017

@trbngr No problem. You can definitely think of Try<A> as a friendlier Either<Exception, A> and TryAsync<A> as a friendlier Task<Either<Exception, A>>.

There’s also TryOption<A> which is basically: Either<Exception, Option<A>> and TryOptionAsync<A> which is Task<Either<Exception, Option<A>>>

Read more comments on GitHub >

github_iconTop Results From Across the Web

Scala: type inference problem for either.joinRight
On your attempt, is the opposite, you expect the first to be a subtype of the second. But for y neither were the...
Read more >
Type inference
Type inference is the ability to automatically deduce, either partially or fully, the type of an expression at compile time. The compiler is...
Read more >
The problems you mentioned about type inference are ...
The simplest answer to your question of how to know what type the compiler will choose for type inferenced variables, is by compiling...
Read more >
Type inference has usability problems
This is a limitation due to the fact that the compiler and the editor are different software. The compiler knows the type, but...
Read more >
Type inference has usability problems
TL;DR: Type inference is pushed by many tools and languages, but there is little evidence that it is beneficial. I argue that it...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found