I was writing such library myself, but yours is much more complete so I was thinking that I can switch to yours. There is one issue though which prevents me from doing it (or I just missed something?). Option type (and I guess others as well) are not supporting covariance.
Please note that if B is derived from A then Option<B> should be compatible with Option<A>.
class A { public virtual string Greet() { return "A"; } }
class B: A { public override string Greet() { return "B"; } }
public static void DoGreet(Option<A> option)
{
option.Iter(o => Console.WriteLine(o.Greet()));
}
static void Main(string[] args)
{
var a = Prelude.Optional(new A());
var b = Prelude.Optional(new B());
DoGreet(a);
// DoGreet(b); does not compile
}
My solution was to make interface IOption<out T> and use it to pass values around. The other advantage is that you can implement Map, Bind, etc as extensions methods therefore they would work on anything which supports quite minimal IOption interface. Disadvantage being performance, of course.
The biggest problem in your approach is that variable of type IOption<T> can contain null and compiler will not help you to find this bug. Option<T> is a struct because we want to be "nullsafe".
@Hinidu You are right, but adding IOption<T> (interface) to library won't hurt. You can still use Option<T> (struct) in your signatures if you like.
@MiloszKrajewski I don't aggree that adding IOption<T> doesn't hurt. It increases cognitive dissonance for the new users. And "nullsafe"-way should be the default because it is the main mission of the hole Option thing. But nothing will save the programmer that doesn't know about such intricate details from using IOption<T> unconsciously.
But of course it is just my opinion and I'm just the regular user of this library.
@MiloszKrajewski As mentioned above it's due to the types being structs to protect against null. If you're programming in a more functional style you'd be using interfaces less anyway. Working with data-types that are just like product types in functional languages (immutable and with no methods attached), and static 'module like' classes I see this issue very rarely. I do understand it's a pain point however.
Introduction of IOption<A> won't work for this library, it has its own problems, as @Hinidu says it turns your bi-state value into a tri-state value (because null is available), which kills the entire reason for the Option type. It also causes unnecessary boxing. And finally, even a minimal implementation of IOption<A> can't be covariant or contravariant, because at the very least you'd want a Match function, which has both input and output arguments:
c#
public B Match<B>(Func<A, B> Some, Func<B> None) => ...
So the best course of action is to map using option.Map(x => x as A).
Paul,
I am certainly on the c# side of the world right now, looking to get my
feet wet with f#. I certainly do quite a bit of interface-based
development and your comment struck me as curious.
Can you recommend a resource (book, web, paper, etc...) that help to
better frame what a well structured f# program looks like so we c#
people, who are f# newbies can being to reason about world without
interfaces.
Thanks
boris
On 10/17/2016 2:53 PM, Paul Louth wrote:
@MiloszKrajewski https://github.com/MiloszKrajewski As mentioned
above it's due to the types being structs to protect against |null|.
If you're programming in a more functional style you'd be using
interfaces less anyway. Working with data-types that are just like
product types in functional languages (immutable and with no methods
attached), and static 'module like' classes I see this issue very
rarely. I do understand it's a pain point however.Introduction of |IOption| won't work for this library, it has its
own problems, as @Hinidu https://github.com/Hinidu says it turns
your bi-state value into a tri-state value (because |null| is
available), which kills the entire reason for the |Option| type. It
also causes unnecessary boxing. And finally, even a minimal
implementation of |IOption| can't be covariant or contravariant,
because at the very least you'd want a |Match| function, which has
both input and output arguments:public B Match<B>(Func<A, B> Some, Func<B> None) => ...So the best course of action is to map using |option.Map(x => x as A)|.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/louthy/language-ext/issues/161#issuecomment-254314394,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADTHxfhNnDtvBm6mdkx1TCsr9Y4AZgsXks5q09ImgaJpZM4KYZnD.
@louthy As I understand your concerns about tri-state value but I have to say say that 'Match' function does not mess with covariance, as both (see below) versions of Match compile just fine:
public interface IOption<out T> {
bool IsSome { get; }
T Value { get; }
R Match<R>(Func<T, R> some, Func<R> none);
}
public static class OptionExtender {
public static R Match<T, R>(this IOption<T> option, Func<T, R> some, Func<R> none) {
return option.IsSome ? some(option.Value) : none();
}
}
Personally, I think potential option.Fix() (not the best name, I know) method could help with tri-state problem:
public static class OptionExtender {
public static IOption<T> Fix<T>(this IOption<T> option) {
return option != null ? option : Option.None<T>();
}
}
c#
public interface IOption<out T> {
bool IsSome { get; }
T Value { get; }
R Match<R>(Func<T, R> some, Func<R> none);
}
Another problem with this design is the Value property which is not null-safe and become public but its usage should be discouraged in preference to Match, Map, IfNone, etc.
@bbarvish It's not so much a dogmatic approach, just more in keeping with the functional style. Certainly interfaces and the OO style can sometimes have benefits, but what I've found with new projects is that my approach is:
These are very much like record-types in F#. Classes with nothing but readonly fields and no methods or properties. If you take a look at an answer I wrote on Stack Overflow about general purpose immutable classes in C# you'll see what these look like.
Where there are methods or properties, they have no external dependencies (to the class). So for example you may have a class called Customer, with a DateOfBirth field, and perhaps a property called Age that calculates the age of the customer from the DateOfBirth. But I wouldn't have a method called SaveToDb() or something like that.
The constructors of these classes validate that incoming data is sound. That means if you have a reference to an immutable data structure, it can never be in a broken state.
This 'schema' represents all of the data in your application, and you design it in a similar way to designing a RDBMS schema. It's all about the shape of the data and its constraints, and keeping external dependencies (especially IO related) away. That means you can safely build your schema project and include the DLL in other projects without any pain-points. If you're building a multi/micro-service system this is very useful.
This is the one thing that the OO world holds onto like some badge of honour. But it just gets in the way nearly all of the time. Especially in this world where we're constantly pushing data over the wire.
If every function is really a projection from A to B (input arguments to return type), and A and B can only ever be in valid states (because they're immutable and you validate in the constructor). Then you can start to create 'islands' of functionality (modules) that are related but separate from the data; rather than grouping all processing with the data.
So let's say you had a Customer data-type, you could then have:
``` c#
public static class CustomerBilling
{
public static Invoice CreateInvoice(Customer c)
{
// code to generate an empty invoice with pre-populated customer data
}
}
public static class CustomerAccount
{
public static Customer SetContactOptions(Customer c, Option<string> phone, Option<string> email)
{
return c.With(Phone: phone, Email: email);
}
}
public static class CustomerDatabase
{
public static Option<int> Write(Customer c) => ...
public static Option<Customer> Read(int id) => ...
}
This is a slightly contrived and not super informative example (I'm flu'd up atm!). But hopefully you get the point. The data is public, immutable, non-corruptible. The functionality just maps from one state to another.
In the OO world this is considered an anti-pattern: [Anaemic Domain Model](http://www.martinfowler.com/bliki/AnemicDomainModel.html), but not in the functional world. This is pretty much how modules and record types work in F#, and is similar in Haskell too.
I would definitely [recommend this video by Rich Hickey](https://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey). When you understand that the concept of time is poorly managed in OO then you'll see the value of the approach above.
### Dependency injection and IO
I don't use and actively despise the dependency-injection frameworks that are out there. It's horrible magic dust that is nearly never to help the architecture of the code or the programmers on the team, it's nearly always to facilitate unit-tests. By using the approach above everything is unit-testable by default. The areas that aren't are the classic problems of IO.
If you spend any time in Haskell you'll see that they've put a lot of effort into 'managing' the IO problem to maintain referential transparency. IO often looks like this:
``` c#
public static class ThingProvider
{
public static void DoSomethingWithGlobalSideffect(Thing useThisThing);
}
It's next to impossible to test this correctly. In the Haskell world you could imagine something like this happening:
c#
public static class ThingProvider
{
public static World DoSomethingWithGlobalSideffect(World world, Thing useThisThing);
}
The World in its current state is passed in, and the function maps to a new World with the changes in. Obviously that isn't exactly how it works out, but it's kind of the theory.
It's quite difficult to do this in C#, so I tend to find a pragmatic approach is best. That is:
Then unit-test the hell out of the pure bit. That's where your logic is. This could be a web-request, a CRUD operation, a file-batch job, whatever. This is why I built the LanguageExt.Process library. It allows for a cluster operations like the above.
If I do need dependency injection, then I use Funcs rather than interfaces, because that's why functional programming is called what it is :)
Unless you're writing a library like this, then it's actually rare to need reusable code. The other myth is that by abstracting everything out to interfaces you can somehow in the future just replace the implementation and everything will 'just work'. Unless you're writing a file-system or something that needs multiple implementations of 'a thing' that needs to be hot-swapped, then interfaces are usually overkill, or they're only there for dependency injection reasons. I manage an enormous (5 million LOC+) web-application, and we got caught in that abstraction trap, for very little gain. So mostly now I work with concrete data-types with module-like static functionality. The classic complaint that comes up is that it's a refactoring nightmare if you need to change it. Actually it isn't, with statically typed languages and a decent IDE, changes are found quickly. Even when interfaces are used, they tend to be tied closer to the underlying concrete implementation than you'd expect.
Func_This isn't prescriptive, this is just what works for me (and I'm not looking for any arguments here)_
I'd also take a look at fsharpforfunandprofit. It has a ton of tutorials on how to think functionaly.
@MiloszKrajewski You still have the boxing problem. There's no free ride here I'm afraid.
I have already added Optional<A> (an interface) to lang-ext for version 2.0 (which is in progress now in the type-classes branch), unfortunately it still won't support invariance and it's not exactly what you need, as I am adding Haskell typeclass-like functionality to the library.
Optional<A> will be an abstraction of all optional types (Option<A>, OptionUnsafe<A>, Either<L, R>, EitherUnsafe<L, R>, Try<A>, TryOption<A>, ...).
So unfortunately Milosz, I don't have a good answer for you right now. I don't want to be making major changes to the Master branch right now, because v2.0 is almost a re-write. Once I have dealt with v2 I'll look at what could be done here, but it won't be super-high on my priority-list because there is a workaround (using Map).
Yet, it is there in other imperative-tinted languages for purely pragmatic reasons: java, fsharp, scala
Just because others make bad decisions, doesn't mean I should too.
Most helpful comment
@bbarvish It's not so much a dogmatic approach, just more in keeping with the functional style. Certainly interfaces and the OO style can sometimes have benefits, but what I've found with new projects is that my approach is:
Build a 'schema' of data types
These are very much like record-types in F#. Classes with nothing but
readonlyfields and no methods or properties. If you take a look at an answer I wrote on Stack Overflow about general purpose immutable classes in C# you'll see what these look like.Where there are methods or properties, they have no external dependencies (to the class). So for example you may have a class called
Customer, with aDateOfBirthfield, and perhaps a property calledAgethat calculates the age of the customer from theDateOfBirth. But I wouldn't have a method calledSaveToDb()or something like that.The constructors of these classes validate that incoming data is sound. That means if you have a reference to an immutable data structure, it can never be in a broken state.
This 'schema' represents all of the data in your application, and you design it in a similar way to designing a RDBMS schema. It's all about the shape of the data and its constraints, and keeping external dependencies (especially IO related) away. That means you can safely build your schema project and include the DLL in other projects without any pain-points. If you're building a multi/micro-service system this is very useful.
Separate code and data
This is the one thing that the OO world holds onto like some badge of honour. But it just gets in the way nearly all of the time. Especially in this world where we're constantly pushing data over the wire.
If every function is really a projection from
AtoB(input arguments to return type), andAandBcan only ever be in valid states (because they're immutable and you validate in the constructor). Then you can start to create 'islands' of functionality (modules) that are related but separate from the data; rather than grouping all processing with the data.So let's say you had a
Customerdata-type, you could then have:``` c#
public static class CustomerBilling
{
public static Invoice CreateInvoice(Customer c)
{
// code to generate an empty invoice with pre-populated customer data
}
}
It's next to impossible to test this correctly. In the Haskell world you could imagine something like this happening:
c# public static class ThingProvider { public static World DoSomethingWithGlobalSideffect(World world, Thing useThisThing); }The
Worldin its current state is passed in, and the function maps to a newWorldwith the changes in. Obviously that isn't exactly how it works out, but it's kind of the theory.It's quite difficult to do this in C#, so I tend to find a pragmatic approach is best. That is:
Then unit-test the hell out of the pure bit. That's where your logic is. This could be a web-request, a CRUD operation, a file-batch job, whatever. This is why I built the
LanguageExt.Processlibrary. It allows for a cluster operations like the above.If I do need dependency injection, then I use Funcs rather than interfaces, because that's why functional programming is called what it is :)
Get over the OO reuse/interface thing
Unless you're writing a library like this, then it's actually rare to need reusable code. The other myth is that by abstracting everything out to interfaces you can somehow in the future just replace the implementation and everything will 'just work'. Unless you're writing a file-system or something that needs multiple implementations of 'a thing' that needs to be hot-swapped, then interfaces are usually overkill, or they're only there for dependency injection reasons. I manage an enormous (5 million LOC+) web-application, and we got caught in that abstraction trap, for very little gain. So mostly now I work with concrete data-types with module-like static functionality. The classic complaint that comes up is that it's a refactoring nightmare if you need to change it. Actually it isn't, with statically typed languages and a decent IDE, changes are found quickly. Even when interfaces are used, they tend to be tied closer to the underlying concrete implementation than you'd expect.
Conclusion
Func_This isn't prescriptive, this is just what works for me (and I'm not looking for any arguments here)_
I'd also take a look at fsharpforfunandprofit. It has a ton of tutorials on how to think functionaly.