Question
Define the type constructor F in Haskell like this:
data BTree a = Leaf a | Branch (BTree a) (BTree a)
data F a = F (Maybe (BTree a))
The type constructor F is polynomial, so it is a Functor, an Applicative, a Traversable, but not obviously a Monad.
Can we implement bind and return for F that satisfy the monad laws and thus make F into a monad? Or is that impossible?
Motivation
The question is motivated by the fact that F is a generator of data structures of a special kind. Namely, certain F-algebras are monoids that may violate the associativity law. More rigorously, the type F t is an initial object in the category of not-quite-monoids generated by type t that may violate the associativity law.
To see why:
First, we can define the monoid-like operations on F a. The empty element is F Nothing and the binary operation puts two values into the branches of a tree unless one of the values is empty.
Second, suppose the type T is an F-algebra, so we have h: F T -> T. This means we have h(F Nothing), which is a value of type T that serves as the empty value of the monoid, and we have a binary operation (T, T) -> T defined by \(x,y) -> h(F(Just(Branch(Leaf x) (Leaf y)))). Further, suppose that h preserves the monoid operations, then h will also preserve the identity laws. So, T is a not-quite-monoid that satisfies the identity laws (but may violate the associativity law).
F looks like the tree monad BTree(Maybe a) factored by the identity laws of a monoid, although I don't have a formal way of deriving F from that.
The usual folklore knowledge is that F-algebras with laws need to be described via monad algebras. But if F is not a monad then that is not true. The typeclass laws need to be described in a more general way.
But the main question is: can we make F into a monad?
Fis indeed a monad. Given that it is the composition ofMaybewithBTree, with both being monads andBTreebeing traversable, we can tentatively make it a monad using the composed-on-the-inside transformer instance:For details on this transformer construction, see the "Flipped transformers and the Eilenberg-Moore adjunction" section of my answer to Do monad transformers, generally speaking, arise out of adjunctions? In particular, that section points out there two conditions which must hold for the
Monad Finstance above to be lawful. Firstly,join @Maybemust preserve(<*>), that is:The difference between the two sides of this equation boils down to the order in which the effects from
mmfandmmaare sequenced. SinceMaybeis a commutative monad, that doesn't matter, and the condition holds.Secondly,
join @BTreemust preservetoList, that is:Since
BTreeis a free monad (of the homogeneous pair functor), itsjoindoes nothing but grafting trees to remove the nesting, with no rearrangement of elements from the point of view oftoList. That being so, this second condition holds as well, and thereforeFis a monad.P.S.: Regarding n. m.'s question in the comments:
The core part of the
(>>=)definition, which amounts tojoinsans the boilerplate, is:From that, we can tell that
joinwill behave as inBTreeif allMaybevalues areJustinF (F a). If there is aNothinganywhere, though, the result will beNothing, assequenceAandjoin @Maybewill propagate failure: