-XStrict: Why some binders are not made strict?

Let's say I have this code: zip :: [a] -> [b] -> [(a, b)] zip [] [] = [] zip (x : xs) (y : ys) = (x, y) : zip xs ys With -XStrict 'x', 'xs', 'y' and 'ys' don't become strict. I'm wondering about the motivation behind this, I found this interesting. I always thought -XStrict gives me this guarantee: If I'm using an already-defined variable(bound by a let or pattern matching) in an expression, I can be sure that the variable won't be bottom in that expression, because it would be `seq`d before the expression is evaluated. So if I have case ... of D x y -> <body> or let x = ... y = ... in <body> In both cases I was thinking that in <body> 'x' and 'y' can't be bottom(with -XStrict). This would make -XStrict programs evaluate like they would in a call-by-value language(even though in the RTS level thunks will be built). Variables can't range over computations; all binders evaluated strictly etc. Can anyone tell me about the motivation behind this decision? I think the wiki page actually conflicts with itself. It says "... bindings to be strict by default" but then in "case expressions" sections says case x of (a,b) -> rhs is interpreted as case x of !(a,b) -> rhs Here bindings 'a' and 'b' are not made strict. I'd expect something like: case x of (!a,!b) -> rhs (Which seems to be invalid Haskell, but same effect could be achieved with `seq a (seq b rhs)`) Thanks.. (I also realized that the wiki page doesn't mention bindings in do syntax, is it because this case is implied by "function definitions"? That is, bangs are added after do syntax is desugared and so they become strict?)

On Mon, Dec 7, 2015 at 8:40 PM, Ömer Sinan Ağacan
With -XStrict 'x', 'xs', 'y' and 'ys' don't become strict. I'm wondering about the motivation behind this, I found this interesting. I always thought -XStrict gives me this guarantee: If I'm using an already-defined variable(bound by a let or pattern matching) in an expression, I can be sure that the variable won't be bottom in that expression, because it would be `seq`d before the expression is evaluated.
Aren't those already guaranteed to be strict because of pattern matching? Try it again with irrefutable patterns. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Aren't those already guaranteed to be strict because of pattern matching? Try it again with irrefutable patterns.
But pattern matching only forces the evaluation up to the pattern that is
matched. We need another pattern matching(or seq etc.) on x, y, xs and ys here.
If you look at the generated Core you'll see it more clearly I think(you'll see
that no pattern matching on x y xs and ys are done in Core).
2015-12-07 20:43 GMT-05:00 Brandon Allbery
On Mon, Dec 7, 2015 at 8:40 PM, Ömer Sinan Ağacan
wrote: With -XStrict 'x', 'xs', 'y' and 'ys' don't become strict. I'm wondering about the motivation behind this, I found this interesting. I always thought -XStrict gives me this guarantee: If I'm using an already-defined variable(bound by a let or pattern matching) in an expression, I can be sure that the variable won't be bottom in that expression, because it would be `seq`d before the expression is evaluated.
Aren't those already guaranteed to be strict because of pattern matching? Try it again with irrefutable patterns.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Adam, Johan,
Looking at the user manual
http://downloads.haskell.org/~ghc/master/users-guide/glasgow_exts.html#stric...,
and indeed the wiki page
https://ghc.haskell.org/trac/ghc/wiki/StrictPragma
it's not really clear whether the sub-components of a pattern are strict. That is, is the second equation of zip strict in x, and xs? (Supposing for now that the list data structure is lazy). The manual doesn't say one way or the other.
What's the answer? And could the user manual please say?
Thanks
Simon
| -----Original Message-----
| From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Ömer
| Sinan Agacan
| Sent: 08 December 2015 01:41
| To: ghc-devs

