如何限制类参数的角色

Cli*_*ton 5 haskell ghc

下面的代码:

\n
{-# LANGUAGE DerivingStrategies #-}\n{-# LANGUAGE GeneralizedNewtypeDeriving #-}\n\nclass C (f :: Type -> Type) where\n  g :: String -> f a\n\nclass D a where\n  h :: C f => a -> f a\n\ninstance C Maybe where\n  g _ = Nothing\n\ninstance C (Either String) where\n  g = Left \n\ninstance D Int where\n  h = g . show\n\nnewtype NewInt = NewInt Int deriving newtype D \n
Run Code Online (Sandbox Code Playgroud)\n

无法编译并出现以下错误:

\n
  Couldn't match representation of type: f Int\n                           with that of: f NewInt\n    arising from the coercion of the method \xe2\x80\x98h\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n

我想这是有道理的,如果f有一些奇怪的家庭用品的话。但我的fs 没有,只​​有MaybeEither,所以我相信Int用它的 newtype替换NewInt可能应该有效。

\n

我怎样才能让 GHC 相信这一点(假设我没有错)。我认为这是RoleAnnotations必需的,但我尝试过的都没有成功。

\n

chi*_*chi 2

不幸的是,我们目前无法指定量化类型变量的角色。我能够想到的最接近的是以下内容,利用安全强制。

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE QuantifiedConstraints #-}

import Data.Kind
import Data.Coerce

class (forall a b . Coercible a b => Coercible (f a) (f b))
      => C (f :: Type -> Type) where
  g :: String -> f a
Run Code Online (Sandbox Code Playgroud)

请注意超类要求,它或多或少需要代表性角色,或者更准确地说,它类似于强制。

其余的与您的代码相同:

class D a where
  h :: C f => a -> f a

instance C Maybe where
  g _ = Nothing

instance C (Either String) where
  g = Left 

instance D Int where
  h = g . show
Run Code Online (Sandbox Code Playgroud)

我们确实必须将 更改newtype .. deriving为自定义实例,但至少我们可以简单地调用coerce并让它发挥其魔力。

newtype NewInt = NewInt Int
  -- No "deriving newtype D" here

-- explicit instance using "coerce"
instance D NewInt where 
  h :: forall f. C f => NewInt -> f NewInt
  h = coerce (h @Int @f)
Run Code Online (Sandbox Code Playgroud)