content: module Main where
  
  import Data.List
  
  -- These four functions should all wind up the same; they represent successive
  -- simplifications that should happen. (Actual details may vary, since find
  -- isn't quite defined this way, but the differences disappear by the end.)
  
  firstEvenIsPositive1 :: [Int] -> Bool
  firstEvenIsPositive1 = maybe False (> 0) . find even
  
  -- After inlining:
  
  firstEvenIsPositive2 :: [Int] -> Bool
  firstEvenIsPositive2 xs =
    let go xs = case xs of x:xs' -> if even x then Just x else go xs'
                           []    -> Nothing
    in case go xs of Just n  -> n > 0
                     Nothing -> False
  
  -- Note that go *could* be a join point if it were declared inside the scrutinee
  -- instead of outside. So it's now Float In's job to move the binding inward a
  -- smidge. *But* if it goes too far inward (as it would until recently), it will
  -- wrap only "go" instead of "go xs", which won't let us mark go as a join point
  -- since join points can't be partially invoked.
  --
  -- After Float In:
  
  firstEvenIsPositive3 :: [Int] -> Bool
  firstEvenIsPositive3 xs =
    case let {-join-} go xs = case xs of x:xs' -> if even x then Just x
                                                            else go xs'
                                         []    -> Nothing
         in go xs of
      Just n  -> n > 0
      Nothing -> False
  
  -- After the simplifier:
  
  firstEvenIsPositive4 :: [Int] -> Bool
  firstEvenIsPositive4 xs =
    let {-join-} go xs = case xs of x:xs' -> if even x then x > 0 else go xs'
                                    []    -> False
    in go xs
  
  -- This only worked because go was a join point so that the case gets moved
  -- inside.
  
  {-# NOINLINE firstEvenIsPositive1 #-}
  
  main = print $ or $ [firstEvenIsPositive1 [1,3..n] | n <- [1..10000]]
  