I'm snowed under but I promise I will try to reply soon! To think about in
the mean time: what do existing strict languages with pattern matching do?
On Tue, Dec 8, 2015 at 3:42 PM, Simon Peyton Jones
Adam, Johan,
Looking at the user manual
http://downloads.haskell.org/~ghc/master/users-guide/glasgow_exts.html#stric... , and indeed the wiki page https://ghc.haskell.org/trac/ghc/wiki/StrictPragma it's not really clear whether the sub-components of a pattern are strict. That is, is the second equation of zip strict in x, and xs? (Supposing for now that the list data structure is lazy). The manual doesn't say one way or the other.
What's the answer? And could the user manual please say?
Thanks
Simon
| -----Original Message----- | From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Ömer | Sinan Agacan | Sent: 08 December 2015 01:41 | To: ghc-devs
| Subject: -XStrict: Why some binders are not made strict? | | Let's say I have this code: | | zip :: [a] -> [b] -> [(a, b)] | zip [] [] = [] | zip (x : xs) (y : ys) = (x, y) : zip xs ys | | With -XStrict 'x', 'xs', 'y' and 'ys' don't become strict. I'm wondering | about | the motivation behind this, I found this interesting. I always thought - | XStrict | gives me this guarantee: If I'm using an already-defined variable(bound by | a | let or pattern matching) in an expression, I can be sure that the variable | won't be bottom in that expression, because it would be `seq`d before the | expression is evaluated. | | So if I have | | case ... of | D x y -> <body> | | or | | let x = ... | y = ... | in <body> | | In both cases I was thinking that in <body> 'x' and 'y' can't be | bottom(with | -XStrict). This would make -XStrict programs evaluate like they would in a | call-by-value language(even though in the RTS level thunks will be built). | Variables can't range over computations; all binders evaluated strictly | etc. | | Can anyone tell me about the motivation behind this decision? | | I think the wiki page actually conflicts with itself. It says "... | bindings to be | strict by default" but then in "case expressions" sections says | | case x of (a,b) -> rhs | | is interpreted as | | case x of !(a,b) -> rhs | | Here bindings 'a' and 'b' are not made strict. I'd expect something like: | | case x of (!a,!b) -> rhs | | (Which seems to be invalid Haskell, but same effect could be achieved with | `seq | a (seq b rhs)`) | | Thanks.. | | (I also realized that the wiki page doesn't mention bindings in do syntax, | is | it because this case is implied by "function definitions"? That is, bangs | are | added after do syntax is desugared and so they become strict?) | _______________________________________________ | ghc-devs mailing list | ghc-devs@haskell.org | https://na01.safelinks.protection.outlook.com/?url=http%3a%2f%2fmail.haske | ll.org%2fcgi-bin%2fmailman%2flistinfo%2fghc- | devs&data=01%7c01%7csimonpj%40064d.mgd.microsoft.com %7cbc68c0830f574466efd | 308d2ff70aba9%7c72f988bf86f141af91ab2d7cd011db47%7c1&sdata=c2VBbt%2f%2fR2c | yFecGEuQotO%2bV71VSbpmWnZJyV9d8KRk%3d

When implementing I took this:
Notice that we do not put bangs on nested patterns. For example let (p,q) = if flob then (undefined, undefined) else (True, False) in ... will behave like let !(p,q) = if flob then (undefined, undefined) else (True, False) in ...
(from the spec) and applied it to all bindings, but I don't know if this is the best implementation. Cheers, Adam On Thu, 10 Dec 2015, at 03:34 PM, Johan Tibell wrote:
I'm snowed under but I promise I will try to reply soon! To think about in the mean time: what do existing strict languages with pattern matching do?
On Tue, Dec 8, 2015 at 3:42 PM, Simon Peyton Jones
wrote: Adam, Johan,
Looking at the user manual
http://downloads.haskell.org/~ghc/master/users-guide/glasgow_exts.html#stric...,
and indeed the wiki page
it's not really clear whether the sub-components of a pattern are strict. That is, is the second equation of zip strict in x, and xs? (Supposing for now that the list data structure is lazy). The manual doesn't say one way or the other.
What's the answer? And could the user manual please say?
Thanks
Simon
| -----Original Message-----
| From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Ömer
| Sinan Agacan
| Sent: 08 December 2015 01:41
| To: ghc-devs
| Subject: -XStrict: Why some binders are not made strict?
|
| Let's say I have this code:
|
|zip :: [a] -> [b] -> [(a, b)]
|zip [] [] = []
|zip (x : xs) (y : ys) = (x, y) : zip xs ys
|
| With -XStrict 'x', 'xs', 'y' and 'ys' don't become strict. I'm | wondering
| about
| the motivation behind this, I found this interesting. I always | thought -
| XStrict
| gives me this guarantee: If I'm using an already-defined | variable(bound by
| a
| let or pattern matching) in an expression, I can be sure that the | variable
| won't be bottom in that expression, because it would be `seq`d | before the
| expression is evaluated.
|
| So if I have
|
|case ... of
|D x y -> <body>
|
| or
|
|let x = ...
|y = ...
|in <body>
|
| In both cases I was thinking that in <body> 'x' and 'y' can't be
| bottom(with
| -XStrict). This would make -XStrict programs evaluate like they | would in a
| call-by-value language(even though in the RTS level thunks will | be built).
| Variables can't range over computations; all binders evaluated | strictly
| etc.
|
| Can anyone tell me about the motivation behind this decision?
|
| I think the wiki page actually conflicts with itself. It says "...
| bindings to be
| strict by default" but then in "case expressions" sections says
|
|case x of (a,b) -> rhs
|
|is interpreted as
|
|case x of !(a,b) -> rhs
|
| Here bindings 'a' and 'b' are not made strict. I'd expect | something like:
|
|case x of (!a,!b) -> rhs
|
| (Which seems to be invalid Haskell, but same effect could be | achieved with
| `seq
| a (seq b rhs)`)
|
| Thanks..
|
| (I also realized that the wiki page doesn't mention bindings in | do syntax,
| is
| it because this case is implied by "function definitions"? That | is, bangs
| are
| added after do syntax is desugared and so they become strict?)
| _______________________________________________
| ghc-devs mailing list
| ghc-devs@haskell.org
| https://na01.safelinks.protection.outlook.com/?url=http%3a%2f%2fmail.haske
| ll.org%2fcgi-bin%2fmailman%2flistinfo%2fghc-
| devs&data=01%7c01%7csimonpj%40064d.mgd.microsoft.com%7cbc68c0830f5- | 74466efd
| 308d2ff70aba9%7c72f988bf86f141af91ab2d7cd011db47%7c1&sdata=c2VBbt%- | 2f%2fR2c
| yFecGEuQotO%2bV71VSbpmWnZJyV9d8KRk%3d

