2

Monad can pass Just [1,2], which is a different type from what the original length function takes, to >>= return . length.

Just [1,2] >>= return . length

Can I say that Monad makes it possible to see Maybe [a] as isomorphic with [a] on length using (>>=, return)? (Of course they are not really isomorphic.)

Can I choose the term "isomorphic" this situation?

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 2
    I would be careful using *isomorphic* on that situation. Perhaps one can say "almost isomorphic". `Maybe a` is not isomorphic with `a` but `Maybe a` excluding `Nothing` is isomorphic to `a`. – md2perpe Mar 13 '22 at 13:22
  • Yes. that is my reason to use term "isomorphic". because `Monad` take `Nothing` out from `Maybe` and then pass it to length function with extracting `Just`. (I don't explain real process but just concept.) – Lionhairdino L. Mar 13 '22 at 13:56
  • 5
    Rather than "tak[ing] `Nothing` out of `Maybe`" it's usually better to think of it as bringing `length` into `Maybe`, with `(>>=)` specifying what should happen in the `Nothing` case, in which there is nothing to apply `length` to. – duplode Mar 13 '22 at 14:16
  • 2
    While this is a different topic, you might be interested in the fact that `length` is a *monoid homomorphism* going from list types to the monoid for `Int` addition. This is shown by the two facts `Sum (length []) = Sum 0` and that if `xs` and `ys` are lists, then `Sum (length (xs <> ys)) = Sum (length xs) <> Sum (length ys)` , where `Sum` is a newtype wrapper that causes `<>` to act like addition (on the RHS, here). If you're interested, there's a paper on using monoid homomorphisms to design a library [here](https://repository.upenn.edu/cgi/viewcontent.cgi?article=1773&context=cis_papers) – David Young Mar 13 '22 at 16:49

2 Answers2

11

What your example ultimately illustrates is that Maybe is a functor: if you have some f :: a -> b, you can use fmap to turn it into fmap f :: Maybe a -> Maybe b in a way that preserves identities and composition. Monads are functors, with \f m -> m >>= return . f being the same as fmap f m. In your case, we have the length function being transformed by the Maybe functor.

can I choose term "isomorphic" this situation?

Not really. fmap for Maybe is not an isomorphism. An isomorphism requires there being a two-sided inverse that undoes it, which in this case would be something like:

unFmapMaybe :: (Maybe a -> Maybe b) -> (a -> b)

-- For it to be a two-sided inverse to `fmap`, we should have:
unFmapMaybe . fmap = id
fmap . unFmapMaybe = id

However, there are no (Maybe a -> Maybe b) -> (a -> b) functions, as there is no way to obtain a b result if the input Maybe a -> Maybe b function gives out a Nothing. While there are specific functors whose fmap is an isomorphism (Identity is one example), that is not the case in general.

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 2
    Informally this is lifting a function, rather than expressing any isomorphism. `fmap` lifts a function from `[a]` to functions from `Maybe [a]` – Iceland_jack Mar 13 '22 at 16:42
  • 1
    Functors like `(->)` or `Dict` that you `unmap` are called [`FullyFaithful`](https://hackage.haskell.org/package/hask-0/docs/Hask-Functor-Faithful.html) – Iceland_jack Mar 13 '22 at 18:14
  • 1
    @Iceland_jack Nice! I don't think I had seen that module before. For the benefit of future readers (as those haddocks aren't the easiest thing to read), it's worth mentioning here that the `(->)` there isn't the function `Functor` (`(->) a` for some `a`). Rather, it amounts to the function profunctor, which, in this encoding, takes `f :: b -> a` (seen as a Hask^op morphism) to `(. f) :: (a -> c) -> b -> c` (seen as a natural transformation from `(->) a` to `(->) b`). – duplode Mar 14 '22 at 21:54
5

[a] is isomorphic to the quotient type of Maybe [a] with Nothing and Just [] considered equivalent. Alternatively it is isomorphic to Maybe (NonEmpty a), which simply eliminates the Just [] case.
In other words, [a] can be factorized as a composition of the Maybe and NonEmpty functors.

A result of this is that you can lift any function on NonEmpty a to a function on [a]:

liftEmptyable :: (NonEmpty a -> r) -> [a] -> Maybe r
liftEmptyable _ [] = Nothing
liftEmptyable f (x:xs) = Just $ f (x:|xs)

Not sure that actually has much to do with your question though. As duplode answered, you don't really do anything but a simple functor mapping. We could at most elaborate that the monad laws ensure that the fmap really behaves as if length acted directly on the contained list:

Just [1,2] >>= return . length
  ≡ return [1,2] >>= return . length  -- def. of `Monad Maybe`
  ≡ return (length [1,2])             -- left-identity monad law
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319