A - > IO B到IO(A - > B)

Kar*_*elė 5 haskell

我想将函数转换A -> IO BIO (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)

但是我对这需要的代码量不满意.

Chr*_*lor 9

一个稍微加强版的Joachim的答案,用于Data.Map更快地执行查找.我也将使用TupleSections编译指示.

{-# LANGUAGE TupleSections #-}

import Data.Map
import Control.Monad
Run Code Online (Sandbox Code Playgroud)

为了增加整洁,假设你的Piece类型可以给出Ord,BoundedEnum实例.

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)

  • 这就是我过去的做法. (2认同)

Joa*_*ner 6

如果你有一个列表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.