我想将函数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'
但是我对这需要的代码量不满意。
答案 0 :(得分:9)
Joachim的答案的一个稍微加强的版本,它使用Data.Map
来更快地执行查找。我也将使用TupleSections编译指示。
{-# LANGUAGE TupleSections #-}
import Data.Map
import Control.Monad
为了增加整洁度,假设您的Piece
类型可以被Ord
,Bounded
和Enum
个实例。
data Piece = Knight | Bishop | Rook deriving (Ord,Bounded,Enum,Show)
并定义有用的enumerate
函数
enumerate :: (Bounded a, Enum a) => [a]
enumerate = [minBound..maxBound]
现在你可以做到
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!)
答案 1 :(得分:6)
如果您有一个列表values :: [A]
,并且A
有一个Eq
- 实例,则可以使用:
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))
正如其他人所说,如果你不介意A
的其他类型类要求,你可以使用map或hashmaps来加速查找。
此外,根据您的用例说明,您似乎正在从程序附带的文件中加载静态数据。根据最终程序运行的环境(例如,保证文件存在且不会发生变化),这可能是unsafePerformIO
将A -> B
简单地定义为顶级函数的有效用途。或者,有一些方法可以在编译源中包含二进制blob。
答案 2 :(得分:4)
为了完整起见,我会提到countable package on Hackage通过提供Finite
类型类来实现这一点。你定义类似
instance Finite Piece where
allValues = [Pawn, Knight, Bishop, Rook, Queen, King]
然后你有
assemble :: (Finite a, Applicative f) => (a -> f b) -> f (a -> b)
将专门针对您的需求。
查看源代码,它似乎使用了一个关联列表,所以如果你的类型很大,它会很慢。另外,它为函数定义了Foldable
和Traversable
以及Eq
(!)的一些孤立实例,有些人可能认为这些实例是令人反感的。
答案 3 :(得分:0)
你有f :: A -> IO B
功能,你有g :: IO A
,
您将convert
功能与Applicative
<*> :: f (a -> b) -> f a -> f b
一起用作
fg :: IO a -> (a ->IO B) -> IO B
fg g f = (convert f) <*> g
但你可以使用monad (>>=) :: m a -> (a -> m b) -> m b
,
fg :: IO a -> (a ->IO B) -> IO B
fg g f = g >>= f
答案 4 :(得分:0)
您的函数签名允许输入任何函数a->m b
,但您可以在其中假设特定范围的值。 convert
并不像签名似乎声明的那样具有多态性。
你所做的是创建一个从a到b的Map,然后创建一个纯函数,在该映射中查找纯值。原因如下:
你所要求的类似于为幺半群类别(C,⊗,I)实施tensorial strength strength :: (Monad m) => (a, m b) -> m (a, b)
- 给定C类中的二元关系and和monad m,转换为⊗ mb到m(a⊗b)。如果这对于满足特定要求的二元关系是可能的,则monad很强。在Haskell中,所有monad都是强大的,如果张量乘积a ab被选择为(a, b)
strength (a, mb) = mb >>= return . (a,)
:->
。然而,在这里你试图对二元关系a -> b
做同样的事情。不幸的是,a
不能被选为张量积,因为它不是双向函数 - 它在(a,b)
中是逆变的。所以你想要的任何功能都无法实现。
在您的情况下有什么不同,基本上您构建了所有对a
。因此,如果您明确枚举所有可能的b
和m (Map a b)
对,例如通过构建{{1}},则可以减少代码量。这里的其他人提供了很好的糖,暴露了“类似功能”的接口,但它们只是在地图中查找。