Search code examples
haskellalternative-functor

Is there instance Alternative ((->) r)?


I got accustomed to using a <> b where a and b take arguments.

> const ["a"] <> const ["b"] $ True
["a","b"]

Why there is no a <|> b as well?

> const ["a"] <|> const ["b"] $ True

<interactive>:64:13:
    No instance for (Alternative ((->) Bool))
      arising from a use of ‘<|>’
    In the expression: const ["a"] <|> const ["b"]
    In the expression: const ["a"] <|> const ["b"] $ True
    In an equation for ‘it’: it = const ["a"] <|> const ["b"] $ True

Solution

  • We'd like to express something like "if the result type is Alternative, then a function returning that type is Alternative too". This isn't right, however, because only * -> * types can be Alternative.

    We might try to remedy our problem by saying that the result type should be f a for some Alternative f, but that's still not good, because the type for which we'd like to define the instance also has to have kind * -> *.

    Actually, we just want to define an Alternative instance for the composition of ((->) r) and some Alternative f. We can generalize this notion slightly to the following:

    import Data.Functor.Compose
    
    instance (Applicative f, Alternative g) => Alternative (Compose f g) where
      empty = Compose $ pure empty
      Compose a <|> Compose b = Compose $ liftA2 (<|>) a b
    

    Unfortunately there is already a different Alternative instance for Compose f g in the transformers library, and it clashes with the above instance.

    Alternatively, we could notice that ReaderT r m is the same thing as Compose ((->) r) m (modulo newtype wrapping), and fortunately the right instance is already exported by transformers:

    instance Alternative m => Alternative (ReaderT r m)
    

    So we can do the following:

    import Control.Monad.Reader
    
    runReaderT (lift ["a"] <|> lift ["b"]) True
    -- ["a", "b"] 
    
    runReaderT (empty <|> lift (Just 0)) True
    -- Just 0