Curious demand in a function parameter

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Curious demand in a function parameter

Ömer Sinan Ağacan
Hi,

In this program

    {-# LANGUAGE MagicHash #-}

    module Lib where

    import Control.Exception
    import GHC.Exts
    import GHC.IO

    data Err = Err
      deriving (Show)
    instance Exception Err

    f :: Int -> Int -> IO Int
    f x y | x > 0     = IO (raiseIO# (toException Err))
          | y > 0     = return 1
          | otherwise = return 2

when I compile this with 8.4 -O2 I get a strict demand on `y`:

    f :: Int -> Int -> IO Int
    [GblId,
     Arity=3,
     Str=<S(S),1*U(U)><S(S),1*U(U)><S,U>,
     ...]

but clearly `y` is not used on all code paths, so I don't understand why we
have a strict demand here.

I found this example in the comments around `raiseIO#`:

    -- raiseIO# needs to be a primop, because exceptions in the IO monad
    -- must be *precise* - we don't want the strictness analyser turning
    -- one kind of bottom into another, as it is allowed to do in pure code.
    --
    -- But we *do* want to know that it returns bottom after
    -- being applied to two arguments, so that this function is strict in y
    --     f x y | x>0       = raiseIO blah
    --           | y>0       = return 1
    --           | otherwise = return 2

However it doesn't explain why we want be strict on `y`.

Interestingly, when I try to make GHC generate a worker and a wrapper for this
function to make the program fail by evaluating `y` eagerly I somehow got a
lazy demand on `y`:

    {-# LANGUAGE MagicHash #-}

    module Main where

    import Control.Exception
    import GHC.Exts
    import GHC.IO

    data Err = Err
      deriving (Show)
    instance Exception Err

    f :: Int -> Int -> IO Int
    f x y | x > 0     = IO (raiseIO# (toException Err))
          | y > 0     = f x (y - 1)
          | otherwise = f (x - 1) y

    main = f 1 undefined

I was thinking that this program should fail with "undefined" instead of "Err",
but the demand I got for `f` became:

    f :: Int -> Int -> IO Int
    [GblId,
     Arity=2,
     Str=<S(S),1*U(U)><L,1*U(U)>,
     ...]

which makes sense to me. But I don't understand how my changes can change `y`s
demand, and why the original demand is strict rather than lazy. Could anyone
give me some pointers?

Thanks

Ömer
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

Re: Curious demand in a function parameter

Sebastian Graf
Hey,

the problem is with eta-expansion in this case, I believe, or rather the lack there-of.
Your recursive `f` is always bottoming out, which makes GHC not want to eta-expand the RealWorld# parameter (Note [State hack and bottoming functions] in CoreArity.hs is probably related).
If you change `f`s last branch to `return 2`, it's no longer (detectably) bottoming out and you get the 'desired' behavior:

test.exe: Prelude.undefined
CallStack (from HasCallStack):
  error, called at libraries\base\GHC\Err.hs:79:14 in base:GHC.Err
  undefined, called at test.hs:25:7 in main:Main

Greetings,
Sebastian




2018-03-25 9:14 GMT+02:00 Ömer Sinan Ağacan <[hidden email]>:
Hi,

In this program

    {-# LANGUAGE MagicHash #-}

    module Lib where

    import Control.Exception
    import GHC.Exts
    import GHC.IO

    data Err = Err
      deriving (Show)
    instance Exception Err

    f :: Int -> Int -> IO Int
    f x y | x > 0     = IO (raiseIO# (toException Err))
          | y > 0     = return 1
          | otherwise = return 2

when I compile this with 8.4 -O2 I get a strict demand on `y`:

    f :: Int -> Int -> IO Int
    [GblId,
     Arity=3,
     Str=<S(S),1*U(U)><S(S),1*U(U)><S,U>,
     ...]

but clearly `y` is not used on all code paths, so I don't understand why we
have a strict demand here.

I found this example in the comments around `raiseIO#`:

    -- raiseIO# needs to be a primop, because exceptions in the IO monad
    -- must be *precise* - we don't want the strictness analyser turning
    -- one kind of bottom into another, as it is allowed to do in pure code.
    --
    -- But we *do* want to know that it returns bottom after
    -- being applied to two arguments, so that this function is strict in y
    --     f x y | x>0       = raiseIO blah
    --           | y>0       = return 1
    --           | otherwise = return 2

However it doesn't explain why we want be strict on `y`.

Interestingly, when I try to make GHC generate a worker and a wrapper for this
function to make the program fail by evaluating `y` eagerly I somehow got a
lazy demand on `y`:

    {-# LANGUAGE MagicHash #-}

    module Main where

    import Control.Exception
    import GHC.Exts
    import GHC.IO

    data Err = Err
      deriving (Show)
    instance Exception Err

    f :: Int -> Int -> IO Int
    f x y | x > 0     = IO (raiseIO# (toException Err))
          | y > 0     = f x (y - 1)
          | otherwise = f (x - 1) y

    main = f 1 undefined

I was thinking that this program should fail with "undefined" instead of "Err",
but the demand I got for `f` became:

    f :: Int -> Int -> IO Int
    [GblId,
     Arity=2,
     Str=<S(S),1*U(U)><L,1*U(U)>,
     ...]

which makes sense to me. But I don't understand how my changes can change `y`s
demand, and why the original demand is strict rather than lazy. Could anyone
give me some pointers?

Thanks

Ömer
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

RE: Curious demand in a function parameter

GHC - devs mailing list
In reply to this post by Ömer Sinan Ağacan
|  but clearly `y` is not used on all code paths, so I don't understand
|  why we have a strict demand here.

Why is f strict in y?

Consider

factorial n acc
  | n <0 = error "bad arg"
  | n==1 = acc
  | otherwise = factorial (n-1) (n*acc)

Is this strict in 'acc'.  GHC says "yes" because it's MUCH more efficient to be strict in 'acc'; and we don’t to the thrown off by the error case.

Also, formally, is  (factorial n bottom) equal to bottom? Yes, even if n<0, because error returns bottom.


---------------

|  Interestingly, when I try to make GHC generate a worker and a wrapper
|  for this function to make the program fail by evaluating `y` eagerly I
|  somehow got a lazy demand on `y`:

That's a little more surprising to me, but as Sebastian point out you have now written a function that ALWAYS returns bottom.  That's not a very interesting function!  And GHC doesn't make much effort to optimise it.  I have not looked into why GHC doesn't eta-expand guaranteed-bottom functions, but I bet there's a note about it.  And I don't care much!

Simon


|  -----Original Message-----
|  From: ghc-devs <[hidden email]> On Behalf Of Ömer Sinan
|  Agacan
|  Sent: 25 March 2018 08:14
|  To: ghc-devs <[hidden email]>
|  Subject: Curious demand in a function parameter
|  
|  Hi,
|  
|  In this program
|  
|      {-# LANGUAGE MagicHash #-}
|  
|      module Lib where
|  
|      import Control.Exception
|      import GHC.Exts
|      import GHC.IO
|  
|      data Err = Err
|        deriving (Show)
|      instance Exception Err
|  
|      f :: Int -> Int -> IO Int
|      f x y | x > 0     = IO (raiseIO# (toException Err))
|            | y > 0     = return 1
|            | otherwise = return 2
|  
|  when I compile this with 8.4 -O2 I get a strict demand on `y`:
|  
|      f :: Int -> Int -> IO Int
|      [GblId,
|       Arity=3,
|       Str=<S(S),1*U(U)><S(S),1*U(U)><S,U>,
|       ...]
|  
|  but clearly `y` is not used on all code paths, so I don't understand
|  why we have a strict demand here.
|  
|  I found this example in the comments around `raiseIO#`:
|  
|      -- raiseIO# needs to be a primop, because exceptions in the IO
|  monad
|      -- must be *precise* - we don't want the strictness analyser
|  turning
|      -- one kind of bottom into another, as it is allowed to do in pure
|  code.
|      --
|      -- But we *do* want to know that it returns bottom after
|      -- being applied to two arguments, so that this function is strict
|  in y
|      --     f x y | x>0       = raiseIO blah
|      --           | y>0       = return 1
|      --           | otherwise = return 2
|  
|  However it doesn't explain why we want be strict on `y`.
|  
|  Interestingly, when I try to make GHC generate a worker and a wrapper
|  for this function to make the program fail by evaluating `y` eagerly I
|  somehow got a lazy demand on `y`:
|  
|      {-# LANGUAGE MagicHash #-}
|  
|      module Main where
|  
|      import Control.Exception
|      import GHC.Exts
|      import GHC.IO
|  
|      data Err = Err
|        deriving (Show)
|      instance Exception Err
|  
|      f :: Int -> Int -> IO Int
|      f x y | x > 0     = IO (raiseIO# (toException Err))
|            | y > 0     = f x (y - 1)
|            | otherwise = f (x - 1) y
|  
|      main = f 1 undefined
|  
|  I was thinking that this program should fail with "undefined" instead
|  of "Err", but the demand I got for `f` became:
|  
|      f :: Int -> Int -> IO Int
|      [GblId,
|       Arity=2,
|       Str=<S(S),1*U(U)><L,1*U(U)>,
|       ...]
|  
|  which makes sense to me. But I don't understand how my changes can
|  change `y`s demand, and why the original demand is strict rather than
|  lazy. Could anyone give me some pointers?
|  
|  Thanks
|  
|  Ömer
|  _______________________________________________
|  ghc-devs mailing list
|  [hidden email]
|  https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmail.h
|  askell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-
|  devs&data=02%7C01%7Csimonpj%40microsoft.com%7C79b17f4f38684f15c4a308d5
|  9220339a%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6365755894481854
|  06&sdata=T7ZJjz%2Bek5CFAUYtt9CmEkRLIml0OAj8yEU5fTsuFC0%3D&reserved=0
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs