It might seem like too much abstraction until you realize that the alternative would be the following obtuse type:
data Updatable a = forall x y . Updatable (forall r . ((x, x -> y -> x, x -> a, STM (Maybe y)) -> IO r) -> IO r))
That's what you get if you completely expand out the implementation of `Updatable`. This is why I build up these abstractions from smaller composable pieces (i.e. `Fold` and `Managed` and `Controller`), each of which is intrinsically useful in isolation.
However, I think if you are trying to understand how `Managed` and `Controller` and `Fold` work all in one go, you're going about it the wrong way. Haskell lends itself really well to abstracting over the implementation by specifying mathematical equations inspired by category theory that the implementation obeys. This is not the fake kind of abstraction that other languages promise but never actually deliver (i.e. abstractions that leak and forces you to read source code, often times several layers deep). Rather, everything you need to know about the abstraction layer immediately underneath you is completely summarized by a succinct set of equations that you can then use to prove a new set of equations for the immediately following layer. This post gives an example of this layered proof process:
By composing these small, correct-by-construction layers of equations you can grow to arbitrary complexity while always preserving correctness. More importantly, you never need to reason about more than a single layer at a time since you've decoupled each layer's correctness proofs from each other. Contrast this with other languages where you often have to reason simultaneously over many layers of abstractions to debug difficult issues or prove correctness.
To answer the meat of your question, all you really need to know to understand how `Updatable` works is that:
* `Fold e` is an `Applicative`, for all `e`
* `Managed (Controller e)` is a `Monoid`, for all `e`
* `onLeft` and `onRight` are `Applicative` homomorphisms (this detail is noted in the library source code but I omitted it from the blog post)
Those four properties concisely summarize everything you need to know about every abstraction underneath `Updatable`. If you approach this correctly, there is no need to dig into their source code further to understand how they work. Truthfully, I would love if you were to dig into their source code to learn how they work, but if you really want to level up as a programmer then you need to start thinking more abstractly, layer-by-layer, instead of trying to fit the entire programming stack into your head all at once.
Thanks, I appreciate the further explanation. However, I take issue with you telling me "if you really want to level up as a programmer then you need to start thinking more abstractly." I don't think it's very appropriate to tell someone what they need to do to "level up" as a programmer, when you have no idea of their abilities as a programmer (and even if you do, it comes off as arrogant). Perhaps a better way to phrase it would be "I've found that thinking more abstractly really lets one level up as a programmer"?
No problem. Thanks for the apology. I will take the opportunity to learn more about equational reasoning in Haskell. I'm currently going through TAPL and Software Foundations, so my theoretical focus in Haskell is more on type theory and less on category theory, although that's long been on my "to-learn" list. If that makes sense :)
That makes total sense. Also, type theory is a very fascinating research area, too, especially with the advent of homotopy type theory and practical implementations of dependent types like Idris.
http://www.haskellforall.com/2014/04/model-view-controller-h...
http://www.haskellforall.com/2013/08/composable-streaming-fo...
It might seem like too much abstraction until you realize that the alternative would be the following obtuse type:
That's what you get if you completely expand out the implementation of `Updatable`. This is why I build up these abstractions from smaller composable pieces (i.e. `Fold` and `Managed` and `Controller`), each of which is intrinsically useful in isolation.However, I think if you are trying to understand how `Managed` and `Controller` and `Fold` work all in one go, you're going about it the wrong way. Haskell lends itself really well to abstracting over the implementation by specifying mathematical equations inspired by category theory that the implementation obeys. This is not the fake kind of abstraction that other languages promise but never actually deliver (i.e. abstractions that leak and forces you to read source code, often times several layers deep). Rather, everything you need to know about the abstraction layer immediately underneath you is completely summarized by a succinct set of equations that you can then use to prove a new set of equations for the immediately following layer. This post gives an example of this layered proof process:
http://www.haskellforall.com/2013/12/equational-reasoning.ht...
By composing these small, correct-by-construction layers of equations you can grow to arbitrary complexity while always preserving correctness. More importantly, you never need to reason about more than a single layer at a time since you've decoupled each layer's correctness proofs from each other. Contrast this with other languages where you often have to reason simultaneously over many layers of abstractions to debug difficult issues or prove correctness.
To answer the meat of your question, all you really need to know to understand how `Updatable` works is that:
* `Fold e` is an `Applicative`, for all `e`
* `Managed (Controller e)` is a `Monoid`, for all `e`
* `onLeft` and `onRight` are `Applicative` homomorphisms (this detail is noted in the library source code but I omitted it from the blog post)
Those four properties concisely summarize everything you need to know about every abstraction underneath `Updatable`. If you approach this correctly, there is no need to dig into their source code further to understand how they work. Truthfully, I would love if you were to dig into their source code to learn how they work, but if you really want to level up as a programmer then you need to start thinking more abstractly, layer-by-layer, instead of trying to fit the entire programming stack into your head all at once.