我写了一些有用的函数来进行逻辑运算。它看起来像(a and b or c) `belongs` x
。
感谢Num
,IsList
和OverloadedLists
,我可以将它用于整数和列表。
λ> (1 && 2 || 3) `belongs` [2]
False
λ> (1 && 2 || 3) `belongs` [1, 2]
True
λ> (1 && 2 || 3) `belongs` [3]
True
λ> :set -XOverloadedLists
λ> ([1, 2] && [2, 3] || [3, 4]) `contains` 1
False
λ> ([1, 2] && [2, 3] || [3, 4]) `contains` 2
True
我是这样做的:
newtype BoolLike a = BoolLike ((a -> Bool) -> Bool)
(&&) :: BoolLike a -> BoolLike a -> BoolLike a
BoolLike f && BoolLike g = BoolLike $ \k -> f k P.&& g k
infixr 3 &&
toBoolLike :: a -> BoolLike a
toBoolLike x = BoolLike $ \k -> k x
belongs :: Eq a => BoolLike a -> [a] -> Bool
belongs (BoolLike f) xs = f (\x -> x `elem` xs)
contains :: Eq a => BoolLike [a] -> a -> Bool
contains (BoolLike f) x = f (\xs -> x `elem` xs)
instance Num a => Num (BoolLike a) where
fromInteger = toBoolLike . fromInteger
我要做的是让它适合任何类型,而无需手动将a
转换为BoolLike a
。
我怎样才能实现它?
首先尝试:
class IsBool a where
type BoolItem a
toBool :: a -> BoolItem a
instance IsBool (BoolLike a) where
type BoolItem (BoolLike a) = BoolLike a
toBool = id
instance IsBool a where
type BoolItem a = BoolLike a
toBool = toBoolLike
失败:
Conflicting family instance declarations:
BoolItem (BoolLike a) -- Defined at BoolLike.hs:54:8
BoolItem a -- Defined at BoolLike.hs:58:8
答案 0 :(得分:3)
你可以试试这个
OVERLAPPABLE
通过将类型族移出类,您可以为所有类型定义它。然后剩下的就是限制其中一个实例。不要忘记你还需要制作一个for
答案 1 :(得分:3)
这个答案可能对你没有用,因为你很可能已经考虑过我建议的替代方案,并认为这对你的恶魔目的来说还不够。然而,偶然发现这个问题的读者可能会发现知道如何实现类似于你所寻找的东西是有用的,如果不是那么漂亮,没有类型级别的诡计。
在您的计划中,BoolLike a
...
newtype BoolLike a = BoolLike ((a -> Bool) -> Bool)
...由一个函数组成,当给定Bool
延续时,该函数产生a -> Bool
结果。在提供延续之前,您的使用示例都归结为将Bool
结果与(&&)
和(||)
相结合。可以使用函数的Applicative
实例(在本例中为(->) (a -> Bool)
)并使用(&)
/ flip ($)
将普通值提升为(a -> Bool) -> Bool
&#来实现34;暂停计算":
GHCi> ((||) <$> ((&&) <$> ($ 1) <*> ($ 2)) <*> ($ 3)) (`elem` [2])
False
当然,写作并不是很整洁。但是,通过定义:
,我们可以改进很多东西(<&&>) :: Applicative f => f Bool -> f Bool -> f Bool
x <&&> y = (&&) <$> x <*> y
(<||>) :: Applicative f => f Bool -> f Bool -> f Bool
x <||> y = (||) <$> x <*> y
(对于定义它们的小图书馆,请查看control-bool。)
有了这些,额外的线路噪音变得非常轻巧:
GHCi> (($ 1) <&&> ($ 2) <||> ($ 3)) (`elem` [2])
False
这也适用于contains
案例的开箱即用 - 所需要的只是改变提供的延续:
GHCi> (($ [1, 2]) <&&> ($ [2, 3]) <||> ($ [3, 4])) (elem 1)
False
作为最后一点,值得指出contains
案例可以用来自intersect
的{{1}}和union
直接表达:
Data.List