haskell中的自动转换类型

时间:2016-10-29 04:35:32

标签: haskell type-conversion type-families

我写了一些有用的函数来进行逻辑运算。它看起来像(a and b or c) `belongs` x

感谢NumIsListOverloadedLists,我可以将它用于整数和列表。

λ> (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

2 个答案:

答案 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