在 SBV 中解决算术问题时如何避免 IO monad
How to avoid the IO monad when solving arithmetic problems in SBV
我正在尝试用 SBV 解决算术问题。
例如
solution :: SymbolicT IO ()
solution = do
[x, y] <- sFloats ["x", "y"]
constrain $ x + y .<= 2
Main> s1 = sat solution
Main> s2 = isSatisfiable solution
Main> s1
Satisfiable. Model:
x = -1.2030502e-17 :: Float
z = -2.2888208e-37 :: Float
Main> :t s1
s1 :: IO SatResult
Main> s2
True
Main> :t s2
s2 :: IO Bool
虽然我可以做一些有用的事情,但使用纯值(SatResult 或 Bool)比使用 IO monad 更容易。
根据文档
sat :: Provable a => a -> IO SatResult
constrain :: SolverContext m => SBool -> m ()
sFloats :: [String] -> Symbolic [SFloat]
type Symbolic = SymbolicT IO
考虑到我使用的函数类型,我理解为什么我总是使用 IO monad。
但是查看函数的 generalized 版本,例如 sFloats。
sFloats :: MonadSymbolic m => [String] -> m [SFloat]
根据函数的类型,我可以使用与 IO 不同的 monad。这给了我希望,我们将获得更有用的 monad,例如 Identity monad。
不幸的是,查看示例总能解决 IO monad 中的问题,因此我找不到任何适用于 me.Besides 我没有太多使用 monad 经验的示例。
最后我的问题是:
在使用 SBV 解决此类问题时,有什么方法可以避免 IO monad 吗?
提前致谢
Depending on type of the function, I can work with a different monad than IO.
从您希望的意义上来说,并没有明显的不同。 class 的每个实例都将成为 IO
的某个转换版本。抱歉!
是时候制定一个涉及理解和使用 IO
的计划了。
SBV 调出您选择的 SMT 解算器(很可能是 z3,但也可以使用其他解算器),然后将结果返回给您。这意味着它在幕后执行 IO
,因此你不能在 IO monad 之外。您可以使用 MonadSymbolic
创建自定义 monad,但这不会使您脱离 IO
monad:因为对 SMT 求解器的调用确实 IO
您将始终处于 IO 中。
(而且我强烈反对使用其中一条评论中建议的 unsafePerformIO
。这确实是个坏主意;您可以在其他地方找到更多关于此的信息,为什么您不应该这样做。)
请注意,这与 Haskell 中任何其他基于 IO 的计算没有什么不同:您“在包装器中”执行 IO,但是一旦获得结果,您就可以做任何您想做的事情喜欢在“纯净”的环境中与他们相处。
这是一个简单的例子:
import Data.SBV
import Data.SBV.Control
example :: IO ()
example = runSMT $ do
[x, y] <- sFloats ["x", "y"]
constrain $ x + y .<= 2
query $ do cs <- checkSat
case cs of
Unsat -> io $ putStrLn "Unsatisfiable"
Sat -> do xv <- getValue x
yv <- getValue y
let result = use xv yv
io $ putStrLn $ "Result: " ++ show result
_ -> error $ "Solver said: " ++ show cs
-- Use the results from the solver, in a purely functional way
use :: Float -> Float -> Float
use x y = x + y
现在你可以说:
*Main> example
Result: -Infinity
函数 example
的类型为 IO ()
,因为它确实涉及调用求解器并获取结果。但是,一旦您提取了这些结果(通过调用 getValue
),您就可以将它们传递给具有非常简单的纯函数类型的函数 use
。因此,您将“包装器”保留在 monad 中,但实际处理、值的使用等仍保留在纯世界中。
或者,您也可以提取值并从那里继续:
import Data.SBV
import Data.SBV.Control
example :: IO (Maybe (Float, Float))
example = runSMT $ do
[x, y] <- sFloats ["x", "y"]
constrain $ x + y .<= 2
query $ do cs <- checkSat
case cs of
Unsat -> pure Nothing
Sat -> do xv <- getValue x
yv <- getValue y
pure $ Just (xv, yv)
_ -> error $ "Solver said: " ++ show cs
现在你可以说:
*Main> Just (a, b) <- example
*Main> a
-Infinity
*Main> b
4.0302105e-21
长话短说:不要避免使用 IO monad。这是有充分理由的。进入它,得到你的结果,然后你的程序的其余部分可以保持纯粹的功能,或者你可能会发现自己处于的任何其他 monad。
请注意 none 这确实是 SBV 特定的。这是通常的 Haskell 范例,说明如何使用具有副作用的函数。 (例如,任何时候您使用 readFile
读取文件内容以进一步处理它。)不要试图“摆脱 IO”。相反,只需使用它即可。
我正在尝试用 SBV 解决算术问题。
例如
solution :: SymbolicT IO ()
solution = do
[x, y] <- sFloats ["x", "y"]
constrain $ x + y .<= 2
Main> s1 = sat solution
Main> s2 = isSatisfiable solution
Main> s1
Satisfiable. Model:
x = -1.2030502e-17 :: Float
z = -2.2888208e-37 :: Float
Main> :t s1
s1 :: IO SatResult
Main> s2
True
Main> :t s2
s2 :: IO Bool
虽然我可以做一些有用的事情,但使用纯值(SatResult 或 Bool)比使用 IO monad 更容易。
根据文档
sat :: Provable a => a -> IO SatResult
constrain :: SolverContext m => SBool -> m ()
sFloats :: [String] -> Symbolic [SFloat]
type Symbolic = SymbolicT IO
考虑到我使用的函数类型,我理解为什么我总是使用 IO monad。
但是查看函数的 generalized 版本,例如 sFloats。
sFloats :: MonadSymbolic m => [String] -> m [SFloat]
根据函数的类型,我可以使用与 IO 不同的 monad。这给了我希望,我们将获得更有用的 monad,例如 Identity monad。
不幸的是,查看示例总能解决 IO monad 中的问题,因此我找不到任何适用于 me.Besides 我没有太多使用 monad 经验的示例。
最后我的问题是:
在使用 SBV 解决此类问题时,有什么方法可以避免 IO monad 吗?
提前致谢
Depending on type of the function, I can work with a different monad than IO.
从您希望的意义上来说,并没有明显的不同。 class 的每个实例都将成为 IO
的某个转换版本。抱歉!
是时候制定一个涉及理解和使用 IO
的计划了。
SBV 调出您选择的 SMT 解算器(很可能是 z3,但也可以使用其他解算器),然后将结果返回给您。这意味着它在幕后执行 IO
,因此你不能在 IO monad 之外。您可以使用 MonadSymbolic
创建自定义 monad,但这不会使您脱离 IO
monad:因为对 SMT 求解器的调用确实 IO
您将始终处于 IO 中。
(而且我强烈反对使用其中一条评论中建议的 unsafePerformIO
。这确实是个坏主意;您可以在其他地方找到更多关于此的信息,为什么您不应该这样做。)
请注意,这与 Haskell 中任何其他基于 IO 的计算没有什么不同:您“在包装器中”执行 IO,但是一旦获得结果,您就可以做任何您想做的事情喜欢在“纯净”的环境中与他们相处。
这是一个简单的例子:
import Data.SBV
import Data.SBV.Control
example :: IO ()
example = runSMT $ do
[x, y] <- sFloats ["x", "y"]
constrain $ x + y .<= 2
query $ do cs <- checkSat
case cs of
Unsat -> io $ putStrLn "Unsatisfiable"
Sat -> do xv <- getValue x
yv <- getValue y
let result = use xv yv
io $ putStrLn $ "Result: " ++ show result
_ -> error $ "Solver said: " ++ show cs
-- Use the results from the solver, in a purely functional way
use :: Float -> Float -> Float
use x y = x + y
现在你可以说:
*Main> example
Result: -Infinity
函数 example
的类型为 IO ()
,因为它确实涉及调用求解器并获取结果。但是,一旦您提取了这些结果(通过调用 getValue
),您就可以将它们传递给具有非常简单的纯函数类型的函数 use
。因此,您将“包装器”保留在 monad 中,但实际处理、值的使用等仍保留在纯世界中。
或者,您也可以提取值并从那里继续:
import Data.SBV
import Data.SBV.Control
example :: IO (Maybe (Float, Float))
example = runSMT $ do
[x, y] <- sFloats ["x", "y"]
constrain $ x + y .<= 2
query $ do cs <- checkSat
case cs of
Unsat -> pure Nothing
Sat -> do xv <- getValue x
yv <- getValue y
pure $ Just (xv, yv)
_ -> error $ "Solver said: " ++ show cs
现在你可以说:
*Main> Just (a, b) <- example
*Main> a
-Infinity
*Main> b
4.0302105e-21
长话短说:不要避免使用 IO monad。这是有充分理由的。进入它,得到你的结果,然后你的程序的其余部分可以保持纯粹的功能,或者你可能会发现自己处于的任何其他 monad。
请注意 none 这确实是 SBV 特定的。这是通常的 Haskell 范例,说明如何使用具有副作用的函数。 (例如,任何时候您使用 readFile
读取文件内容以进一步处理它。)不要试图“摆脱 IO”。相反,只需使用它即可。