5 ms·
Are you familiar with C# or Visual Basic? LINQ [1] is a tragic example of what happens when a host language does not support higher-kinded types - every time y
by harveywi 10y ago
Are you familiar with C# or Visual Basic? LINQ [1] is a tragic example of what happens when a host language does not support higher-kinded types - every time you use a LINQ method on a concrete type that may have important semantics, its (static) type gets downgraded to a plain "IEnumerable<X>". This can cause major bugs and headaches when trying (intentionally or inadvertently) to write code to abstract over various combinations of LINQ flavors such as "LINQ to Objects" and, say, "LINQ to Entities/SQL".
The LINQ abstraction is leaky, half-baked, and not as safe as it should be. Some of the various "LINQ to X" flavors are actually unable to support certain operations (cf. [2]), so your software can unexpectedly fail at runtime.
If C# and Visual Basic had support for higher-kinded types, then your LINQ-equipped thing wouldn't have to be punted back to an IEnumerable - it could retain its (static) type after applying LINQ methods.
[1] https://msdn.microsoft.com/en-us/library/mt693024.aspx https://msdn.microsoft.com/en-us/library/mt693024.aspx
[2] https://msdn.microsoft.com/en-us/library/bb738550(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/bb738550(v=vs.110)....
- recursive 10y agoI assume by concrete type, you mean the actual run-time manifested type of an object. If that's true, you're slightly wrong. Most linq methods have parallel definitions for IQueryable<T>. (For those that don't, IQueryable<T> also extends IEnumerable<T>, so that's always a fallback.) In practice this means that not all linq methods return IEnumerable<T>. x.Where(...) may return a statically typed IEnumerable<T> or an IQueryable<T> depending on whether x was IQueryable<T>. Edit: I don't think linq would even actually improve by having this kind of fancy stuff. I don't want .Where() called on an array to return another array. I want a lazy IEnumerable<T> almost every time. I might want to consume only the first 10 elements from the filtered result. Allocating a whole 100000 array could be a tremendous waste of time.
- harveywi 10y agoThanks - you are right about the IQueryable/IEnumerable distinction - it's been a long time since I worked with C#. I think it is somewhat unfortunate that IQueryable is a subtype of IEnumerable because the semantics are so different and one can be so easily coerced into the other. For an alternative example of limitations induced by lack of higher-kinded type abstraction, imagine implementing an Option/Maybe type [1] in C# and piggybacking on LINQ. Under the LINQ API, if you try to do anything with an instance of your Option/Maybe, its static type gets obliterated into an IEnumerable, which destroys the semantics. (The fact that the thing is an Option/Maybe and not, say, a list of things, is important for reasoning about, building, and abstracting over things.) [1] https://en.wikipedia.org/wiki/Option_type https://en.wikipedia.org/wiki/Option_type
- recursive 10y agoRather than a tragedy, I think linq is quite brilliant. As for writing a linq-query compatible option implementation, I think you're wrong. I've written one. You can play with it here. https://dotnetfiddle.net/2hzRtE https://dotnetfiddle.net/2hzRtE No IEnumerable<> to be seen. Incidentally, I think I might have just now understood what a monad even is. All those years of reading blog posts are finally starting to pay off!
- harveywi 10y agoNice Maybe implementation! The thing that you miss out on with that particular style of implementation, though, is that you don't get certain higher-order functions for free. For example, if you were to endow your Maybe implementation with a "GetEnumerator" method, then you would get all of the other LINQ methods for free (at the expense for losing your "Maybe"-ness as its static type is transmogrified into a vanilla IEnumerable). With higher-kinded types, you could potentially gain a whole host of methods for free that would, in contrast to LINQ, return a Maybe<T> instead of just an IEnumerable<T>, but you would still just need to provide a small handful of manually-implemented methods (e.g., Select, SelectMany, Where, etc.).
- louthy 10y ago> Under the LINQ API, if you try to do anything with an instance of your Option/Maybe, its static type gets obliterated into an IEnumerable, which destroys the semantics. This isn't true. It's only true if you derive your Option type from IEnumerable, but you can provide your own implementation of Select, SelectMany, and Where to maintain the Option type - this is my implementation [1], with SelectMany returning Option [2]. Granted, it doesn't make it a higher-kinded type, but you don't have to use IEnumerable to use LINQ. [1] https://github.com/louthy/language-ext/blob/type-classes/LanguageExt.Core/Option/Option.cs https://github.com/louthy/language-ext/blob/type-classes/Lan... [2] https://github.com/louthy/language-ext/blob/type-classes/LanguageExt.Core/Option/Option.cs#L234 https://github.com/louthy/language-ext/blob/type-classes/Lan...
- harveywi 10y ago
- harveywi 10y ago> I don't want .Where() called on an array to return another array. I want a lazy IEnumerable<T> almost every time. Yes, good point. There is a lot of space to explore in collections API designs, and the LINQ design is just one of them that makes its own tradeoffs (e.g., only have to implement "GetEnumerator()" to get a watered-down monadic experience). Contrast that with the well-meaning but convoluted and buggy Scala collections API (very difficult for novices to create a custom collection due to CanBuildFrom and other tricky stuff). Paul Phillips came up with a pretty neat collections API [1, 2] that provides a nice feature set, including high-performance and typesafe views when you don't really want to wastefully create a temporary collection just to interact with the first 10 elements. [1] http://www.slideshare.net/extempore/a-scala-corrections-library http://www.slideshare.net/extempore/a-scala-corrections-libr... [2] https://www.youtube.com/watch?v=uiJycy6dFSQ https://www.youtube.com/watch?v=uiJycy6dFSQ
- pjmlp 10y agoStill it was a way to bring FP concepts into mainstream. On my area of work, even alternative JVM and CLR languages are no go, so I really appreciate having at least the ability to use some FP concepts, even if the end solution isn't perfect.
- louthy 10y ago> If C# and Visual Basic had support for higher-kinded types Although it doesn't have higher-kinded types it can do ad-hoc polymorphism which will get you most of the way there. The primary problem is C#'s terrible type-inference, which means type annotations everywhere. I am currently migrating the types in my language-ext project [1] (a functional framework for C#) to support this. Take a look at this [2] issue raised for a discussion on ad-hoc polymorphism in C#. There are lots of links to examples further down. [1] https://github.com/louthy/language-ext https://github.com/louthy/language-ext [2] https://github.com/louthy/language-ext/issues/120 https://github.com/louthy/language-ext/issues/120