简体   繁体   English

类型类中的非法多态或限定类型

[英]Illegal polymorphic or qualified type in typeclass

The following file Poly.hs file 以下文件为Poly.hs文件

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE RankNTypes #-}
module Poly () where

type ListModifier s = forall a. s -> [a] -> [a]

instance Monoid (ListModifier s) where
  mempty = const id
  mappend f g st = f st . g st

Gets the typechecker to complain: 使类型检查器抱怨:

Poly.hs:8:10: Illegal polymorphic or qualified type: ListModifier s …
    In the instance declaration for ‘Monoid (ListModifier s)’
Compilation failed.

Initially I though it couldn't compose the rank 2 types but: 最初,我虽然不能构成等级2类型,但是:

λ> :t (undefined :: forall a . a -> String ) . (undefined :: forall b . String -> b)
(undefined :: forall a . a -> String ) . (undefined :: forall b . String -> b)
  :: String -> String

I feel the Poly module is in some way inherently inconsistent but I can't put my finger on the problem. 我觉得Poly模块在某种程度上是固有的不一致,但是我不能直指这个问题。

ListModifier is a type alias, not a “real” type. ListModifier是类型别名,而不是“真实”类型。 Type aliases are essentially macros at the type level, always expanded by the typechecker before actually typechecking. 类型别名本质上是类型级别的宏,通常在实际进行类型检查之前由类型检查器扩展。 That means your instance declaration is equivalent to the following: 这意味着您的实例声明等效于以下内容:

instance Monoid (forall a. s -> [a] -> [a]) where

Even if that were allowed, it would overlap with the existing Monoid (a -> b) instance, so it still wouldn't work. 即使是允许,将与现有的重叠, Monoid (a -> b)的实例,所以它仍然是行不通的。 The larger problem, however, is that you can't have an instance defined on a forall -quantified type because it wouldn't make sense from the perspective of instance resolution. 但是,更大的问题是,您无法在forall量化类型上定义实例,因为从实例解析的角度来看,这是没有意义的。

What you could do instead is define a fresh type instead of a type alias using newtype : 您可以做的是使用newtype定义一个新鲜的类型而不是类型别名:

newtype ListModifier s = ListModifier (forall a. s -> [a] -> [a])

Now you can define a Monoid instance, since typeclass resolution only needs to look for the ListModifier type, which is much simpler to match on: 现在,您可以定义一个Monoid实例,因为类型类解析仅需要查找ListModifier类型,这在以下情况上要简单得多:

instance Monoid (ListModifier s) where
  mempty = ListModifier (const id)
  mappend (ListModifier f) (ListModifier g) = ListModifier (\st -> f st . g st)

Alternatively, you could keep your type alias and define a newtype with a different name, like ReifiedListModifier , then define an instance on that, and you could only do the wrapping when you need to store a ListModifier in a container or use a typeclass instance. 或者,您可以保留类型别名并使用其他名称定义一个新类型,例如ReifiedListModifier ,然后在其上定义一个实例,并且仅在需要将ListModifier存储在容器中或使用ListModifier实例时才可以进行包装。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM