Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A -> IO B to IO (A -> B)

Tags:

haskell

I want to convert a function A -> IO B to IO (A -> B), knowing that there is only a finite number of possible values of A. At the moment I just do

 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'

However I'm not satisfied with the amount of code this requires.

like image 251
Karolis Juodelė Avatar asked Sep 19 '13 09:09

Karolis Juodelė


People also ask

What is IO () Haskell?

IO is the way how Haskell differentiates between code that is referentially transparent and code that is not. IO a is the type of an IO action that returns an a . You can think of an IO action as a piece of code with some effect on the real world that waits to get executed.

What is an IO action?

IO actions are used to affect the world outside of the program. Actions take no arguments but have a result value. Actions are inert until run. Only one IO action in a Haskell program is run ( main ).

What is an IO string?

An IO String is a String in the IO-Monad. If a function in the IO-Monad returns an IO String, you get the String by doing: do str <- ioFunc.

What is an IO string Haskell?

You can think of IO as a wrapper. When you see a particular type, for example, x :: IO String , you should interpret that to mean " x is an action that, when performed, does some arbitrary I/O and then returns something of type String " (note that in Haskell, String and [Char] are exactly the same thing).


2 Answers

A slightly souped-up version of Joachim's answer, that uses Data.Map to perform the lookup faster. I'll be using the TupleSections pragma as well.

{-# LANGUAGE TupleSections #-}

import Data.Map
import Control.Monad

For added neatness, assume that your Piece type can be given Ord, Bounded and Enum instances.

data Piece = Knight | Bishop | Rook deriving (Ord,Bounded,Enum,Show)

and define the useful enumerate function

enumerate :: (Bounded a, Enum a) => [a]
enumerate = [minBound..maxBound]

Now you can do

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!)
like image 72
Chris Taylor Avatar answered Oct 11 '22 07:10

Chris Taylor


If you have a list values :: [A], and A has an Eq-Instance, this would work:

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))

As other have noted, if you don’t mind the additional type class requirements for A, you can use maps or hashmaps to speed up the lookup.

Also, from your use-case description, it seems that you are loading static data from a file that comes with your program. Depending on the environment where your final program runs (e.g. guaranteed that the files exist and are not changing), this might be a valid use for unsafePerformIO to simply define A -> B as a top-level function. Alternatively there are ways to include binary blobs in the compile source.

like image 42
Joachim Breitner Avatar answered Oct 11 '22 05:10

Joachim Breitner