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

时间:2013-09-19 09:03:03

标签: haskell

我想将函数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'

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

5 个答案:

答案 0 :(得分:9)

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

{-# LANGUAGE TupleSections #-}

import Data.Map
import Control.Monad

为了增加整洁度,假设您的Piece类型可以被OrdBoundedEnum个实例。

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来加速查找。

此外,根据您的用例说明,您似乎正在从程序附带的文件中加载静态数据。根据最终程序运行的环境(例如,保证文件存在且不会发生变化),这可能是unsafePerformIOA -> 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)

将专门针对您的需求。

查看源代码,它似乎使用了一个关联列表,所以如果你的类型很大,它会很慢。另外,它为函数定义了FoldableTraversable以及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。因此,如果您明确枚举所有可能的bm (Map a b)对,例如通过构建{{1}},则可以减少代码量。这里的其他人提供了很好的糖,暴露了“类似功能”的接口,但它们只是在地图中查找。