Haskell:在声明一个类时,如何使用不在构造函数中的类型变量?

时间:2015-06-22 05:23:47

标签: haskell

我想定义一个函数<-?来检查元素是否在列表/集合/映射中。

module Test where
import qualified Data.Map as Map
import qualified Data.Set as Set

class Memberable a where
    (<-?) :: b -> a -> Bool
instance Memberable [x] where
    (<-?) = elem
instance Memberable (Map.Map k v) where
    (<-?) = Map.member
instance Memberable (Set.Set x) where
    (<-?) = Set.member

类声明中的类型变量b应该是我想要检查的元素的类型。但是,这对Haskell不起作用。

Test.hs:8:13:
    Couldn't match type 'b' with 'x'
      'b' is a rigid type variable bound by
          the type signature for (<-?) :: b -> [x] -> Bool at Test.hs:8:5
      'x' is a rigid type variable bound by
          the instance declaration at Test.hs:7:10
    Expected type: b -> [x] -> Bool
      Actual type: b -> [b] -> Bool
    Relevant bindings include
      (<-?) :: b -> [x] -> Bool (bound at Test.hs:8:5)
    In the expression: elem
    In an equation for '<-?': (<-?) = elem

如何在类声明中使用b,但仍然使类型重合?

1 个答案:

答案 0 :(得分:9)

使用TypeFamilies

问题是你必须以某种方式将b与你的集合(其中的元素)联系起来 - 有几种方法可以做到这一点,但我认为一个相当不错的方法是使用TypeFamilies:< / p>

{-# LANGUAGE TypeFamilies #-}

module Test where

import qualified Data.Map as Map
import qualified Data.Set as Set

class Memberable a where
  type ElemT a :: *
  (<-?) :: (ElemT a) -> a -> Bool

instance Eq x => Memberable [x] where
    type ElemT [x] = x
    (<-?) = elem

instance Ord k => Memberable (Map.Map k v) where
    type ElemT (Map.Map k v) = k
    (<-?) = Map.member

instance Ord x => Memberable (Set.Set x) where
    type ElemT (Set.Set x) = x
    (<-?) = Set.member

正如您所看到的,我在类中添加了一个type 成员,其中包含所使用元素的类型(因此您可以将它们用于(<-?)下一行)。

我还为您的实例添加了所需的约束 - 这些约束确实来自elemMap.memberSet.member等已使用的函数。

使用MultiParamTypeClasses

这是@dfeuer暗示的(或者我认为):

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

module Test where

import qualified Data.Map as Map
import qualified Data.Set as Set

class Memberable e a where
  (<-?) :: e -> a -> Bool

instance Eq x => Memberable x [x] where
    (<-?) = elem

instance Ord k => Memberable k (Map.Map k v) where
    (<-?) = Map.member

instance Ord x => Memberable x (Set.Set x) where
    (<-?) = Set.member

备注:

我认为使用这种方法你也可能想要添加这个FunctionalDependency,因为你明确地在集合a和它的元素e之间存在这样的依赖关系; )

{-# LANGUAGE FunctionalDependencies #-}

class Memberable e a | a -> e where
  (<-?) :: e -> a -> Bool