Skip to content

Replace explicit wrapping and unwrapping of newtypes by coercions #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 27, 2020

Conversation

kl0tl
Copy link
Member

@kl0tl kl0tl commented Sep 9, 2020

This adds Coercible superclasses to Newtype in order to implement most combinators with coerce.

Newtype constructors have to be exported for them to be unwrapped when solving Coercible constraints so this means that Newtype instances for types with hidden constructors won’t be usable anymore outside of their module of definition. This is mitigated by purescript/purescript#3907 which emits a warning for such types (this warning would be turned into an error by the pull request updating the compiler support for Newtype instances deriving) and purescript/purescript#3927 which suggests to export hidden newtype constructors when a Coercible constraint fails to solve because of that.

This also means that Newtype instances can only de declared for representationally equal types.

I reckon that releasing this along with the next compiler release is perhaps too reckless but at least it should provide more context and motivation for the mentioned warnings.

class Newtype t a | t -> a where
wrap :: a -> t
unwrap :: t -> a
class (Coercible t a, Coercible a t) <= Newtype t a | t -> a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't Coercible symmetric? What's the thinking behind giving two Coercible superclasses instead of just one and letting the compiler solve the other as needed—or does that not work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Computing subgoals is indeed symmetric, the subgoals of Coercible a b are the same than the subgoals of Coercible b a, but the compiler doesn’t deduce Coercible b a from Coercible a b.

This means that we need Newtype t a to imply both Coercible t a and Coercible a t for unwrap and wrap to compile:

module Example where

import Safe.Coerce (coerce)
import Prim.Coerce (class Coercible)

class Coercible t a <= Newtype t a | t -> a

wrap :: forall t a. Newtype t a => a -> t
wrap = coerce
Error found:
in module Example
at Example.purs:9:8 - 9:14 (line 9, column 8 - line 9, column 14)

  No type class instance was found for
                            
    Prim.Coerce.Coercible a0
                          t1
                            

while checking that type forall (a :: Type) (b :: Type). Coercible a b => a -> b
  is at least as general as type a0 -> t1
while checking that expression coerce
  has type a0 -> t1
in value declaration wrap

where t1 is a rigid type variable
        bound at (line 9, column 8 - line 9, column 14)
      a0 is a rigid type variable
        bound at (line 9, column 8 - line 9, column 14)

See https://github.com/purescript/documentation/blob/master/errors/NoInstanceFound.md for more information,
or to contribute content related to this error.

This is something we might be able to do though, I’ll look into it.

@hdgarrood
Copy link

Is there a particular reason not to do this now? I think if we're going to do it at all, then the sooner the better.

@kl0tl
Copy link
Member Author

kl0tl commented Oct 10, 2020

I’d like to release this with the v0.14.0 too but this depends on and implies a few things:

@hdgarrood
Copy link

Right, ok, I didn't realise we'd have to do all that stuff too. In that case I think we should leave this out for now. If we manage to do all those things before we're ready to release v0.14.0 then that's fine, but otherwise I don't think this is an important enough change to hold v0.14.0 up.

@JordanMartinez
Copy link
Contributor

Of the PRs @kl0tl mentioned above that need to be merged before this PR can be merged, only purescript/purescript#3927 hasn't yet been merged (but looks like it will be soon).

@hdgarrood
Copy link

I think purescript/purescript#3927 isn't even a hard requirement for merging this, it's just a change to make certain compiler errors more helpful, so I'd be in favour of going ahead and merging this now.

@JordanMartinez
Copy link
Contributor

Do we need to rebase this on top of current master to make GH Actions run rather than Travis?

@hdgarrood
Copy link

I think so, yes.

@thomashoneyman thomashoneyman force-pushed the zero-cost-newtype-combinators branch from fb2d6c0 to cb8d91e Compare December 26, 2020 23:48
@JordanMartinez
Copy link
Contributor

CI is failing with this error:

[1/1 NoInstanceFound] src/Data/Newtype.purs:42:1

  42  instance newtypeMultiplicative :: Newtype (Multiplicative a) a
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  
  No type class instance was found for
  
    Prim.Coerce.Coercible (Multiplicative a0)
                          a0
  
  while checking that expression #dict Coercible
    has type Coercible$Dict @Type (Multiplicative a0) a0
  in value declaration newtypeMultiplicative
  
  where a0 is a rigid type variable
          bound at (line 0, column 0 - line 0, column 0)

           Src   Lib   All
Warnings   0     0     0  
Errors     1     0     1  

@hdgarrood
Copy link

That will probably be because the Multiplicative constructor is not in scope, we’ll need to import it in that module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants