Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid function duplication (IO and not-IO versions)

Tags:

haskell

I have two functions:

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    

First of them creates Map from two strings; second one generates random Map. First one returns Map.Map Char Char; second one returns IO (Map.Map Char Char)

Now I need to lookup value from this Map and I have created two functions - one for IO Map and one for 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

I don't like it because it causes code duplication. I have already duplicate one function and if I code by this way I will need to duplicate all my functions.

Is there a way to create function which will works with Map and IO (Map) the same way ?

like image 567
ceth Avatar asked Dec 15 '22 17:12

ceth


2 Answers

do-notation, or the "bind" operator >>= that it de-sugars to, takes care of this for you.

Anywhere that you would have called translate_char_io, you can use a call to the pure function by letting the monad syntax unwrap your table for you. For example, if you want to create a random table and look up two different characters in it, you could do it this way:

test c1 c2 = do
    table <- random_trans_table
    return (translate_char table c1, translate_char table c2)
like image 91
Jamey Sharp Avatar answered Dec 30 '22 10:12

Jamey Sharp


You can use liftM (import Control.Monad) to write a converter:

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

Note sure why you'd want only the table argument to be IO, but it's an option. Flipping the arguments would let you get rid of the inner function for just liftM (translate_char c) table as well. If you want both arguments to come as IO actions, you can also use liftM2:

translate_get_char = liftM2 translate_char random_trans_table getChar

Or you can just use do-notation to use your pure function in your IO code, which is probably easiest in this case:

translateAndPrint = do
    table <- random_trans_table
    char <- getChar
    putChar (translate_char table char)
like image 31
bdonlan Avatar answered Dec 30 '22 10:12

bdonlan