bang patterns give fundamentally new capabilities?

I was recently presented with the problem of writing a function like so seqInt__ :: forall a . a -> Int# -> Int# seqInt__ x y = x `seq` y which seems fine, except 'seq' of type forall a b . a -> b -> b cannot be applied to an unboxed value. I could not think of a way to actually get the behavior I wanted until I remembered bang patterns. seqInt__ :: forall a . a -> Int# -> Int# seqInt__ !x y = y which seems to work! my question is, is this actually something fundamentally new that bang patterns allow or was I just not able to figure out the "old" way to do it? Also, is there a way to do something similar but for 'lazy' rather than 'seq'? I want something of type type World__ = State# RealWorld {-# NOINLINE newWorld__ #-} newWorld__ :: a -> World__ newWorld__ x = realWord# -- ??? except that I need newWorld__ to be lazy in its first argument. I need to convince the opimizer that the World__ newWorld__ is returning depends on the argument passed to newWorld__. using any wacky ghc primitives in 6.6 is fine, I would just like something that stands up to ghc -O2. when compiling with -O, ghc is too clever and sees through tricks like this. (at least, that is what I think is happening) John -- John Meacham - ⑆repetae.net⑆john⑈

On Thu, Nov 30, 2006 at 08:13:13PM -0800, John Meacham wrote:
I was recently presented with the problem of writing a function like so
seqInt__ :: forall a . a -> Int# -> Int# seqInt__ x y = x `seq` y
which seems fine, except 'seq' of type forall a b . a -> b -> b cannot be applied to an unboxed value.
I could not think of a way to actually get the behavior
How about something like this: seqInt__ :: forall a . a -> Int# -> Int# seqInt__ x y = case x `seq` (I# y) of (I# y') -> y' The question is: will GHC optimize out the unneccesary boxing and unboxing? Looking at the output from "ghc -O2 -ddump-simpl" makes me think the answer is "yes". Best regards Tomasz

| I was recently presented with the problem of writing a function like so | | seqInt__ :: forall a . a -> Int# -> Int# | seqInt__ x y = x `seq` y | | which seems fine, except 'seq' of type forall a b . a -> b -> b cannot | be applied to an unboxed value. Actually it works fine. Did you try it? Seq is special because its second type argument can be instantiated to an unboxed type. I see that is not documented in the user manual; it should be. GHC has a kinding system that looks quite similar to the one you described for jhc. Here's teh comment from compiler/Type.lhs ? / \ / \ ?? (#) / \ * # where * [LiftedTypeKind] means boxed type # [UnliftedTypeKind] means unboxed type (#) [UbxTupleKind] means unboxed tuple ?? [ArgTypeKind] is the lub of *,# ? [OpenTypeKind] means any type at all | Also, is there a way to do something similar but for 'lazy' rather than | 'seq'? I want something of type | | type World__ = State# RealWorld | | {-# NOINLINE newWorld__ #-} | newWorld__ :: a -> World__ | newWorld__ x = realWord# -- ??? | | except that I need newWorld__ to be lazy in its first argument. I need | to convince the opimizer that the World__ newWorld__ is returning | depends on the argument passed to newWorld__. I don't understand what you meant here. The definition of newWorld__ that you give is, of course, lazy in x. Simon

On Sat, Dec 02, 2006 at 11:02:28PM +0000, Simon Peyton-Jones wrote:
| I was recently presented with the problem of writing a function like so | | seqInt__ :: forall a . a -> Int# -> Int# | seqInt__ x y = x `seq` y | | which seems fine, except 'seq' of type forall a b . a -> b -> b cannot | be applied to an unboxed value.
Actually it works fine. Did you try it? Seq is special because its second type argument can be instantiated to an unboxed type. I see that is not documented in the user manual; it should be.
I was getting this problem, http://hackage.haskell.org/trac/ghc/ticket/1031 I assumed it was because I was passing an unboxed value to seq because when I switched them all to bang patterns, it started to work. but I guess it was a different issue alltogether.
GHC has a kinding system that looks quite similar to the one you described for jhc. Here's teh comment from compiler/Type.lhs
? / \ / \ ?? (#) / \ * #
where * [LiftedTypeKind] means boxed type # [UnliftedTypeKind] means unboxed type (#) [UbxTupleKind] means unboxed tuple ?? [ArgTypeKind] is the lub of *,# ? [OpenTypeKind] means any type at all
yup. certainly not an accident. :) incidentally, (tangent) the more I think about it after writing my other mail, my rule ((#),?,!) seems to be not very useful, the only reason it makes a difference is because of the existence of 'seq' which lets me tell the difference between _|_ and \_ -> _|_. replacing it wich ((#),?,#->) where #-> is the kind of unboxed functions. with no rule of the form (#->,?,?) means that it is statically guarenteed things that take unboxed tuples are always fully applied to their arguments. i.e. exactly what we want for join points or other functions we wish to ensure become loops. Seems much more useful than functions of kind '!'. (end tangent)
| Also, is there a way to do something similar but for 'lazy' rather than | 'seq'? I want something of type | | type World__ = State# RealWorld | | {-# NOINLINE newWorld__ #-} | newWorld__ :: a -> World__ | newWorld__ x = realWord# -- ??? | | except that I need newWorld__ to be lazy in its first argument. I need | to convince the opimizer that the World__ newWorld__ is returning | depends on the argument passed to newWorld__.
I don't understand what you meant here. The definition of newWorld__ that you give is, of course, lazy in x.
it is getting type 'Absent' assigned to it by the demand analysis, I want it to be lazy (and not strict) 3 newWorld__ :: a -> World__ {- Arity: 1 HasNoCafRefs Strictness: A -} John -- John Meacham - ⑆repetae.net⑆john⑈

On 12/3/06, John Meacham
On Sat, Dec 02, 2006 at 11:02:28PM +0000, Simon Peyton-Jones wrote: [snip]
| Also, is there a way to do something similar but for 'lazy' rather than | 'seq'? I want something of type | | type World__ = State# RealWorld | | {-# NOINLINE newWorld__ #-} | newWorld__ :: a -> World__ | newWorld__ x = realWord# -- ??? | | except that I need newWorld__ to be lazy in its first argument. I need | to convince the opimizer that the World__ newWorld__ is returning | depends on the argument passed to newWorld__.
I don't understand what you meant here. The definition of newWorld__ that you give is, of course, lazy in x.
it is getting type 'Absent' assigned to it by the demand analysis, I want it to be lazy (and not strict)
3 newWorld__ :: a -> World__ {- Arity: 1 HasNoCafRefs Strictness: A -}
Well, yeah, that's because it *is* absent. If you want to convince the demand analyzer that it isn't, then use x somewhere on the right-hand side of the definition of newWorld__. Maybe I could be more helpful if I knew what you were really trying to do here? (My best guess is that you're trying to implement your own IO monad, which really shouldn't be possible AFAIK unless there's something seriously wrong with GHC that I don't know about. Unless you use The Function That Shall Not Be Named.) Cheers, Kirsten -- Kirsten Chevalier* chevalier@alum.wellesley.edu *Often in error, never in doubt

| > | Also, is there a way to do something similar but for 'lazy' rather than | > | 'seq'? I want something of type | > | | > | type World__ = State# RealWorld | > | | > | {-# NOINLINE newWorld__ #-} | > | newWorld__ :: a -> World__ | > | newWorld__ x = realWord# -- ??? | > | | > | except that I need newWorld__ to be lazy in its first argument. I need | > | to convince the opimizer that the World__ newWorld__ is returning | > | depends on the argument passed to newWorld__. | > | > I don't understand what you meant here. The definition of newWorld__ that you give is, of course, | lazy in x. | | it is getting type 'Absent' assigned to it by the demand analysis, I | want it to be lazy (and not strict) Ah I think I understand now. You want a lazy primop discard# :: a -> () Now you can write newWorld x = discard x `seq` realWorld# The code generator treats (discard# x) as (), and (case (discard# x) of () -> e) as e. It should be a primop so that this behaviour is not exposed too early. An alternative would be to do the transformation in the core-to-STG step, but that might be too early. Still easier would be to write discard x = () {-# NOINLINE[0] discard #-} to prevent it getting inlined until the final stages of the optmisier. The trouble is that I have no idea of what it means to expose discard "too early" is in your case. Not hard to implement if you feel like doing so. Simon
participants (4)
-
John Meacham
-
Kirsten Chevalier
-
Simon Peyton-Jones
-
Tomasz Zielonka