我想将函数转换A -> IO B
为IO (A -> B)
,知道只有有限数量的可能值A
.目前我只是这样做
convert :: (A -> IO B) -> IO (A -> B)
convert f = do
b1 <- f a1
b2 <- f a2
...
let f' a1 = b1
f' a2 = b2
...
return f'
Run Code Online (Sandbox Code Playgroud)
但是我对这需要的代码量不满意.
一个稍微加强版的Joachim的答案,用于Data.Map
更快地执行查找.我也将使用TupleSections编译指示.
{-# LANGUAGE TupleSections #-}
import Data.Map
import Control.Monad
Run Code Online (Sandbox Code Playgroud)
为了增加整洁,假设你的Piece
类型可以给出Ord
,Bounded
和Enum
实例.
data Piece = Knight | Bishop | Rook deriving (Ord,Bounded,Enum,Show)
Run Code Online (Sandbox Code Playgroud)
并定义有用的enumerate
功能
enumerate :: (Bounded a, Enum a) => [a]
enumerate = [minBound..maxBound]
Run Code Online (Sandbox Code Playgroud)
现在你可以做到
convert :: (Monad m, Bounded a, Enum a, Ord a) => (a -> m b) -> m (a -> b)
convert f = do
memo <- sequence [liftM (a,) (f a) | a <- enumerate]
return (fromList memo!)
Run Code Online (Sandbox Code Playgroud)
如果你有一个列表values :: [A]
,并且A
有一个Eq
-Instance,这将有效:
convert :: (A -> IO B) -> IO (A -> B)
convert f = do
lookupTable <- sequence [ (\b -> (a,b)) `fmap` f a | a <- values]
return $ (\a -> fromJust (lookup a lookupTable))
Run Code Online (Sandbox Code Playgroud)
正如其他人所指出的,如果您不介意其他类型类要求A
,可以使用map或hashmaps来加速查找.
此外,从您的用例描述中,您似乎正在从程序附带的文件中加载静态数据.根据最终程序运行的环境(例如,保证文件存在且不会更改),这可能是一个有效的用途,unsafePerformIO
可以简单地定义A -> B
为顶级函数.或者,有一些方法可以在编译源中包含二进制blob.