Автоматически получаемый Предоставляемый для предикатов по записям в SBV

Я нахожусь в ситуации, когда у меня есть тип данных, как

data X = X {foo :: SInteger, bar :: SInteger}

и я хочу доказать, например,

forAll_ $ \x -> foo x + bar x .== bar x + foo x

используя ssv Haskell. Это не компилируется, потому что X -> SBool не является экземпляром Provable. Я могу сделать это, например, с

instance (Provable p) => Provable (X -> p) where
  forAll_ k = forAll_ $ \foo bar -> forAll_ $ k $ X foo bar
  forAll (s : ss) k =
    forAll ["foo " ++ s, "bar " ++ s] $ \foo bar -> forAll ss $ k $ X foo bar
  forAll [] k = forAll_ k
  -- and similarly `forSome_` and `forSome`

но это утомительно и подвержено ошибкам (например, использование forSome когда forAll должен был быть использован). Есть ли способ автоматического вывода Provable для моего типа?

2 ответа

По крайней мере, это можно сделать менее подверженным ошибкам:

onX :: (((SInteger, SInteger) -> a) -> b) -> ((X -> a) -> b)
onX f g = f (g . uncurry X)

instance Provable p => Provable (X -> p) where
    forAll_  = onX forAll_
    forSome_ = onX forSome_
    forAll   = onX . forAll
    forSome  = onX . forSome

Существует также обобщаемая модель, в случае, если существующих экземпляров SBV для 7-кортежей недостаточно.

data Y = Y {a, b, c, d, e, f, g, h, i, j :: SInteger}
-- don't try to write the types of these, you will wear out your keyboard
fmap10 = fmap . fmap . fmap . fmap . fmap . fmap . fmap . fmap . fmap . fmap
onY f g = f (fmap10 g Y)

instance Provable p => Provable (Y -> p) where
    forAll_  = onY forAll_
    forSome_ = onY forSome_
    forAll   = onY . forAll
    forSome  = onY . forSome

Тем не менее, все еще утомительно.

Даниэль ответит "как хорошо", если вы действительно хотите использовать квантификаторы напрямую с вашими лямбда-выражениями. Однако вместо создания Provable Например, я настоятельно рекомендую определить вариант free для вашего типа:

freeX :: Symbolic X
freeX = do f <- free_
           b <- free_
           return $ X f b

Теперь вы можете использовать это так:

test = prove $ do x <- freeX
                  return $ foo x + bar x .== bar x + foo x

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

freeX :: Symbolic X
freeX = do f <- free_
           b <- free_
           constrain $ f .> b
           constrain $ b .> 0
           return $ X f b

Обратите внимание, что это будет работать правильно в обоих prove а также sat контексты, так как free знает, как правильно вести себя в каждом конкретном случае.

Я думаю, что это намного более читабельно и проще в использовании, даже если оно заставляет вас использовать do-нотацию. Вы также можете создать версию, которая принимает имена, например:

freeX :: String -> Symbolic X
freeX nm = do f <- free $ nm ++ "_foo"
              b <- free $ nm ++ "_bar"
              constrain $ f .> b
              constrain $ b .> 0
              return $ X f b

test = prove $ do x <- freeX "x"
                  return $ foo x + bar x .== bar x * foo x

Теперь мы получаем:

*Main> test
Falsifiable. Counter-example:
  x_foo = 3 :: Integer
  x_bar = 1 :: Integer

Вы также можете сделать X "разбирается" SBV. В этом случае полный код выглядит так:

data X = X {foo :: SInteger, bar :: SInteger} deriving Show

freeX :: Symbolic X
freeX = do f <- free_
           b <- free_
           return $ X f b

instance SatModel X where
  parseCWs xs = do (x, ys) <- parseCWs xs
                   (y, zs) <- parseCWs ys
                   return $ (X (literal x) (literal y), zs)

Следующий тест демонстрирует:

test :: IO (Maybe X)
test = extractModel `fmap` (prove $ do
                x <- freeX
                return $ foo x + bar x .== bar x * foo x)

У нас есть:

*Main> test >>= print
Just (X {foo = -4 :: SInteger, bar = -5 :: SInteger})

Теперь вы можете взять свои контрпримеры и постобработать их, как пожелаете.

Другие вопросы по тегам