How we used Category Theory to solve a problem in Java
techblog.realestate.com.au
techblog.realestate.com.au
a b c d e f g h i j
How many ways can you do that with a binary operation? i.e. how many ways can you completely parenthesise that expression. e.g. ((a (((b (c d)) e) ((f g) h)) i) j)
I haven't worked it out exactly, but I think it's about a trillion.On the other hand, suppose your binary operation is associative. How many different ways are there now? Effectively one.
That's a huge reduction in complexity. The notion of associativity is extremely useful in developing composable programs.
Anyway, 4862 times is still a substantial complexity reduction.
Thanks for the correction.
One of the ten or so "Aha!" moments that each Haskell novice must experience on one's journey to mastery is the realization that you can't make Set an instance of Functor, at least as Functor is commonly defined in the standard Prelude. And the reason why one cannot do so is echoed by their misleading diagram in which their functor replaces each node in a binary tree with its mapped value. What needs to be appreciated is that the resulting tree is no longer ordered (assuming that the original was), and cannot be used to locate an element in logarithmic time.
We can definitely create a `newtype` which composes any applicative with a monoid in the appropriate way:
newtype ApMonoid a m = ApMonoid (a m)
instance (Applicative a, Monoid m) => Monoid (ApMonoid a m) where
mempty = pure mempty
mappend (ApMonoid x) (ApMonoid y) = Wrap $ mappend <$> x <*> y
Then they have two data types: (1) they pull in a bunch of `Extras` up front and then each transformer successively pares down the extras to some subset of data that it cares about, then (2) each function transforms the search `Results`. We could say that this is: data Extras = Extras {e1 :: {- type1 -}, e2 :: {- type2 -}, ... eN :: {- typeN -}}
and their resulting transformation looks like: transN (eN extra) . ... . trans2 (e2 extra) . trans1 (e1 extra) :: Results -> Results
so I guess I'm trying to store `transN . eN` as something like an `ApMonoid (Reader Extras) (Endo Results)` via the above. The logic looks pretty solid.However, if it doesn't kill the basic logic, it might be more Haskell-y to compose these by lifting the monoid to an applicative using `Const`, so that in this case:
newtype ApAp a b x = ApAp (a (b x)) deriving (Functor)
instance (Applicative a, Applicative b) => Applicative (ApAp a b) where
pure = pure . pure
(<*>) = liftA2 (<*>)
We would instead say that this is `ApAp (Reader Extras) (Const (Endo Results))`. Can anyone with more experience with such things comment on whether those are the same? I'm a little shaky here.Also, there's no generic way to "drop" an applicative to be simply a monoid, right? We need an existing Monoid to feed to `ApMonoid`, no?
I.e., use map and comp and avoid objects (to add 'extensions' the way you want).
The category theory + Java part certainly helps selling this to big enterprises though.
Note: I should note that the reason for this was getting to be about 9 years ago, L.J. Hooker (who I believe owns realestate.com.au) were using Btrieve in it's original incarnation to manage all their listings. Total nightmare.
realestate.com.au is Murdoch, in fact, but was a Melbourne startup many years ago.
Java 8 Lambdas provide such an elegant syntax for anonymous classes... When lambdas were first introduced in Java , a lot of people were underwhelmed by them. It is hard to evaluate at first the impact they can have on a well designed API.
foldMap :: Monoid m => (a -> m) -> t a -> m
? default <T> Extension<T> contraMap(Function<T, S> f)
foldMap :: Monoid m => (a -> m) -> t a -> m
I also prefer the Haskell syntax, but it looks like more of a difference because the implementation is inline.Anyway, tastes and education play a role there, but to me the arrow notation is way easier to read, as it is what I would write on paper.
Not saying that there are no monster type signatures in Haskell (just recovering from one such instance in purescript-halogen), but it's really the most leightweight syntax for annotating types out there.
I often use Haskell signatures in comments when writing JS or whatever, just to keep the types straight.
<T> Extension<T> contraMap(Function<T, S> f); contramap :: (a -> b) -> f b -> f a
http://hackage.haskell.org/package/contravariant-1.3.3/docs/Data-Functor-Contravariant.html#v:contramapIf `f` is Function<T, S> then given `T src`, like we have, then f.apply(src) has type S. But Extension.this.apply is applied to values of S even though T is the thing qualified as an Extension.
Actually, I'm just entirely not sure I know how to read Java type signatures.
Why would one need anything special to think this through?
I work with a guy that has a PhD in some heavy duty categorical machinery and I haven't heard him once say he used category theory to design some piece of code.
Composing a list of `A -> A` functions into a single one is already sort of a tall order. They would also have to realize that `(B, A) -> A` is equivalent to `B -> (A -> A)`, and that they can compose `A -> A` under the `B` environment. I just don't see this happening.