即使使用 bang 模式也无法调用 IO () 函数
Cannot call IO () function even with bang patterns
我有一些库具有使用 gnuplot 库制作绘图的功能:
import Graphics.Gnuplot.Simple
drawMap :: [(Int, Int)] -> IO ()
drawMap m = do
!a <- plotList [] m
print a
我从 main 调用这个函数是这样的:
main = do
!a <- drawMap [(1,2),(3,5)]
print a
我用堆栈构建项目,我尝试了 -O2 和 -O0 优化,但绘图永远不起作用(print a
函数总是被成功调用并打印 ()
)。
我如何强制绘图以及为什么它对库不起作用,但如果我只是从 main 调用 plotList
?
UPD.
在 main 和 drawMap
by $!
中使用严格的应用程序也不起作用:
drawMap :: [(Int, Int)] -> IO ()
drawMap m = plotList [] $! m
main = do
drawMap $! [(1,2),(3,5)]
UPD 2
一些最小的例子:
这对我不起作用:
import Graphics.Gnuplot.Simple
main = plotList [] ([(1,2),(3,5)] :: [(Int,Int)])
但这行得通:
{-# LANGUAGE BangPatterns #-}
import Graphics.Gnuplot.Simple
main = do
!a <- plotList [] ([(1,2),(3,5)] :: [(Int,Int)])
print a
但是如果 drawMap
在 main
.
之外的其他模块中,即使使用 bang patterns/strict 应用程序,我的问题中的代码也不起作用
严格是转移注意力的问题。该库没有正确执行并发。一些来源潜水显示:
runGnuplot ::
Graph.C graph =>
[Attribute] -> String -> Plot.T graph -> IO ()
runGnuplot attrs cmd (Plot.Cons mp) =
void $ Cmd.asyncIfInteractive (interactiveTerm attrs) $ Cmd.run $ \dir ->
let files = MR.runReader (MS.evalStateT mp 0) dir
in (map attrToProg attrs ++
[cmd ++ " " ++
extractRanges attrs ++ " " ++
commaConcat (plotFileStatements files)],
files)
interactiveTerm :: [Attribute] -> Bool
interactiveTerm =
all $ \attr ->
case attr of
Terminal term -> Terminal.interactive term
PNG _ -> False
EPS _ -> False
_ -> True
asyncIfInteractive :: Bool -> IO ExitCode -> IO ExitCode
asyncIfInteractive interactive act =
if interactive
then fmap (const ExitSuccess) $ forkIO $ void act
else act
特别是,当属性列表为空时,它将被视为“交互式”,并且 IO
操作将被分叉到它自己的线程中。 Haskell 程序的语义是它们在 main
线程退出时退出,所以这是一个竞争条件:将 main
首先退出,还是分叉线程调用 gnuplot
第一?
这里正确的做法是 runGnuplot
为用户提供 IO
等待分叉线程完成的操作,他们可以从 main
线程调用(例如,通过分配一个 MVar
,在分叉线程中写入它,并在返回的操作中从中读取)。一个简单的错误做法是将 threadDelay
扔进你的程序中:
import Graphics.Gnuplot.Simple
import Control.Concurrent
main = do
plotList [] ([(1,2),(3,5)] :: [(Int,Int)])
threadDelay 1000000
这仍然是一个竞争条件,但现在分叉线程至少有一秒钟的时间在程序被强制销毁之前完成其 gnuplot
调用 -- for a computer, that is nearly an eternity.
我有一些库具有使用 gnuplot 库制作绘图的功能:
import Graphics.Gnuplot.Simple
drawMap :: [(Int, Int)] -> IO ()
drawMap m = do
!a <- plotList [] m
print a
我从 main 调用这个函数是这样的:
main = do
!a <- drawMap [(1,2),(3,5)]
print a
我用堆栈构建项目,我尝试了 -O2 和 -O0 优化,但绘图永远不起作用(print a
函数总是被成功调用并打印 ()
)。
我如何强制绘图以及为什么它对库不起作用,但如果我只是从 main 调用 plotList
?
UPD.
在 main 和 drawMap
by $!
中使用严格的应用程序也不起作用:
drawMap :: [(Int, Int)] -> IO ()
drawMap m = plotList [] $! m
main = do
drawMap $! [(1,2),(3,5)]
UPD 2 一些最小的例子:
这对我不起作用:
import Graphics.Gnuplot.Simple
main = plotList [] ([(1,2),(3,5)] :: [(Int,Int)])
但这行得通:
{-# LANGUAGE BangPatterns #-}
import Graphics.Gnuplot.Simple
main = do
!a <- plotList [] ([(1,2),(3,5)] :: [(Int,Int)])
print a
但是如果 drawMap
在 main
.
严格是转移注意力的问题。该库没有正确执行并发。一些来源潜水显示:
runGnuplot ::
Graph.C graph =>
[Attribute] -> String -> Plot.T graph -> IO ()
runGnuplot attrs cmd (Plot.Cons mp) =
void $ Cmd.asyncIfInteractive (interactiveTerm attrs) $ Cmd.run $ \dir ->
let files = MR.runReader (MS.evalStateT mp 0) dir
in (map attrToProg attrs ++
[cmd ++ " " ++
extractRanges attrs ++ " " ++
commaConcat (plotFileStatements files)],
files)
interactiveTerm :: [Attribute] -> Bool
interactiveTerm =
all $ \attr ->
case attr of
Terminal term -> Terminal.interactive term
PNG _ -> False
EPS _ -> False
_ -> True
asyncIfInteractive :: Bool -> IO ExitCode -> IO ExitCode
asyncIfInteractive interactive act =
if interactive
then fmap (const ExitSuccess) $ forkIO $ void act
else act
特别是,当属性列表为空时,它将被视为“交互式”,并且 IO
操作将被分叉到它自己的线程中。 Haskell 程序的语义是它们在 main
线程退出时退出,所以这是一个竞争条件:将 main
首先退出,还是分叉线程调用 gnuplot
第一?
这里正确的做法是 runGnuplot
为用户提供 IO
等待分叉线程完成的操作,他们可以从 main
线程调用(例如,通过分配一个 MVar
,在分叉线程中写入它,并在返回的操作中从中读取)。一个简单的错误做法是将 threadDelay
扔进你的程序中:
import Graphics.Gnuplot.Simple
import Control.Concurrent
main = do
plotList [] ([(1,2),(3,5)] :: [(Int,Int)])
threadDelay 1000000
这仍然是一个竞争条件,但现在分叉线程至少有一秒钟的时间在程序被强制销毁之前完成其 gnuplot
调用 -- for a computer, that is nearly an eternity.