Haskell 如何为可重叠实例、单态容器和新类型包装器实现默认的关联类型族?
我有以下Haskell代码:Haskell 如何为可重叠实例、单态容器和新类型包装器实现默认的关联类型族?,haskell,type-families,Haskell,Type Families,我有以下Haskell代码: type family Element t class ToList t where toList :: t -> [Element t] 早些时候有人建议我将元素作为关联的类型族: 我试图实施这种方法。但这对我的案子不起作用。以下是全部代码: {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} import Prelude hiding (toList) import
type family Element t
class ToList t where
toList :: t -> [Element t]
早些时候有人建议我将元素
作为关联的类型族:
我试图实施这种方法。但这对我的案子不起作用。以下是全部代码:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
import Prelude hiding (toList)
import qualified Data.Foldable as Foldable (toList)
import Data.Text (Text, unpack)
class ToList t where
type Element t :: *
toList :: t -> [Element t]
-- | This instance makes 'ToList' compatible and overlappable by 'Foldable'.
instance {-# OVERLAPPABLE #-} Foldable f => ToList (f a) where
type Element (f a) = a
toList = Foldable.toList
instance ToList Text where
type Element Text = Char
toList = unpack
newtype WrappedList l = WrappedList l
instance ToList l => ToList (WrappedList l) where
type Element (WrappedList l) = Element l
toList (WrappedList l) = toList l
当我使用GHC-8.2.2
编译此代码时,我看到以下错误:
Element.hs:14:10: error:
Conflicting family instance declarations:
Element (f a) = a -- Defined at Element.hs:14:10
Element (WrappedList l) = Element l -- Defined at Element.hs:24:10
|
14 | type Element (f a) = a
| ^^^^^^^^^^^^^^^^^
如何修复此错误?我不知道如何使它与关联的类型族一起可编译…基本问题是不能使用类型类重叠来生成重叠的类型族。这根本没有意义——类型族从输入类型计算类型,结果类型可能不取决于编译器如何选择类型类实例(否则它就不是函数——因为函数的输出可能只取决于输入)。这个问题很常见,但是如何解决它完全取决于您的特定用例 最简单的解决方案是使用
DefaultSignatures
提供默认实现。请注意,关联的类型族也可以具有默认值:
type family ElementDefault (t :: *) :: * where
ElementDefault (f a) = a
class ToList t where
type Element t :: *
type Element t = ElementDefault t
toList :: t -> [Element t]
default toList :: (Foldable f, t ~ f a, Element t ~ a) => t -> [Element t]
toList = Foldable.toList
这允许您为所有可折叠的类型编写实例,而无需给出实现:
instance ToList [a]
instance ToList (Maybe a)
-- etc...
如果希望避免编写此类实例(甚至实例头),则需要将关联的类型移动到类实例头中。因为只有类可以重叠,而不是开放类型族,这样做允许“元素”类型也重叠
class ToList t e | t -> e where
toList :: t -> [e]
instance {-# OVERLAPPABLE #-} (a ~ a', Foldable f) => ToList (f a) a' where
toList = Foldable.toList
instance ToList Text Char where
toList = unpack
instance ToList l a => ToList (WrappedList l) a where
toList (WrappedList l) = toList l
提供多个默认定义的最简单方法是在类之外提供它们。如果您有15个类函数,那么这确实会很乏味。在这种情况下,我将使用记录实现该类:
data ToList' t e = ToList'
{ toList' :: t -> [e] {- 14 more fields... -} }
class ToList t where
type Element t
toList_impl :: ToList' t (Element t)
-- For consumers of ToList
toList :: ToList t => t -> [Element t]
toList = toList' toList_impl
instance ToList Text where
type Element Text = Char
toList_impl = ToList' unpack
toList_Foldable_default :: Foldable f => ToList' (f a) a
toList_Foldable_default = ToList' Foldable.toList
toList_Wrapped_list :: ToList l => ToList' l (Element l)
toList_Wrapped_list = ToList' toList
通过这种方法,您可以完全免除typeclass;它唯一剩下的用途是获得实例唯一性。您不能-打开类型族不允许重叠。您可以使用多参数typeclass。然而,我看不出WrappedList
实例的用途。@user240738我想这是一个简单的健全性检查,以确保稍微复杂的类型可以是实例,也可以是简单的实例。@user2407038另一个让我满意的解决方案是使用type元素的默认实现
type族,并使用覆盖此实现的能力@关于健全性检查的HTNW点是正确的:)但在我的例子中,我有类ToList t=>Container t
其中Container
有15个方法。这个WrappedList
包装器通过Container
实例为[a]
实现所有Container
方法。如果您想使用Container
方法,但不想为您的数据类型实现~15个方法,则可能会很有用。@user2407038对此问题的回答建议使用“默认关联内射类型族”,但我不知道如何使用它:谢谢!这是非常有用的。现在我明白了问题所在。