2016-10-29 14 views
4

Я написал несколько полезных функций для выполнения логической операции. Это выглядит как (a and b or c) `belongs` x.Тип автоматического преобразования в haskell

Благодаря 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

Существует [Boolean] (https://hackage.haskell.org/package/Boolean) пакет. –

ответ

3

Вы могли бы попробовать это

type family BoolItem a where 
    BoolItem (BoolLike a) = BoolLike a 
    BoolItem a = BoolLike a 

class IsBool a where 
    toBool :: a -> BoolItem a 

instance IsBool (BoolLike a) where 
    toBool = id 

instance (BoolItem a ~ BoolLike a) => IsBool a where 
    toBool = toBoolLike 

Перемещая семейного типа из класса вы можете определить его для всех типов , Тогда все, что остается, ограничивает один из экземпляров. Не забывайте, что вам тоже нужно будет сделать это OVERLAPPABLE

+0

Я определяю '&&' as '(toBool -> BoolLike f) && (toBool -> BoolLike g) = BoolLike $ \ k -> f k P. && g k' и' || 'похоже. Он выглядит хорошо, пока 'let relation = 1 && 2 || 3', ghci не может вывести (BoolItem a10 ~ BoolLike a3). Полный исходный код: https://github.com/qzchenwl/BoolLike/blob/master/src/BoolLike.hs – wenlong

+0

Это нормально для 'Char'. Я обновил ['Main.hs'] (https://github.com/qzchenwl/BoolLike/blob/master/src/Main.hs) – wenlong

+1

@wenlong, это потому, что' 1', '2', и т. д. имеют немономорфный тип, т. е. 'Num a => a'. Если тип недвусмыслен, например '(1 :: Int) && (2 :: Int) || (3 :: Int) ', он работает. – dkasak

3

Этот ответ, вероятно, вам не пригодится, так как, по всей вероятности, вы уже рассмотрели альтернативу, которую я собираюсь предложить, и посчитал, что этого недостаточно для ваши дьявольские цели. Тем не менее, читатели, которые спотыкаются на этот вопрос, могут сочтет полезным узнать, как добиться чего-то похожего на то, что вы искали, если не совсем так же изящно, без трюков класса.

В ваших планах, BoolLike a ...

newtype BoolLike a = BoolLike ((a -> Bool) -> Bool) 

... состоит из функции, которая производит Bool результата, когда дается a -> Bool продолжения. Ваши примеры использования сводятся к объединению результатов Bool с (&&) и (||) перед подачей продолжения. Это может быть достигнуто с помощью Applicative экземпляра для функций (в данном случае, (->) (a -> Bool)) и используя (&)/flip ($) продвигать простые значения в (a -> Bool) -> Bool «взвешенных вычисления»:

GHCi> ((||) <$> ((&&) <$> ($ 1) <*> ($ 2)) <*> ($ 3)) (`elem` [2]) 
False 

Это, конечно, вовсе не аккуратный написать. Тем не менее, мы можем улучшить положение вещей довольно много, определяя:

(для маленькой библиотеки, определяющей их, посмотреть на control-bool.)

Вооруженное этим, дополнительные линии шум становится довольно легким:

GHCi> (($ 1) <&&> ($ 2) <||> ($ 3)) (`elem` [2]) 
False 

Это работает из коробки для contains случая, а также - все, что требуется меняются прилагаемое продолжение:

GHCi> (($ [1, 2]) <&&> ($ [2, 3]) <||> ($ [3, 4])) (elem 1) 
False 

в заключительной ноте, это стоит отметить, contains случай может быть прямолинейно выражена в терминах intersect и union от Data.List:

GHCi> [1, 2] `intersect` [2, 3] `union` [3, 4] & elem 1 
False 
+1

Также стоит отметить, что если вы помечаете свой " нижние значения ", как вы это делаете здесь с' $ ', это также заставляет проблему OP исчезнуть, так как операторы могут быть реализованы только для' BoolLike', и они не должны быть полиморфными. –