On 12/10/2015 04:34 PM, Johan Tibell wrote:
I'm snowed under but I promise I will try to reply soon! To think about in the mean time: what do existing strict languages with pattern matching do?
Well, strict languages do not have lazy data to force to begin with, do they? Personally, I find the simple intuition of "all patterns are strict by default" rather appealing. E.g. I wouldn't expect the expressions let (v1,v2) = a in f v2 and let (v1,v2) = a; v3 = v2 in f v3 to have different semantics. If we decide to adopt this semantics, we need to address the meaning of the pattern ~(v1, v2) under -XStrict. Intuitively, ~ should propagate to the subpatterns. An alternative is to disallow this pattern under -XStrict and require writing all ~s explicitly, which may get tedious: ~(~v1, ~v2) ~(~v1, ~(~v2, ~v3)) etc. We also need to ensure the consistency between this extension and the unlifted data types proposal [1], given their similarity. Interestingly, I don't see constructor patterns explained there either. [1]: https://ghc.haskell.org/trac/ghc/wiki/UnliftedDataTypes#Dynamicsemanticsofun...

I believe Scala has optional lazy values, but you could also consider in
strict languages if you do manual thunking.
If we force strictness all the way down it's not really call-by-value
either, because the caller doesn't know what to evaluate (I think).
In addition, making pattern matching strict in this way makes it hard to
mix and match strict and lazy data types (e.g. Maybe), because using a lazy
data type from another module will make it appear strict in your code
(hurting modularity).
On Fri, Dec 11, 2015 at 7:54 AM, Roman Cheplyaka
On 12/10/2015 04:34 PM, Johan Tibell wrote:
I'm snowed under but I promise I will try to reply soon! To think about in the mean time: what do existing strict languages with pattern matching do?
Well, strict languages do not have lazy data to force to begin with, do they?
Personally, I find the simple intuition of "all patterns are strict by default" rather appealing.
E.g. I wouldn't expect the expressions
let (v1,v2) = a in f v2
and
let (v1,v2) = a; v3 = v2 in f v3
to have different semantics.
If we decide to adopt this semantics, we need to address the meaning of the pattern
~(v1, v2)
under -XStrict. Intuitively, ~ should propagate to the subpatterns. An alternative is to disallow this pattern under -XStrict and require writing all ~s explicitly, which may get tedious:
~(~v1, ~v2) ~(~v1, ~(~v2, ~v3)) etc.
We also need to ensure the consistency between this extension and the unlifted data types proposal [1], given their similarity. Interestingly, I don't see constructor patterns explained there either.
[1]: https://ghc.haskell.org/trac/ghc/wiki/UnliftedDataTypes#Dynamicsemanticsofun...
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

