
Higher-kinded types: the difference between giving up and moving forward - dmit
http://typelevel.org/blog/2016/08/21/hkts-moving-forward.html
======
tveita
So tuple() for lists takes two lists and gives all combinations with one item
from each list. We could call it product.

For options it takes two options and return an option with a tuple of values
if both are set, otherwise it return None. We could call it bothOrNone.

For Either... It's not unambiguous, but presumably it's similar to option,
returning either a tuple of Rights, or the first Left of the arguments,
following convention of using Left for errors. We could call it
bothRightsOrALeft.

For State I'm just guessing. getBothWithStateOfLast?

These may all have interesting mathematical similarities, but the way you
would use them in a program would be pretty different. I don't think you are
making your program clearer by giving them all the same name.

Sometimes reading "overabstracted" code can feel similar to reading assembly
code. Sure, add and mov are simple operations with a clear definition of input
and output, just like flatMap and fold, but they say very little about the
programmer's intent in using them. If you give me a chain of them without any
comment, I'll need to work backwards to figure out what you're actually trying
to accomplish.

~~~
svanderbleek
But if you look at Haskell's Control.Monad you see why the abstraction is
helpful [0]. We get things like foldM, it's similar to how you can fold across
more than just lists, you can fold across trees and so on, but for all these
different structures like State, Either, etc. And yes you are right to point
out that some of them have multiple implementations, State can be combined
"backwards" or "forwards" (or both). The trade off is you have to invest the
time so these abstractions become natural, but once you do so you can use
generic control structures that save tons of implementation time.

[0]
[https://hackage.haskell.org/package/base-4.9.0.0/docs/Contro...](https://hackage.haskell.org/package/base-4.9.0.0/docs/Control-
Monad.html)

~~~
skybrian
I guess, but I don't think I need that library? Or at least, not badly enough
to complicate the type system.

It adds complexity, so I'm not convinced I'd save any time.

~~~
edejong
Some might argue that many similarly behaving structures without structural
connectivity (also known as, DRY principle) is the very essence of complexity.

~~~
skybrian
Not necessarily. There is often code that works the same way "by coincidence."
The simplest example is two constants that just happen to be assigned the same
value. In that case, you shouldn't share common code - it's just going to make
changes harder. If you modify one constant, you don't want to modify the
other.

Having a mathematical concept in common: is that important, or is it a
coincidence? Seems like it depends on the domain and maybe on your point of
view.

At the end of the day it's about how hard the code is to maintain. Sharing
dependencies makes things better if you want to fix a bug in one place. But
every dependency has a cost, too, since it's another thing that can break you
if it changes.

Too many dependencies and you get something like the "fragile base class"
problem where you can't easily change the code, even to fix a bug, because
there are so many downstream usages that depend on it working the way it does
today.

So, I would not be quick to depend on a common utility library for simple
convenience, unless I know how stable and bug-free it is, and that it doesn't
have too many dependencies of its own.

------
justusw
Interesting article and funny to see the shout out to Elm. I think the first
few code samples made it very clear that Elm is missing something, at least
for this type of generic programming, namely higher order types. Too many
times I had to re-implement something that could have been solved in a generic
fashion. Kind of similar to how Go programmers have to reimplement instead of
generalise.

It is actively discussed in the Elm community ([https://github.com/elm-
lang/elm-compiler/issues/1039](https://github.com/elm-lang/elm-
compiler/issues/1039)), but with a Wait-And-See-Approach. In my opinion, this
is really laudable, as it signals readiness to add higher order types, and at
the same time keeps developer friendliness of the resulting feature extension
in mind.

------
chowes
What I wish to see in these blog posts are real-world examples. My day job is
coding an MVC app in Rails / Ember. Fine, I won't be able to get past Chapter
11 in a book, but what am I REALLY missing out on? Are these solutions meant
for a different problem set, or would I be able to use these concepts in my
day-to-day (assuming we were written in a Scala, Haskell, etc.)?

~~~
harveywi
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)....](https://msdn.microsoft.com/en-
us/library/bb738550\(v=vs.110\).aspx)

~~~
recursive
I 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
Thanks - 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
Rather 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
Nice 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.).

------
tengbretson
I really hope that higher-kinded types can be brought to TypeScript. I really
want that kind of power on the front-end, but I can't count on getting an
entire team up to speed with PureScript.

~~~
svanderbleek
If TypeScript had these features would it not be as hard to get up to speed
with as PureScript? Thus why not just get up to speed with PureScript. I
believe in you and your team :)

------
vorotato
I'm really confused is he saying that fsharp can't have functional
combinators? That seems incorrect. Also the title is just generally abrasive.
I could just as well say scala doesn't natively support dependent types, so
clearly it's between F* and giving up.

~~~
premium-concern
> doesn't natively support dependent types

Well, it does.

~~~
vorotato
Scala supports dependent types like how F# supports higher kinded types. Kind
of, some of the time, but you might be able to find a library which shims full
support.

------
premium-concern
Jesus Christ, don't be so salty.

~~~
dang
Please post civilly and substantively, regardless of how wrong or annoying
someone else's comment is.

We detached this comment from
[https://news.ycombinator.com/item?id=12340160](https://news.ycombinator.com/item?id=12340160)
and marked it off-topic.

------
rjdevereux
I thought this was gong to be an article on hiring kind people and that the
title was screwed up.

