如何避免功能重复(IO和非IO版本)

时间:2012-11-01 05:55:55

标签: haskell

我有两个功能:

trans_table :: [Char] -> [Char] -> Map.Map Char Char
trans_table s1 s2 = Map.fromList (zip s1 s2)

random_trans_table :: IO (Map.Map Char Char)
random_trans_table = do
    rawKey <- shuffle ascii
    let zipped = zip ascii rawKey 
    let map = Map.fromList zipped
    return map    

首先从两个字符串创建Map;第二个生成随机地图。 第一个返回Map.Map Char Char;第二个返回IO(Map.Map Char Char)

现在我需要从这个Map中查找值,我创建了两个函数 - 一个用于IO Map,另一个用于Map:

translate_char :: Map.Map Char Char -> Char -> Maybe Char
translate_char table c = Map.lookup c table

translate_char_io :: IO (Map.Map Char Char) -> Char -> IO (Maybe Char)
translate_char_io table c = do
    raw_table <- table
    let result = Map.lookup c raw_table
    return result

我不喜欢它,因为它会导致代码重复。我已经复制了一个函数,如果我通过这种方式编码,我将需要复制我的所有函数。

有没有办法创建与Map和IO(Map)一样的函数?

2 个答案:

答案 0 :(得分:8)

do - 符号,或者它所取消的“绑定”运算符>>=,为您处理此问题。

你可以调用translate_char_io的任何地方,你可以通过让monad语法为你打开你的表来调用纯函数。例如,如果您想创建一个随机表并在其中查找两个不同的字符,您可以这样做:

test c1 c2 = do
    table <- random_trans_table
    return (translate_char table c1, translate_char table c2)

答案 1 :(得分:4)

您可以使用liftMimport Control.Monad)来编写转换器:

translate_char_io table c = liftM (\table' -> translate_char table' c) table

请注意,为什么您只希望table参数为IO,但这是一个选项。翻转参数可以让你摆脱liftM (translate_char c) table的内部函数。如果您希望两个参数都作为IO操作,您也可以使用liftM2

translate_get_char = liftM2 translate_char random_trans_table getChar

或者您可以使用do-notation在IO代码中使用纯函数,在这种情况下这可能是最简单的:

translateAndPrint = do
    table <- random_trans_table
    char <- getChar
    putChar (translate_char table char)