Checking in four directions for board game win
Nice question! This is a little trickier than the usual &ldquo;clean up a string of Maybes&rdquo; in that you want short-circuiting on the Just case, and continued computation on Nothing. There is&mdash;of course&mdash;a function for that. Skip to the bottom for the answer, or read through for how you might find or derive it.

Finding the Answer

Hoogle. Pretty much always Hoogle. Think about what you've got and what you need, then start testing types against Hoogle. In this case you've got a sequence of Maybe a values that you want to reduce to a single value. The key insight is that the sequence must all be of the same Maybe a type, so it's possible (and thus probably necessary) to create a list (an actual [Maybe a]) of them. Thus our candidate type signature&mdash;

? :: [Maybe a] -&gt; Maybe a


Pop that type signature into Hoogle and the very first thing that comes up is&mdash;

-- "base" Control.Monad
-- | This generalizes the list-based concat function.
msum :: MonadPlus m =&gt; [m a] -&gt; m a


Test it in GHCi and&mdash;

&gt; msum [Nothing, Just 1, Just 2]
Just 1
&gt; msum [Just 1, Nothing, Just 2]
Just 1
&gt; msum [Nothing] :: Maybe Int
Nothing


Yep, that's what we want.

Deriving the Answer

There are probably two different ways you might come up with your own solution. Either start at the concrete and generalize, or cobble a solution from typeclasses down.

From the Concrete

Consider again the type of the function you need.

? :: [Maybe a] -&gt; Maybe a


A no-frills solution to this with primitive recursion and pattern matching should be self-obvious (or will be with experience).

firstJust :: [Maybe a] -&gt; Maybe a
firstJust []             = Nothing
firstJust (Nothing:ms)   = firstJust ms
firstJust (j@(Just _):_) = j


From there we can deploy our usual bag of tricks higher-order functions to write this more idiomatically.

firstJust :: [Maybe a] -&gt; Maybe a
firstJust = foldr orElse Nothing
    where
        orElse :: Maybe a -&gt; Maybe a -&gt; Maybe a
        orElse Nothing    m = m
        orElse j@(Just _) _ = j


Now a few facts may percolate to the top of your brain and strike you as insight.


Maybe encapsulates a notion of &ldquo;failure&rdquo;.
orElse provides an alternative value in the case of failure.
Alternative is a typeclass that exists.


So we get a spiffy one-liner&mdash;

firstJust :: [Maybe a] -&gt; Maybe a
firstJust = foldr (&lt;|&gt;) Nothing


From the Typeclass Down

This all depends on which tools you're most familiar with. If you're a Monoid fan and used to relying on newtypes you may recognize the pattern of condensing a list of values as Control.Monoid.mconcat. Then you just need to write or choose an appropriate Monoid instance, and of course if you're really on top of the ball you'll know that one already exists.

import Control.Monoid
import Data.Coerce (coerce) -- GHC 7.8.1

firstJust :: [Maybe a] -&gt; Maybe a
firstJust = getFirst . mconcat . coerce
-- Compatible with older GHC but slower, `getFirst . mconcat . map First`


Just the Answer

This version relies on MonadPlus, which ties up all of the other concepts we used and provides various handy functions to leverage their combined power. This version is as terse as it's gonna get.

import Control.Monad (msum) -- Or, Control.Applicative.asum

checkWon :: Board -&gt; Maybe col
checkWon board = msum [ east moves col n
                      , west moves col n
                      , north moves col n
                      , south moves col n
                      ]
    where
        moves = pieces board
        n     = target board
        col   = snd (moves !! 0)