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]]