On 12/11/2015 02:21 PM, Johan Tibell wrote:
If we force strictness all the way down it's not really call-by-value either, because the caller doesn't know what to evaluate (I think).
Not sure what you mean here.
In addition, making pattern matching strict in this way makes it hard to mix and match strict and lazy data types (e.g. Maybe), because using a lazy data type from another module will make it appear strict in your code (hurting modularity).
I don't think this is a case about modularity. A lazy Maybe value defined in a lazy module remains lazy; and you can pass it to lazy functions without forcing it. Only when you pattern match on it *in the strict module*, the evaluation happens. As I said, I prefer this semantics mainly because it's easier to explain: all variables (and underscores) bound in a strict module refer to WHNF values. Do you have a similarly simple explanation for the semantics you're suggesting? Roman

I agree with Roman here.
Probably another reason for making every binding strict is this: (sorry if this
is mentioned)
Suppose I imported `data D = D ...` from another library and I'm in -XStrict.
In this code:
case ... of
D b1 b2 ... -> <body>
I should be able to assume that b1, b2 ... etc. are all strict(that is, WHNF in
<body>) becuase I'm in -XStrict. This also makes the behavior more consistent,
I think.
2015-12-11 7:57 GMT-05:00 Roman Cheplyaka
On 12/11/2015 02:21 PM, Johan Tibell wrote:
If we force strictness all the way down it's not really call-by-value either, because the caller doesn't know what to evaluate (I think).
Not sure what you mean here.
In addition, making pattern matching strict in this way makes it hard to mix and match strict and lazy data types (e.g. Maybe), because using a lazy data type from another module will make it appear strict in your code (hurting modularity).
I don't think this is a case about modularity. A lazy Maybe value defined in a lazy module remains lazy; and you can pass it to lazy functions without forcing it. Only when you pattern match on it *in the strict module*, the evaluation happens.
As I said, I prefer this semantics mainly because it's easier to explain: all variables (and underscores) bound in a strict module refer to WHNF values. Do you have a similarly simple explanation for the semantics you're suggesting?
Roman
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

| As I said, I prefer this semantics mainly because it's easier to
| explain: all variables (and underscores) bound in a strict module refer
| to WHNF values. Do you have a similarly simple explanation for the
| semantics you're suggesting?
Here's one, which is roughly what the current implementation does (modulo bugs):
* Code compiled under -XStrict constructs no thunks.
So consider
module M1 where data T = C Int Int
module M2 where f n = C (n+1) (n-1)
module M3 where g x = let C y z = f x in ...
Look at M3. Usually we'd get a thunk for (f 4), but not with -XStrict. But even with -XStrict in M3, y,z might be bound to thunks.
If you compile M2 with -XStrict, function f won't build thunks for (n+1), (n-1) but will evaluate them instead.
If you compile M1 with StrictData, then C is made strict, so again M2 will build no thunks even if M2 was compiled without -XStrict.
I quite like this design. It's not clear to me that anything useful is gained by forcing y and z in M3 before evaluating the body "...".
So Roman's design makes sense, but so does the implemented design (modulo any bugs). The trouble is that the implemented design is not well described.
Simon
| -----Original Message-----
| From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Roman
| Cheplyaka
| Sent: 11 December 2015 12:57
| To: Johan Tibell

Thanks Simon, this is an interesting and compelling interpretation. But I'm wondering whether it is enough to specify the dynamic semantics unambiguously. Two examples: 1. let _ = undefined in () Intuitively, since we are talking about /strictness/, this should evaluate to bottom. However, it seems that your rule also admits () as an answer; it is equivalent to () under lazy evaluation *and* it does not create any thunks. 2. f g = g undefined When compiled lazily, this code doesn't construct any thunks: f = \r srt:SRT:[02v :-> undefined] [g_suM] g_suM undefined; So, under your rule, this is an admissible code for -XStrict, too. But we can hardly call it strict. On 12/12/2015 12:38 AM, Simon Peyton Jones wrote:
| As I said, I prefer this semantics mainly because it's easier to | explain: all variables (and underscores) bound in a strict module refer | to WHNF values. Do you have a similarly simple explanation for the | semantics you're suggesting?
Here's one, which is roughly what the current implementation does (modulo bugs):
* Code compiled under -XStrict constructs no thunks.
So consider
module M1 where data T = C Int Int module M2 where f n = C (n+1) (n-1) module M3 where g x = let C y z = f x in ...
Look at M3. Usually we'd get a thunk for (f 4), but not with -XStrict. But even with -XStrict in M3, y,z might be bound to thunks.
If you compile M2 with -XStrict, function f won't build thunks for (n+1), (n-1) but will evaluate them instead.
If you compile M1 with StrictData, then C is made strict, so again M2 will build no thunks even if M2 was compiled without -XStrict.
I quite like this design. It's not clear to me that anything useful is gained by forcing y and z in M3 before evaluating the body "...".
So Roman's design makes sense, but so does the implemented design (modulo any bugs). The trouble is that the implemented design is not well described.
Simon
| -----Original Message----- | From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Roman | Cheplyaka | Sent: 11 December 2015 12:57 | To: Johan Tibell
| Cc: ghc-devs@haskell.org | Subject: Re: -XStrict: Why some binders are not made strict? | | On 12/11/2015 02:21 PM, Johan Tibell wrote: | > If we force strictness all the way down it's not really call-by-value | > either, because the caller doesn't know what to evaluate (I think). | | Not sure what you mean here. | | > In addition, making pattern matching strict in this way makes it hard to | > mix and match strict and lazy data types (e.g. Maybe), because using a | > lazy data type from another module will make it appear strict in your | > code (hurting modularity). | | I don't think this is a case about modularity. A lazy Maybe value | defined in a lazy module remains lazy; and you can pass it to lazy | functions without forcing it. Only when you pattern match on it *in the | strict module*, the evaluation happens. | | As I said, I prefer this semantics mainly because it's easier to | explain: all variables (and underscores) bound in a strict module refer | to WHNF values. Do you have a similarly simple explanation for the | semantics you're suggesting? | | Roman

