Project Euler # 95 in Haskell - kind-of finding longest cycle in a graph
If something can't happen, put your money on it and make it crash if it does anyway instead of silently carrying on.

maximumOn deserves its own name to lift our burden of manual tuple juggling.

Adding elements to the end of a list smells. Let me reverse the list order to fix that.

The scanl with the booleans looks degenerated, there must be a better way - let me undo its introduction. The booleans can be replaced with conditional recursion. The set need not be returned in the cases that used to return True. The list entries that are to be later returned need not be passed deeper into the recursion - and then the list need not be passed down at all. Incidentally, this restores the original list order.

Let's look what I got so far:

maximumOn f = fst . maximumBy (comparing snd) . map (id &amp;&amp;&amp; f)

main :: IO ()
main = print $ maximumOn amicableChainLength [2..lim] where
  amicableChainLength x
    | sumOfProperDivisors x == x = 1 -- perfect numbers
    | otherwise = (\list -&gt; case elemIndices (last list) list of (a:b:_) -&gt; b - a)
      $ include x Set.empty
      $ iterate sumOfProperDivisors x
  -- tries to include number into set/list and also tell when to stop
  include x set (n:ns)
    -- number is member of observed numbers
    | Set.member n set = if x==n
        then [n] -- chain should start at that number
        else error "otherwise no meaning"
    -- this cannot be answer since we are required to find minimum element of that chain
    | n &gt; lim || n &lt; 2 || n &lt; x = error "out of bounds or an number earlier encountered"
    -- New number, add it &amp; continue
    | otherwise = n : include x (Set.insert n set) ns


This code kinda looks like the error cases do happen regularly. Were your comments under the assumption that we are currently looking at the x that is going to win the maximumBy contest? Let me use list comprehension syntax to skip through erroring xs without hacking our way through the data we happen to be working with too much. (Sadly this requires pulling back of parts of maximumOn.) Also the perfect numbers case seems to be subsumed by the other. Also the last element of the list is x if we're interested in it. Also the two positions of x are always in the front and back so we can just compare lengths, and we need not even pass the list out of include.

main :: IO ()
main = print $ fst $ maximumBy (comparing snd)
  [ (x, l)
  | x &lt;- [2..lim]
  , let Just l = include x Set.empty $ iterate sumOfProperDivisors x
  ] where
  -- tries to include number into set/list and also tell when to stop
  include x set (n:ns)
    -- number is member of observed numbers
    | Set.member n set = if x==n
        then Just 0
        else Nothing
    -- this cannot be answer since we are required to find minimum element of that chain
    | n &gt; lim || n &lt; 2 || n &lt; x = Nothing
    -- New number, add it &amp; continue
    | otherwise = (+1) &lt;$&gt; include x (Set.insert n set) ns


I now notice that list comprehensions don't actually use the Alternative instance of lists within let expressions. Ah well, since the Ord instance for Maybe happens to make Nothing smaller than Just anything, I can get rid of the list comprehension again, and restore maximumOn!

main :: IO ()
main = print $ maximumOn (\x -&gt; include x Set.empty $ iterate sumOfProperDivisors x)
  [2..lim] where
  -- tries to include number into set/list and also tell when to stop
  include x set (n:ns)
    -- number is member of observed numbers
    | Set.member n set = if x==n
        then Just 0
        else Nothing
    -- this cannot be answer since we are required to find minimum element of that chain
    | n &gt; lim || n &lt; 2 || n &lt; x = Nothing
    -- New number, add it &amp; continue
    | otherwise = (+1) &lt;$&gt; include x (Set.insert n set) ns


n&lt;2 is subsumed in n&lt;x, the chain length is monotonous with the set size, and do notation feels like it might help with include.

main :: IO ()
main = print $ maximumOn (\x -&gt; include x Set.empty x) [2..lim] where
  -- set keeps track of previously seen numbers
  include x set n = do
    guard $ n &lt;= lim &amp;&amp; n &gt;= x
    if Set.member n set
      then guard (x == n) &gt;&gt; Just (Set.size set)
      else include x (Set.insert n set) (sumOfProperDivisors n)


I have a hunch there's a way to decompose include completely, but I don't see it.