如何为OVERLAPPABLE实例,单形容器和newtype包装器实现默认关联类型系列?

时间:2017-12-14 14:54:33

标签: haskell type-families

我有以下Haskell代码:

FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("MainMenu.fxml"));
// or just FXMLLoader loader = new FXMLLoader(getClass().getResource("MainMenu.fxml"));

Parent root = loader.load();

我之前建议让type family Element t class ToList t where toList :: t -> [Element t] 成为相关类型系列:Foldable IntSet

我试图实现这种方法。但它对我的情况不起作用。这是完整的代码:

Element

当我使用{-# 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

我如何解决此错误?我不知道如何使这个与相关类型系列相容...

1 个答案:

答案 0 :(得分:5)

基本问题是您无法使用类型重叠来制作重叠的类型系列。它根本没有意义 - 类型族从输入类型计算类型,结果类型可能不依赖于编译器如何选择类型类实例(否则它不会是一个函数 - 因为函数的输出可能只取决于输入)。这个问题很常见,但是你如何解决它完全取决于你的具体用例。

最简单的解决方案是使用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

这允许您为所有Foldable类型编写实例,而不提供实现:

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

通过这种方法,您可以完全省去类型类;它唯一剩下的用途是获得实例唯一性。