#
`Coercible`

solver

More issues with This page is to discuss further issues with the `Coercible`

solver, and how to fix them. This is all spurred on by comment:9:ticket:10079, and affected by the implementation of the plan in comment:14:ticket:7788. The work-in-progress can be viewed at Phab:D653, or at my repo, branch `wip/rae`

.

See examples at bottom of page.

## The Problem

Suppose we have `newtype N a = MkN rhs`

and `[W] N t1 ~R N t2`

, where `N`

is a newtype whose constructor is in scope. We have **two** ways of proceeding: (1) either decompose to `[W] t1 ~r t2`

, where the `r`

is determined by the role on `N`

's parameter; **or** (2) unwrap `N`

to expose its representation, simplifying to `[W] rhs[a |-> t1] ~R rhs[a |-> t2]`

.

Approach (1) is always good **except in the presence of a role annotation**. By the correctness of role inference, `t1 ~r t2 <=> rhs[a |-> t1] ~R rhs[a |-> t2]`

, assuming the role `r`

is simply inferred from the structure of `rhs`

. If `N`

sports a role annotation, that assumption is bogus, and the arrow only points left. Thus, it's possible that decomposing gets us stuck, whereas unwrapping can yield an answer.

Approach (2) is always good **unless the newtype is recursive**. For a recursive newtype, approach (2) can trap us in an infinite loop.

Currently, we don't track whether or not a type has a role annotation, but we could. (We'd want to be careful not to treat a documentation-only role annotation as different from no role annotation, though.) Thus, if a type **either** has no role annotation **or** is non-recursive, we have a nice, complete algorithm. But, what on earth to do with recursive, role-annotated types?

### Why this is important

I (Richard E.) don't really know why recursive newtypes are all that important. But, role-annotated types are. It's easily conceivable that a library author wants a newtype whose constructor is held abstract. That author puts a role annotation on, preventing users from violating some key principle. But, the author also wants to coerce among different types freely in her own code. Thus, the solver needs, sometimes, to unwrap and not decompose. Indeed, this ability to coerce within a library but prevent coercions outside the library is a requested feature of the whole `Coercible`

mechanism, and is advertised.

## Solutions

Simon and I agree that recursive newtypes aren't all that important, except in one case: when we have `newtype N a = ty`

, we **must** be able to prove `N x ~R ty[a |-> x]`

, even in the recursive case. Happily, this is captured by an eager reflexivity check, done after unwrapping. So, that's the plan going forward.

One very dissatisfying solution is to track role annotations and recursivity and then decide between decomposition and unwrapping based on these flags. For a role-annotated, recursive newtype, we just do our best, for some definition of best.

### How 7.10 works

There is a stop-gap solution in 7.10: see Note [Eager reflexivity check] in TcCanonical. Basically, the implementation prefers unwrapping, but checks for reflexivity before looping. This catches most, but not all cases. And it's a bit unprincipled. It will miss the following

```
newtype B a = MkB [B a]
foo :: B Int -> B Bool
foo = coerce
```

## Examples

This is a collection of examples to think about when solving for `Coercible`

. Some, but not all, of these are in the regression suite. They probably all should be.

`typecheck/should_compile/T9117`

:

```
newtype Phant a = MkPhant Char
type role Phant representational
ex1 :: Phant Bool
ex1 = coerce (MkPhant 'x' :: Phant Int)
```

Make sure that we unwrap when the constructor is in scope. Not recursive.

`typecheck/should_compile/T9117_2`

:

```
newtype Foo a = Foo (Foo a)
newtype Age = MkAge Int
ex1 :: (Foo Age) -> (Foo Int)
ex1 = coerce
```

Make sure that we **don't** unwrap when doing so would lead to an infinite loop. This example is rather perverse, so it might be reasonable to fail here.

`typecheck/should_compile/T9117_3`

:

```
eta :: Coercible f g => Coercion (f a) (g a)
eta = Coercion
```

Eta-expansion.

`typecheck/should_compile/T9117_4`

:

```
newtype Bar a = Bar (Either a (Bar a))
newtype Age = MkAge Int
x :: Bar Age
x = coerce (Bar (Left (5 :: Int)))
```

Like `T9117_2`

, but not quite as perverse. This recursive newtype is at least inhabited.

`typecheck/should_fail/TcCoercibleFail`

:

These should all **fail**:

```
import Data.Ord (Down)
newtype Age = Age Int deriving Show
one :: Int
one = 1
type role Map nominal _
data Map a b = Map a b deriving Show
foo1 = coerce $ one :: () -- utterly bogus
foo2 :: forall m. Monad m => m Age
foo2 = coerce $ (return one :: m Int) -- don't know the role of m
foo3 = coerce $ Map one () :: Map Age () -- make sure we respect the role annotation
foo4 = coerce $ one :: Down Int -- make sure we respect not-imported constructor
newtype Void = Void Void
foo5 = coerce :: Void -> () -- utterly bogus, but shouldn't loop
newtype VoidBad a = VoidBad (VoidBad (a,a))
foo5' = coerce :: (VoidBad ()) -> () -- ditto
-- This shoul fail with a context stack overflow
newtype Fix f = Fix (f (Fix f))
foo6 = coerce :: Fix (Either Int) -> Fix (Either Age) -- stack overflow
foo7 = coerce :: Fix (Either Int) -> () -- stack overflow / bogus
```

`typecheck/should_fail/TcCoercibleFail3`

:

```
data T f = T (f Int)
newtype NT1 a = NT1 (a -> Int)
newtype NT2 a = NT2 (a -> Int)
foo :: T NT1 -> T NT2
foo = coerce -- should fail with stack overflow
```

This is a proper stack overflow. `NT1 Int`

and `NT2 Int`

are representationally equal in the limit. GHC is correct to keep unwrapping looking for this limit!

```
foo :: f a -> f a
foo = coerce
```

Even though `AppTy`

s can't be decomposed, we still must make sure that `Coercible`

is reflexive!

```
newtype X = MkX (Int -> X)
foo :: X -> X
foo = coerce
```

This could easily fall into an infinite loop, but we want this to succeed. Contrast with the `NT1`

/`NT2`

case a few examples up.

```
newtype X a = MkX Char
type role X nominal
foo :: X Int -> X Bool
foo = coerce
```

This can succeed only by unwrapping, not by decomposition.

```
newtype Age = MkAge Int
newtype Y a = MkY a
type role Y nominal
foo :: Y Age -> T Int
foo = coerce
```

Again, can succeed only by unwrapping, but has nothing to do with phantoms.

```
newtype Z a = MkZ ()
type role Z representational
foo :: Z Int -> Z Bool
foo = coerce
```

Again only by unwrapping, but no nominal arguments.

```
newtype N a = MkN [N a]
newtype Age = MkAge Int
foo :: N Age -> N Int
foo = coerce
```

Can succeed only by decomposition, because otherwise we loop.

```
newtype App f a = MkApp (f a)
foo :: f a -> App f a
foo = coerce
```

Need to flatten/zonk before failing when we see an `AppTy`

.

```
newtype Q a = MkQ [Q a]
foo :: Q Bool -> Q Int
foo = coerce
```

Solvable by decomposition only. (`a`

is phantom!)

```
foo :: Coercible a b => b -> a
foo = coerce
```

Symmetry.

```
foo :: (Coercible a b, Coercible b c) => a -> c
foo = coerce
```

Transitivity.

```
foo :: (Coercible (a b) (c d), Coercible (c d) (e f)) => a b -> e f
foo = coerce
```

This should work, but this would require a lot more engineering. The problem is that `AppTy`

s can't be decomposed, so it's hard to make the constraints canonical and then usable in substitution.

```
newtype N1 a = MkN1 a
newtype N2 a = MkN2 (N1 a)
foo :: N1 a -> N2 a
foo = coerce
```

Here, we **won't** decompose, because the heads differ. Even though this is similar to a case where we do decompose. Simon says that this means we should prefer unwrapping. I agree.