[GHC] #14806: Officially sanction certain unsafeCoerce applications with unboxed unary tuples

#14806: Officially sanction certain unsafeCoerce applications with unboxed unary tuples -------------------------------------+------------------------------------- Reporter: dfeuer | Owner: (none) Type: feature | Status: new request | Priority: normal | Milestone: 8.6.1 Component: Documentation | Version: 8.2.2 Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- It seems that it should be okay to `unsafeCoerce` between types that wrap certain things in unboxed unary tuples and ones that do not. For example, {{{#!hs unsafeCoerce :: (A -> B) -> A -> (# B #) unsafeCoerce :: ((# A #) -> B) -> A -> B }}} Generally, I believe `unsafeCoerce :: E1 -> E2` should be okay when the only differences are in what is wrapped in an unboxed unary tuple and both of the following hold: 1. Each new unary tuple wrapper in `E2` is in a positive position. 2. Each new unary tuple wrapper in `E1` is in a negative position. Semantically, {{{#!hs unsafeCoerce :: (A -> B) -> A -> (# B #) = \f a -> let !fa = f a in (# fa #) unsafeCoerce :: ((# A #) -> B) -> A -> B = \f !a -> f a }}} Am I correct in this interpretation? If so, is this something the developers would be willing to commit to and document? The first version in particular (a new unary tuple wrapper in positive position in the result) would be very useful for reducing both source code and generated code size in libraries supporting both strict and lazy operations. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14806 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14806: Officially sanction certain unsafeCoerce applications with unboxed unary tuples -------------------------------------+------------------------------------- Reporter: dfeuer | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.6.1 Component: Documentation | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Description changed by dfeuer: Old description:
It seems that it should be okay to `unsafeCoerce` between types that wrap certain things in unboxed unary tuples and ones that do not. For example,
{{{#!hs unsafeCoerce :: (A -> B) -> A -> (# B #) unsafeCoerce :: ((# A #) -> B) -> A -> B }}}
Generally, I believe `unsafeCoerce :: E1 -> E2` should be okay when the only differences are in what is wrapped in an unboxed unary tuple and both of the following hold:
1. Each new unary tuple wrapper in `E2` is in a positive position. 2. Each new unary tuple wrapper in `E1` is in a negative position.
Semantically,
{{{#!hs unsafeCoerce :: (A -> B) -> A -> (# B #) = \f a -> let !fa = f a in (# fa #)
unsafeCoerce :: ((# A #) -> B) -> A -> B = \f !a -> f a }}}
Am I correct in this interpretation? If so, is this something the developers would be willing to commit to and document? The first version in particular (a new unary tuple wrapper in positive position in the result) would be very useful for reducing both source code and generated code size in libraries supporting both strict and lazy operations.
New description: It seems that it should be okay to `unsafeCoerce` between types that wrap certain things in unboxed unary tuples and ones that do not. For example, {{{#!hs unsafeCoerce :: (A -> B) -> A -> (# B #) unsafeCoerce :: ((# A #) -> B) -> A -> B }}} Generally, I believe `unsafeCoerce :: E1 -> E2` should be okay when the only differences are in what is wrapped in an unboxed unary tuple and both of the following hold: 1. Each new unary tuple wrapper in `E2` is in a positive position. 2. Each new unary tuple wrapper in `E1` is in a negative position. Semantically, {{{#!hs unsafeCoerce :: (A -> B) -> A -> (# B #) = \f a -> let !fa = f a in (# fa #) unsafeCoerce :: ((# A #) -> B) -> A -> B = \f !a -> f (# a #) }}} Am I correct in this interpretation? If so, is this something the developers would be willing to commit to and document? The first version in particular (a new unary tuple wrapper in positive position in the result) would be very useful for reducing both source code and generated code size in libraries supporting both strict and lazy operations. -- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14806#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14806: Officially sanction certain unsafeCoerce applications with unboxed unary tuples -------------------------------------+------------------------------------- Reporter: dfeuer | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.6.1 Component: Documentation | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by andrewthad): I'm also interested in seeing this officially supported. For reference, David describes an application of this in further detail here: https://github.com/tibbe/unordered-containers/pull/188. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14806#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14806: Officially sanction certain unsafeCoerce applications with unboxed unary tuples -------------------------------------+------------------------------------- Reporter: dfeuer | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.6.1 Component: Documentation | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj):
`unsafeCoerce :: (A -> B) -> A -> (# B #)`
The first version in particular would be very useful for reducing both
I think you are right that this is ok. But the reverse is not {{{ unsafeCoerce :: (A -> {# B #)) -> A -> B }}} because the argument function may return an unevaluated `B`, while the returned function is supposed to guaranteed to return an evaluated `B`. Tricky. source code and generated code size in libraries supporting both strict and lazy operations. Would you care to explain your use-case? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14806#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14806: Officially sanction certain unsafeCoerce applications with unboxed unary tuples -------------------------------------+------------------------------------- Reporter: dfeuer | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: 8.6.1 Component: Documentation | Version: 8.2.2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by dfeuer): Consider {{{#!hs adjust, adjust' :: (a -> a) -> Seq a -> Seq a }}} The lazy version can leak memory, but it's been around for years so we need to support it. The study version is efficient. Both versions can be defined using a more general operation: {{{#!hs adjust# :: (a -> (# a #)) -> Int -> Seq a -> Seq a adjust f = adjust# (\x -> (# f x #)) adjust' f = adjust# (\x -> let !x' = f x in (# x' #)) }}} `adjust#` allows the given function to perform as much work as it wants, then suspend as much further work as it wants. The definitions of `adjust` and `adjust'` above add a closure allocation and a bit of indirection over the equivalent direct definitions, unless we inline and duplicate code. For `adjust`, which already leaks memory, that's tolerable. For `adjust'`, it's not. What can we do? Define {{{#!hs adjust' f = adjust# (unsafeCoerce f) }}} That should just work. Since the result of applying `f` is interested by `adjust#` as an unboxed unary tuple, it gets forced. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14806#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC