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?
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 dataM a
, buttype 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 equalityt a = s b
, we can now conclude thatt = s
anda = 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 acceptsString
arguments.