Hello, Given the upcoming 8.0 feature freeze I think the correct approach for 8.0 is to document the current implementation (I'll try to do that this week). It would probably be good if interested parties would document their input in a ticket. Cheers, --Adam On Sat, 12 Dec 2015, at 12:55 AM, Roman Cheplyaka wrote:
Thanks Simon, this is an interesting and compelling interpretation. But I'm wondering whether it is enough to specify the dynamic semantics unambiguously.
Two examples:
1.
let _ = undefined in ()
Intuitively, since we are talking about /strictness/, this should evaluate to bottom. However, it seems that your rule also admits () as an answer; it is equivalent to () under lazy evaluation *and* it does not create any thunks.
2.
f g = g undefined
When compiled lazily, this code doesn't construct any thunks:
f = \r srt:SRT:[02v :-> undefined] [g_suM] g_suM undefined;
So, under your rule, this is an admissible code for -XStrict, too. But we can hardly call it strict.
On 12/12/2015 12:38 AM, Simon Peyton Jones wrote:
| As I said, I prefer this semantics mainly because it's easier to | explain: all variables (and underscores) bound in a strict module refer | to WHNF values. Do you have a similarly simple explanation for the | semantics you're suggesting?
Here's one, which is roughly what the current implementation does (modulo bugs):
* Code compiled under -XStrict constructs no thunks.
So consider
module M1 where data T = C Int Int module M2 where f n = C (n+1) (n-1) module M3 where g x = let C y z = f x in ...
Look at M3. Usually we'd get a thunk for (f 4), but not with -XStrict. But even with -XStrict in M3, y,z might be bound to thunks.
If you compile M2 with -XStrict, function f won't build thunks for (n+1), (n-1) but will evaluate them instead.
If you compile M1 with StrictData, then C is made strict, so again M2 will build no thunks even if M2 was compiled without -XStrict.
I quite like this design. It's not clear to me that anything useful is gained by forcing y and z in M3 before evaluating the body "...".
So Roman's design makes sense, but so does the implemented design (modulo any bugs). The trouble is that the implemented design is not well described.
Simon
| -----Original Message----- | From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Roman | Cheplyaka | Sent: 11 December 2015 12:57 | To: Johan Tibell
| Cc: ghc-devs@haskell.org | Subject: Re: -XStrict: Why some binders are not made strict? | | On 12/11/2015 02:21 PM, Johan Tibell wrote: | > If we force strictness all the way down it's not really call-by-value | > either, because the caller doesn't know what to evaluate (I think). | | Not sure what you mean here. | | > In addition, making pattern matching strict in this way makes it hard to | > mix and match strict and lazy data types (e.g. Maybe), because using a | > lazy data type from another module will make it appear strict in your | > code (hurting modularity). | | I don't think this is a case about modularity. A lazy Maybe value | defined in a lazy module remains lazy; and you can pass it to lazy | functions without forcing it. Only when you pattern match on it *in the | strict module*, the evaluation happens. | | As I said, I prefer this semantics mainly because it's easier to | explain: all variables (and underscores) bound in a strict module refer | to WHNF values. Do you have a similarly simple explanation for the | semantics you're suggesting? | | Roman _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs Email had 1 attachment: + signature.asc 1k (application/pgp-signature)

Agreed. (Sorry for the sporadic communication. This is a very busy time at work.) I thought about this a bit more recently. Here's one way where I think "force every binder" goes wrong: -- In a Strict module f (Just x) = Just x This is not the identity. This might matter in practice if a user defines a data type, in a lazy module, that uses a lazy field to cache an expensive calculation: -- In a lazy module: data CachedHash = CH a Int mkCH x = CH x (expensiveHash x) The intention here is that we only evaluate the expensive hash function when needed. However, if we evaluate every binder this is no longer transparent to users of the module, because doing this forces both fields: f (CH x h) = CH x h This also seems a bit "one sided" in that we do not evaluate expr in `Just <expr>` under Strict, to not break modularity, but we would do it if you pattern-matched on the contents of Just. I'm also worried that we might make a choice here that we can't undo. I at least understand the meaning of Strict in relation to strict languages. "Evaluate every binder" is a new thing. Perhaps it deserves its own pragma, StrictBinders. On Mon, Dec 14, 2015 at 6:50 PM, Adam Sandberg Eriksson < adam@sandbergericsson.se> wrote:
Hello,
Given the upcoming 8.0 feature freeze I think the correct approach for 8.0 is to document the current implementation (I'll try to do that this week).
It would probably be good if interested parties would document their input in a ticket.
Cheers, --Adam
Thanks Simon, this is an interesting and compelling interpretation. But I'm wondering whether it is enough to specify the dynamic semantics unambiguously.
Two examples:
1.
let _ = undefined in ()
Intuitively, since we are talking about /strictness/, this should evaluate to bottom. However, it seems that your rule also admits () as an answer; it is equivalent to () under lazy evaluation *and* it does not create any thunks.
2.
f g = g undefined
When compiled lazily, this code doesn't construct any thunks:
f = \r srt:SRT:[02v :-> undefined] [g_suM] g_suM undefined;
So, under your rule, this is an admissible code for -XStrict, too. But we can hardly call it strict.
On 12/12/2015 12:38 AM, Simon Peyton Jones wrote:
| As I said, I prefer this semantics mainly because it's easier to | explain: all variables (and underscores) bound in a strict module refer | to WHNF values. Do you have a similarly simple explanation for the | semantics you're suggesting?
Here's one, which is roughly what the current implementation does (modulo bugs):
* Code compiled under -XStrict constructs no thunks.
So consider
module M1 where data T = C Int Int module M2 where f n = C (n+1) (n-1) module M3 where g x = let C y z = f x in ...
Look at M3. Usually we'd get a thunk for (f 4), but not with -XStrict. But even with -XStrict in M3, y,z might be bound to thunks.
If you compile M2 with -XStrict, function f won't build thunks for (n+1), (n-1) but will evaluate them instead.
If you compile M1 with StrictData, then C is made strict, so again M2 will build no thunks even if M2 was compiled without -XStrict.
I quite like this design. It's not clear to me that anything useful is gained by forcing y and z in M3 before evaluating the body "...".
So Roman's design makes sense, but so does the implemented design (modulo any bugs). The trouble is that the implemented design is not well described.
Simon
| -----Original Message----- | From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Roman | Cheplyaka | Sent: 11 December 2015 12:57 | To: Johan Tibell
| Cc: ghc-devs@haskell.org | Subject: Re: -XStrict: Why some binders are not made strict? | | On 12/11/2015 02:21 PM, Johan Tibell wrote: | > If we force strictness all the way down it's not really call-by-value | > either, because the caller doesn't know what to evaluate (I think). | | Not sure what you mean here. | | > In addition, making pattern matching strict in this way makes it hard to | > mix and match strict and lazy data types (e.g. Maybe), because using a | > lazy data type from another module will make it appear strict in your | > code (hurting modularity). | | I don't think this is a case about modularity. A lazy Maybe value | defined in a lazy module remains lazy; and you can pass it to lazy | functions without forcing it. Only when you pattern match on it *in On Sat, 12 Dec 2015, at 12:55 AM, Roman Cheplyaka wrote: the
| strict module*, the evaluation happens. | | As I said, I prefer this semantics mainly because it's easier to | explain: all variables (and underscores) bound in a strict module refer | to WHNF values. Do you have a similarly simple explanation for the | semantics you're suggesting? | | Roman
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs Email had 1 attachment: + signature.asc 1k (application/pgp-signature)

| Given the upcoming 8.0 feature freeze I think the correct approach for
| 8.0 is to document the current implementation (I'll try to do that this
| week).
Yes, that's right. Thanks!
Simon
|
| It would probably be good if interested parties would document their
| input in a ticket.
|
| Cheers,
| --Adam
|
|
| On Sat, 12 Dec 2015, at 12:55 AM, Roman Cheplyaka wrote:
| > Thanks Simon, this is an interesting and compelling interpretation. But
| > I'm wondering whether it is enough to specify the dynamic semantics
| > unambiguously.
| >
| > Two examples:
| >
| > 1.
| >
| > let _ = undefined in ()
| >
| > Intuitively, since we are talking about /strictness/, this should
| > evaluate to bottom. However, it seems that your rule also admits () as
| > an answer; it is equivalent to () under lazy evaluation *and* it does
| > not create any thunks.
| >
| > 2.
| >
| > f g = g undefined
| >
| > When compiled lazily, this code doesn't construct any thunks:
| >
| > f = \r srt:SRT:[02v :-> undefined] [g_suM] g_suM undefined;
| >
| > So, under your rule, this is an admissible code for -XStrict, too. But
| > we can hardly call it strict.
| >
| > On 12/12/2015 12:38 AM, Simon Peyton Jones wrote:
| > > | As I said, I prefer this semantics mainly because it's easier to
| > > | explain: all variables (and underscores) bound in a strict module refer
| > > | to WHNF values. Do you have a similarly simple explanation for the
| > > | semantics you're suggesting?
| > >
| > > Here's one, which is roughly what the current implementation does (modulo
| bugs):
| > >
| > > * Code compiled under -XStrict constructs no thunks.
| > >
| > > So consider
| > >
| > > module M1 where data T = C Int Int
| > > module M2 where f n = C (n+1) (n-1)
| > > module M3 where g x = let C y z = f x in ...
| > >
| > > Look at M3. Usually we'd get a thunk for (f 4), but not with -XStrict.
| But even with -XStrict in M3, y,z might be bound to thunks.
| > >
| > > If you compile M2 with -XStrict, function f won't build thunks for (n+1),
| (n-1) but will evaluate them instead.
| > >
| > > If you compile M1 with StrictData, then C is made strict, so again M2
| will build no thunks even if M2 was compiled without -XStrict.
| > >
| > > I quite like this design. It's not clear to me that anything useful is
| gained by forcing y and z in M3 before evaluating the body "...".
| > >
| > >
| > > So Roman's design makes sense, but so does the implemented design (modulo
| any bugs). The trouble is that the implemented design is not well described.
| > >
| > > Simon
| > >
| > > | -----Original Message-----
| > > | From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Roman
| > > | Cheplyaka
| > > | Sent: 11 December 2015 12:57
| > > | To: Johan Tibell

