6

我想将一个函数转换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'

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

4

5 回答 5

9

Joachim 答案的略微增强版本,用于Data.Map更快地执行查找。我也将使用 TupleSections pragma。

{-# LANGUAGE TupleSections #-}

import Data.Map
import Control.Monad

为了更加整洁,假设您的Piece类型可以给出Ord,BoundedEnum实例。

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!)
于 2013-09-19T11:08:28.247 回答
6

如果你有一个 list 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))

正如其他人所指出的,如果您不介意 的附加类型类要求A,您可以使用映射或哈希映射来加快查找速度。

此外,从您的用例描述来看,您似乎正在从程序附带的文件中加载静态数据。根据最终程序运行的环境(例如,保证文件存在并且不会更改),这可能是unsafePerformIO简单地定义A -> B为顶级函数的有效用途。或者,有一些方法可以在编译源中包含二进制 blob。

于 2013-09-19T09:08:11.043 回答
4

为了完整起见,我将提到Hackage 上的可数包通过提供Finite类型类使这成为可能。你定义了类似的东西

instance Finite Piece where
  allValues = [Pawn, Knight, Bishop, Rook, Queen, King]

那么你有

assemble :: (Finite a, Applicative f) => (a -> f b) -> f (a -> b)

它将专门满足您的需求。

看源码,好像是使用了关联列表,所以如果你的类型很大的话会很慢。此外,它还为函数定义了一些孤立的FoldableTraversableEq(!) 实例,有些人可能认为这些实例令人反感。

于 2013-09-19T18:07:52.390 回答
0

你有功能f :: A -> IO B ,你有g :: IO A,你用你的convert功能Applicative <*> :: f (a -> b) -> f a -> f bas

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
于 2013-09-19T09:52:09.410 回答
0

您的函数签名允许a->m b输入任何函数,但在您内部假设特定范围的值。convert不像签名似乎声明的那样多态。

您所做的是创建了一个从 a 到 b 的 Map,然后创建了一个纯函数,在该映射中查找纯值。原因如下:

你所要求的类似于为一个类 (C, ⊗, I)实现张量强度- 给定类别 C 中的二元关系 ⊗ 和一个单子 m,将 a ⊗ mb 转换为 m (a ⊗ b)。 strength :: (Monad m) => (a, m b) -> m (a, b)当这对于满足某些要求的二元关系是可能的时,monad 是强大的。在 Haskell 中,所有单子都是强的,如果张量积 a ⊗ b 被选为一对(a, b): strength (a, mb) = mb >>= return . (a,). 然而,在这里你试图对二元关系做同样的事情->。不幸的是,a -> b不能选择作为张量积,因为它不是双函子 - 它在a. 因此,您想要的任何功能都无法实现。

您的情况不同的是,本质上您构建了所有 pairs (a,b)。因此,如果您显式枚举所有可能的aand对b,例如通过构建一个m (Map a b). 这里的其他人提供了很好的糖,暴露了“类似函数”的接口,但它们只是地图中的查找。

于 2013-09-19T11:29:42.157 回答