Search code examples
haskellhaskell-backpack

How to avoid the need of an auxiliary newtype when matching this Backpack signature?


I'm using the new Backpack module system along with Cabal 2. I have the follwing signature:

{-# LANGUAGE KindSignatures #-}
signature Streamy where

import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.IO.Class

data Stream :: * -> (* -> *) -> * -> *

instance Monad m => Functor (Stream o m)
instance Monad m => Applicative (Stream o m)
instance Monad m => Monad (Stream o m)
instance MonadIO m => MonadIO (Stream o m) 
instance MonadTrans (Stream o)

I'm trying to match it with the concrete Stream type from streaming, like this:

import Streaming (Of(..))
import qualified Streaming as Q

type Stream o m r = Q.Stream (Of o) m r

However, this gives me the error

Illegal parameterized type synonym in implementation of abstract data. (Try eta reducing your type synonym so that it is nullary)

It seems that the type synonym can't have parameters. I need at least one however, because the o is sitting inside the Of instead of being a direct parameter of Q.Stream.

I can solve the problem by defining an adapter newtype:

{-# language GeneralizedNewtypeDeriving #-}
newtype S o m r = S { unS :: Q.Stream (Of o) m r } 
        deriving (Functor,Applicative,Monad,MonadIO,MonadTrans)

type Stream = S

Is there another way that doesn't require defining the newtype?


Solution

  • It seems that there isn't a way to avoid the newtype. The Backpack thesis clearly states that type synonyms are required to be nullary (section 6.2.4 "Subtyping rules"):

    the implementing type synonyms are required to be nullary: for example, type M a = a is not a valid implementation of the abstract data M a, but type M = Maybe :: * -> * is. This restriction stems from an old design decision in Haskell to not support type level lambdas. This restriction greatly helps type inference, since given the type equality t a = s b, we can now conclude that t = s and a = b (this property is called generativity).

    Also related, in section B.2 "Challenges":

    Translucency: A feature not supported in Backpack’14 but supported in Backpack’17 is the ability to use a type synonym to implement an abstract data type. This introduces a form of “translucency”, where the abstract data type is opaque while the implementation is unknown, and transparent afterwards. For example, in Section 2.3, we demonstrated how we could instantiate a generic regular expression matcher to match on strings. Inside the implementation of the matcher, we knew nothing about the abstract type Str; after instantiating it, the accept function transparently accepts String arguments.