我想在代码中表示上限的概念,所以我创建了一个有区别的联合:
data UpperBound a = UpperBound a | DoNotCare deriving (Eq, Read, Show)
然后我手动派生了几个有用的类型类实例(出于学习目的):
instance Functor UpperBound where
fmap _ DoNotCare = DoNotCare
fmap f (UpperBound x) = UpperBound $ f x
instance Applicative UpperBound where
pure = UpperBound
DoNotCare <*> _ = DoNotCare
_ <*> DoNotCare = DoNotCare
(UpperBound f) <*> (UpperBound x) = UpperBound $ f x
instance Foldable UpperBound where
foldr _ s DoNotCare = s
foldr f s (UpperBound x) = f x s
instance Traversable UpperBound where
traverse _ DoNotCare = pure DoNotCare
traverse f (UpperBound x) = fmap UpperBound $ f x
instance Alternative UpperBound where
empty = DoNotCare
DoNotCare <|> x = x
x <|> DoNotCare = x
x <|> _ = x
instance Monad UpperBound where
return = pure
DoNotCare >>= _ = DoNotCare
(UpperBound x) >>= f = f x
instance MonadPlus UpperBound where
mzero = empty
mplus = (<|>)
和一个实用程序功能:
isWithinBound :: Ord a => a -> UpperBound a -> Bool
isWithinBound _ DoNotCare = True
isWithinBound x (UpperBound b) = x <= b
typeclass实例看起来与Maybe
(Just x
-> UpperBound x
,Nothing
-> DoNotCare
)的实例几乎一样,显然这里有不必要的重复。
我如何以某种方式“包装” Maybe
并将类型类实例实现重定向到该类型,并仍然公开isWithinBound
函数?
答案 0 :(得分:3)
最简单的方法是使用newtype
和GeneralizedNewtypeDeriving
扩展名,如下所示:
{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveTraversable #-}
module UpperBound (UpperBound, isWithinBound) where
import Control.Monad
import Control.Applicative
newtype UpperBound a = UpperBound { unUpperBound :: Maybe a }
deriving (Functor, Applicative, Foldable, Traversable, Alternative, Monad, MonadPlus)
isWithinBound :: Ord a => a -> UpperBound a -> Bool
isWithinBound x = maybe True ((<=) x) . unUpperBound
因此,您需要的所有实例都会自动重定向到Maybe
的实例(除了Traversable
,它是使用DeriveTraversable
扩展名自动得出的)。