正如Moggi 20年前提出的那样,像ML这样的语言的有效函数空间->
可以分解为标准的总函数空间=>
加上强大的monad T
来捕获效果。
A -> B
分解为A => (T B)
现在,Haskell支持monad,包括一个看起来足以满足ML效果的IO monad,它有一个包含=>的函数空间。 (但也包括部分功能)。因此,我们应该能够通过这种分解将相当大的ML片段转换为Haskell。从理论上讲,我认为这是有效的。
我的问题是这样的嵌入是否可行:是否有可能设计一个Haskell库,允许在Haskell中以不太远的ML的方式编程?如果是这样,性能如何呢?
我对“实际”的标准是,现有的广泛使用效果的ML代码可以通过嵌入相对容易地转录到Haskell中,包括涉及高阶函数的复杂情况。
为了使这个具体化,我自己尝试通过嵌入这样的转录是在下面。主要功能是转录一些简单的ML代码,强制生成5个不同的变量名。我的版本不是直接使用分解,而是提升函数以便评估它们的参数 - main
之前的定义是一个包含提升原语的迷你库。这没关系,但有些方面并不完全令人满意。
val
将值注入到计算中会有太多的语法噪音。拥有未提升的函数版本(如rdV
)会有所帮助,但需要定义这些函数。varNum
等非值定义要求<-
中的do
进行一元绑定。然后,这会强制任何依赖于它们的定义也在同一个do
表达式中。do
表达式中。这就是ML程序经常被考虑的方式,但在Haskell中它并没有得到很好的支持 - 例如,你被迫使用case
代替方程式。那么,有关改进这一点的建议,或使用相同分解的更好方法,甚至是使用反映ML的样式在Haskell中实现相同的编程目标的完全不同的方法? (这不是我不喜欢Haskell的风格,只是因为我希望能够轻松地映射现有的ML代码。)
import Data.IORef
import Control.Monad
val :: Monad m => a -> m a
val = return
ref = join . liftM newIORef
rdV = readIORef -- Unlifted, hence takes a value
(!=) r x = do { rr <- r; xx <- x; writeIORef rr xx }
(.+),(.-) :: IO Int -> IO Int -> IO Int
( (.+),(.-) ) = ( liftM2(+), liftM2(-) )
(.:) :: IO a -> IO [a] -> IO [a]
(.:) = liftM2(:)
showIO :: Show a => IO a -> IO String
showIO = liftM show
main = do
varNum <- ref (val 0)
let newVar = (=<<) $ \() -> val varNum != (rdV varNum .+ val 1) >>
val 'v' .: (showIO (rdV varNum))
let gen = (=<<) $ \n -> case n of 0 -> return []
nn -> (newVar $ val ()) .: (gen (val n .- val 1))
gen (val 5)