| Thanks Simon, this is an interesting and compelling interpretation.
| But I'm wondering whether it is enough to specify the dynamic
| semantics unambiguously.
You make good points. Also, consider
f (g x)
where f is define in some other (lazy) module. Is that done call-by value? That is, is (g x) evaluated before the call? According to the current spec, no. So a thunk is built!
But if we write
let y = g x in f y
then, lo, the (g x) is computed eagerly.
So the present semantics seems to be more
functions always evaluate their arguments to
WHNF (but not to NF)!
I do think this could do with a tighter spec. Johan, Adam, this is your baby.
Simon
| -----Original Message-----
| From: Roman Cheplyaka [mailto:roma@ro-che.info]
| Sent: 11 December 2015 23:56
| To: Simon Peyton Jones

In addition, making pattern matching strict in this way makes it hard to mix and match strict and lazy data types (e.g. Maybe), because using a lazy data type from another module will make it appear strict in your code (hurting modularity).
I didn’t understand that.
Simon
From: ghc-devs [mailto:ghc-devs-bounces@haskell.org] On Behalf Of Johan Tibell
Sent: 11 December 2015 12:22
To: Roman Cheplyaka
I'm snowed under but I promise I will try to reply soon! To think about in the mean time: what do existing strict languages with pattern matching do?
Well, strict languages do not have lazy data to force to begin with, do they? Personally, I find the simple intuition of "all patterns are strict by default" rather appealing. E.g. I wouldn't expect the expressions let (v1,v2) = a in f v2 and let (v1,v2) = a; v3 = v2 in f v3 to have different semantics. If we decide to adopt this semantics, we need to address the meaning of the pattern ~(v1, v2) under -XStrict. Intuitively, ~ should propagate to the subpatterns. An alternative is to disallow this pattern under -XStrict and require writing all ~s explicitly, which may get tedious: ~(~v1, ~v2) ~(~v1, ~(~v2, ~v3)) etc. We also need to ensure the consistency between this extension and the unlifted data types proposal [1], given their similarity. Interestingly, I don't see constructor patterns explained there either. [1]: https://ghc.haskell.org/trac/ghc/wiki/UnliftedDataTypes#Dynamicsemanticsofun... _______________________________________________ ghc-devs mailing list ghc-devs@haskell.orgmailto:ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devshttps://na01.safelinks.protection.outlook.com/?url=http%3a%2f%2fmail.haskell.org%2fcgi-bin%2fmailman%2flistinfo%2fghc-devs&data=01%7c01%7csimonpj%40064d.mgd.microsoft.com%7c2faaa875c5764c6701d408d30225b68d%7c72f988bf86f141af91ab2d7cd011db47%7c1&sdata=xaohOEQRieXHuSG%2btmcecSX%2fr7tQG1OMkt1YQe9kmT0%3d
participants (6)
-
Adam Sandberg Eriksson
-
Brandon Allbery
-
Johan Tibell
-
Roman Cheplyaka
-
Simon Peyton Jones
-
Ömer Sinan Ağacan