如何根据类型类值进行模式匹配?

时间:2015-03-11 22:39:14

标签: haskell typeclass

假设我在Haskell中定义了一个类型类,

class (Bool a) where
    false :: a
    true  :: a

为了为任何not定义通用Bool操作,需要对其潜在值进行模式匹配:

not :: Bool a => a → a
not true  = false
not false = true
但是,这并没有编译。我怎样才能让它发挥作用?

2 个答案:

答案 0 :(得分:5)

你不能像这样匹配类型类值。类型抽象超过实际的底层类型,而模式匹配会暴露它。

但是,您仍然可以使用ViewPatterns扩展名获得相同的行为和一些很好的语法。我们的想法是包含一个函数,它接受您的抽象值,并为您提供表示结构视图的具体类型的值:

class (Bool a) where
  true   :: a
  false  :: a
  asBool :: a -> Bool

现在,您可以在模式匹配中使用asBool函数作为视图模式:

not :: Bool a => a -> a
not (asBool -> True)  = false
not (asBool -> False) = true

但请注意,这可能导致asBool被计算两次,如果它隐藏了昂贵的计算,则可能会出现问题。

答案 1 :(得分:1)

class CBool a where
    cfalse :: a
    ctrue  :: a

cfalsectruea类型的成员,但此类型可以包含不是cfalsectrue的元素。例如

instance CBool Int where
    cfalse = 0
    ctrue  = 1

cnot 2应该是什么? Nothing是个不错的选择。然后是cnot 0 = Just 1cnot 1 = Just 0。所以

cnot :: (CBool a, Eq a) => a -> Maybe a
cnot x | x == cfalse = Just ctrue
       | x == ctrue  = Just cfalse
       | otherwise   = Nothing

但还有另一种方式:

class CIBool a where
    citrue  :: a 'True
    cifalse :: a 'False

type family Not (b :: Bool) :: Bool where
    Not False = True
    Not True  = False

data Booley :: Bool -> * where
  Falsey :: Booley False
  Truey  :: Booley True

cinot :: CIBool a => Booley b -> a b -> a (Not b)
cinot Falsey _ = citrue
cinot Truey  _ = cifalse

此处CIBoolBool编制索引,Booley是单身,cinot是依赖类型的。可以从CIBool派生CBool

newtype a :@ b = Tag { detag :: a }
    deriving Eq

instance CBool a => CIBool ((:@) a) where
    cifalse = Tag cfalse
    citrue  = Tag ctrue

(:@)可以定义为类型系列,因为它只是Const,但我更愿意尽可能避免使用类型系列。如果

zero :: Int :@ False
zero = cifalse

one  :: Int :@ True
one  = citrue

然后cinot Falsey zero == onecinot Truey one == zero

但请注意,虽然这是类型错误:

zero' :: Int :@ True
zero' = cifalse

我们可以像这样定义zero'

zero' :: Int :@ True
zero' = Tag 0

然后是cinot Truey zero' == zero,这是毫无意义的。因此,Tag构造函数只应在CIBool实例定义中提及。

使用的扩展程序:{-# LANGUAGE GADTs, KindSignatures, DataKinds, PolyKinds, TypeFamilies, TypeOperators #-}