假设我在Haskell中定义了一个类型类,
class (Bool a) where
false :: a
true :: a
为了为任何not
定义通用Bool
操作,需要对其潜在值进行模式匹配:
not :: Bool a => a → a
not true = false
not false = true
但是,这并没有编译。我怎样才能让它发挥作用?
答案 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
cfalse
和ctrue
是a
类型的成员,但此类型可以包含不是cfalse
和ctrue
的元素。例如
instance CBool Int where
cfalse = 0
ctrue = 1
cnot 2
应该是什么? Nothing
是个不错的选择。然后是cnot 0 = Just 1
和cnot 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
此处CIBool
由Bool
编制索引,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 == one
和cinot 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 #-}
。