How do laziness and exceptions work together in Haskell? - exception

The question is similar to this question. However, this one is about exceptions, not about lazy I/O.
Here is a test:
{-# LANGUAGE ScopedTypeVariables #-}
import Prelude hiding ( catch )
import Control.Exception
fooLazy :: Int -> IO Int
fooLazy m = return $ 1 `div` m
fooStrict :: Int -> IO Int
fooStrict m = return $! 1 `div` m
test :: (Int -> IO Int) -> IO ()
test f = print =<< f 0 `catch` \(_ :: SomeException) -> return 42
testLazy :: Int -> IO Int
testLazy m = (return $ 1 `div` m) `catch` \(_ :: SomeException) -> return 42
testStrict :: Int -> IO Int
testStrict m = (return $! 1 `div` m) `catch` \(_ :: SomeException) -> return 42
So I wrote two functions fooLazy which is lazy and fooStrict which is strict, also there is two tests testLazy and testStrict, then I try to catch division by zero:
> test fooLazy
*** Exception: divide by zero
> test fooStrict
42
> testLazy 0
*** Exception: divide by zero
> testStrict 0
42
and it fails in lazy cases.
The first thing that comes to mind is to write a version of the catch function that force the evaluation on its first argument:
{-# LANGUAGE ScopedTypeVariables #-}
import Prelude hiding ( catch )
import Control.DeepSeq
import Control.Exception
import System.IO.Unsafe
fooLazy :: Int -> IO Int
fooLazy m = return $ 1 `div` m
fooStrict :: Int -> IO Int
fooStrict m = return $! 1 `div` m
instance NFData a => NFData (IO a) where
rnf = rnf . unsafePerformIO
catchStrict :: (Exception e, NFData a) => IO a -> (e -> IO a) -> IO a
catchStrict = catch . force
test :: (Int -> IO Int) -> IO ()
test f = print =<< f 0 `catchStrict` \(_ :: SomeException) -> return 42
testLazy :: Int -> IO Int
testLazy m = (return $ 1 `div` m) `catchStrict` \(_ :: SomeException) -> return 42
testStrict :: Int -> IO Int
testStrict m = (return $! 1 `div` m) `catchStrict` \(_ :: SomeException) -> return 42
it seems to work:
> test fooLazy
42
> test fooStrict
42
> testLazy 0
42
> testStrict 0
42
but I use the unsafePerformIO function here and this is scary.
I have two questions:
Can one be sure that the catch function always catches all exceptions, regardless of the nature of it first argument?
If not, is there a well-known way to deal with this kind of problems? Something like the catchStrict function is suitable?
UPDATE 1.
This is a better version of the catchStrict function by nanothief:
forceM :: (Monad m, NFData a) => m a -> m a
forceM m = m >>= (return $!) . force
catchStrict :: (Exception e, NFData a) => IO a -> (e -> IO a) -> IO a
catchStrict expr = (forceM expr `catch`)
UPDATE 2.
Here is another 'bad' example:
main :: IO ()
main = do
args <- getArgs
res <- return ((+ 1) $ read $ head args) `catch` \(_ :: SomeException) -> return 0
print res
It should be rewritten like this:
main :: IO ()
main = do
args <- getArgs
print ((+ 1) $ read $ head args) `catch` \(_ :: SomeException) -> print 0
-- or
--
-- res <- return ((+ 1) $ read $ head args) `catchStrict` \(_ :: SomeException) -> return 0
-- print res
--
-- or
--
-- res <- returnStrcit ((+ 1) $ read $ head args) `catch` \(_ :: SomeException) -> return 0
-- print res
--
-- where
returnStrict :: Monad m => a -> m a
returnStrict = (return $!)
UPDATE 3.
As nanothief noticed, there is no guarantee that the catch function always catch any exception. So one need to use it carefully.
Few tips on how to solve related problems:
Use ($!) with return, use forceM on the first argument of catch, use the catchStrict function.
I also noticed that sometimes people add some strictness to instances of their transformers.
Here is an example:
{-# LANGUAGE GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances
, MultiParamTypeClasses, UndecidableInstances, ScopedTypeVariables #-}
import System.Environment
import Prelude hiding ( IO )
import qualified Prelude as P ( IO )
import qualified Control.Exception as E
import Data.Foldable
import Data.Traversable
import Control.Applicative
import Control.Monad.Trans
import Control.Monad.Error
newtype StrictT m a = StrictT { runStrictT :: m a } deriving
( Foldable, Traversable, Functor, Applicative, Alternative, MonadPlus, MonadFix
, MonadIO
)
instance Monad m => Monad (StrictT m) where
return = StrictT . (return $!)
m >>= k = StrictT $ runStrictT m >>= runStrictT . k
fail = StrictT . fail
instance MonadTrans StrictT where
lift = StrictT
type IO = StrictT P.IO
instance E.Exception e => MonadError e IO where
throwError = StrictT . E.throwIO
catchError m h = StrictT $ runStrictT m `E.catch` (runStrictT . h)
io :: StrictT P.IO a -> P.IO a
io = runStrictT
It is essentially the identity monad transformer, but with strict return:
foo :: Int -> IO Int
foo m = return $ 1 `div` m
fooReadLn :: Int -> IO Int
fooReadLn x = liftM (`div` x) $ liftIO readLn
test :: (Int -> IO Int) -> P.IO ()
test f = io $ liftIO . print =<< f 0 `catchError` \(_ :: E.SomeException) -> return 42
main :: P.IO ()
main = io $ do
args <- liftIO getArgs
res <- return ((+ 1) $ read $ head args) `catchError` \(_ :: E.SomeException) -> return 0
liftIO $ print res
-- > test foo
-- 42
-- > test fooReadLn
-- 1
-- 42
-- ./main
-- 0

Firstly (I'm not sure if you know this already), the reason the catch doesn't work with the lazy case is the
1 `div` 0
expression isn't evaluated until it is needed, which is inside the print function. However, the catch method is applied just to the f 0 expression, not the whole print =<< f 0 expression, so the exception isn't caught. If you did:
test f = (print =<< f 0) `catch` \(_ :: SomeException) -> print 42
instead, it works correctly in both cases.
If you want to make a catch statement though that forces complete evaluation of the IO result, instead of making a new instance of NFData, you could write a forceM method, and use that in the catchStrict method:
forceM :: (Monad m, NFData a) => m a -> m a
forceM m = m >>= (return $!) . force
catchStrict :: (Exception e, NFData a) => IO a -> (e -> IO a) -> IO a
catchStrict expr = (forceM expr `catch`)
(I'm a bit surprised that forceM isn't inside the Control.DeepSeq library)
Regarding your comment:
No, the rule is the exception is only thrown when the value is computed, and that is only done when it is needed by haskell. And if haskell can delay the evaluation of something it will.
An example test function that doesn't use $!, but still causes an exception straight away (so the normal catch will catch the divide by zero exception) is:
fooEvaluated :: Int -> IO Int
fooEvaluated m = case 3 `div` m of
3 -> return 3
0 -> return 0
_ -> return 1
Haskell is forced to evaluated the "3 `div` m" expression, as it needs to match the result against 3 and 0.
As a last example, the following doesn't throw any exception, and when used with the test function returns 1:
fooNoException :: Int -> IO Int
fooNoException m = case 3 `div` m of
_ -> return 1
This is because haskell never needs to calculate "3 `div` m" expression (as _ matches everything), so it is never calculated, hence no exception is thrown.

Related

Haskell cassava (Data.Csv): Carry along additional columns

I have two .csv files
A.csv:
A,B,C,D,E
1,2,3,4,5
5,4,3,2,1
B.csv
A,E,B,C,F
6,7,8,9,1
4,3,4,5,6
I would like to read them in Haskell with strict parsing rules for the variables A, B and C. I would then like to apply complex merge and filter operations to the rows of A.csv and B.csv and create a file C.csv from the result. The code block at the end of this post essentially covers this functionality.
Question:
I would now like to do all of this while keeping the variables D, E and F around. In my real dataset I have an unknown and arbitrary number of such additional columns. I can not easily represent them in the respective data type (ABC below). All of them should stay and be properly represented in the output dataset.
With the code below, C.csv looks like this:
A,B,C
1,2,3
5,4,3
6,8,9
4,4,5
I would instead like to have a result like this:
A,B,C,D,E,F
1,2,3,4,5,_
5,4,3,2,1,_
6,8,9,_,7,1
4,4,5,_,3,6
Is there a way to do this with cassava? Do I have to write a custom parser from scratch to get this functionality? How would I go about this?
This example code lacks the desired feature. It is a self-contained stack script.
#!/usr/bin/env stack
-- stack --resolver lts-18.7 script --package cassava,bytestring,vector
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import qualified Data.ByteString.Lazy as B
import qualified Data.Csv as C
import qualified Data.Vector as V
data ABC = ABC {a :: Int, b :: Int, c :: Int} deriving Show
instance C.FromNamedRecord ABC where
parseNamedRecord m =
ABC <$> m C..: "A" <*> m C..: "B" <*> m C..: "C"
instance C.ToNamedRecord ABC where
toNamedRecord ABC {..} =
C.namedRecord ["A" C..= a, "B" C..= b, "C" C..= c]
decodeABC :: B.ByteString -> [ABC]
decodeABC x =
case C.decodeByName x of
Left err -> error err
Right (_,xs) -> V.toList xs
header :: C.Header
header = V.fromList ["A", "B", "C"]
main :: IO ()
main = do
fileA <- B.readFile "A.csv"
fileB <- B.readFile "B.csv"
let decodedA = decodeABC fileA
let decodedB = decodeABC fileB
putStrLn $ show decodedA
putStrLn $ show decodedB
B.writeFile "C.csv" $ C.encodeByName header (decodedA ++ decodedB)
This code includes the desired feature (thanks to the input of #Daniel Wagner):
#!/usr/bin/env stack
-- stack --resolver lts-18.7 script --package cassava,bytestring,vector,unordered-containers
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import qualified Data.ByteString.Lazy as B
import qualified Data.Csv as C
import qualified Data.HashMap.Strict as HM
import qualified Data.Vector as V
data ABC = ABC {a :: Int, b :: Int, c :: Int, addCols :: C.NamedRecord} deriving Show
abcDefinedCols = ["A", "B", "C"]
abcRefHashMap = HM.fromList $ map (\x -> (x, ())) abcDefinedCols
instance C.FromNamedRecord ABC where
parseNamedRecord m =
pure ABC
<*> m C..: "A"
<*> m C..: "B"
<*> m C..: "C"
<*> pure (m `HM.difference` abcRefHashMap)
instance C.ToNamedRecord ABC where
toNamedRecord m =
(addCols m) `HM.union` C.namedRecord ["A" C..= a m, "B" C..= b m, "C" C..= c m]
decodeABC :: B.ByteString -> [ABC]
decodeABC x =
case C.decodeByName x of
Left err -> error err
Right (_,xs) -> V.toList xs
makeCompleteHeader :: [ABC] -> C.Header
makeCompleteHeader ms = V.fromList $ abcDefinedCols ++ HM.keys (HM.unions (map addCols ms))
combineABCs :: [ABC] -> [ABC] -> [ABC]
combineABCs xs1 xs2 =
let simpleSum = xs1 ++ xs2
addColKeys = HM.keys (HM.unions (map addCols simpleSum))
toAddHashMap = HM.fromList (map (\k -> (k, "n/a")) addColKeys)
in map (\x -> x { addCols = fillAddCols (addCols x) toAddHashMap }) simpleSum
where
fillAddCols :: C.NamedRecord -> C.NamedRecord -> C.NamedRecord
fillAddCols cur toAdd = HM.union cur (toAdd `HM.difference` cur)
main :: IO ()
main = do
fileA <- B.readFile "A.csv"
fileB <- B.readFile "B.csv"
let decodedA = decodeABC fileA
let decodedB = decodeABC fileB
putStrLn $ show decodedA
putStrLn $ show decodedB
let ab = combineABCs decodedA decodedB
B.writeFile "C.csv" $ C.encodeByName (makeCompleteHeader ab) ab
data ABCPlus = ABCPlus { a :: Int, b :: Int, c :: Int, d :: NamedRecord } deriving Show
instance FromNamedRecord ABCPlus where
parseNamedRecord m = pure ABC
<*> m .: "A"
<*> m .: "B"
<*> m .: "C"
<*> pure m -- or perhaps: pure (m `HM.difference` HM.fromList [("A", ()), ("B", ()), ("C", ())])
instance ToNamedRecord ABCPlus where
toNamedRecord m = d m -- or perhaps: d m `HM.union` namedRecord ["A" .= a m, "B" .= b m, "C" .= c m]
headers :: [ABCPlus] -> Header
headers ms = header $ ["A", "B", "C"] ++ HM.keys (relevant combined) where
relevant m = m `HM.difference` HM.fromList [("A", ()), ("B", ()), ("C", ())] -- or perhaps: m
combined = HM.unions [relevantKeys (d m) | m <- ms]

How can I draw that number of cards from a deck in haskell

In this program , I wanted to ask the user about number of cards and draw that number of cards from a deck (see below) and tell the user the cards and the
"total" of those cards. In this case, I mean a blackjack count of up to 21, with
counts over 21 returning Nothing. A blackjack count counts 2-10 as its face value, jacks,
queens and kings count as 10 and aces count as 1 or 11. I need two functions:
drawHand :: Int ->Deck ->([Card],Deck) and totalCards :: [Card] ->Maybe Int
import Data.List
import Data.Random
drawHand :: Int -> Deck -> ([Card], Deck)
totalCards :: [Card] -> Maybe Int
main = do
putStrLn "How many cards?"
Random :: MonadRandom m => Deck-> m Deck
Random ran = runRVar (shuffle deck) StdRandom
Random <- getLine
putStrLn "Hand of [] totalCards: " ++ totalCards
error:
Failed to load interface for ‘Data.Random’
Perhaps you meant Data.Ratio (from base-4.9.0.0)
Use -v to see a list of the files searched for.
PLEASE HELP ME
At this point we have no information about the Card and Deck data types.
However, it seems that the problem at hand is to randomly extract M cards from an initial deck of N cards.
If this interpretation of the question is correct, we can thus use the Rand monad constructor, and start by defining a monadic action that transfers just one card from the right deck to the left deck.
As we have no information about the types in use, we will assume that the “cards” are denoted by plain numbers, from 0 to 51.
Next, we define an action moving M cards recursively, moving one card and then calling ourselves with an (M-1) argument. For M=0, we define the action as a no-op.
This would be the monadic code:
import System.Random
import Control.Monad.Random
moveOneCardLeft :: RandomGen g => ([a],[a]) -> Rand g ([a],[a])
moveOneCardLeft (deck, rest) =
do
let remCount = length rest
choice <- getRandomR (0, (remCount-1))
let (top, bot) = splitAt choice rest
return $ ((head bot) : deck, top ++ (tail bot))
moveSomeCardsLeft :: RandomGen g => Int -> ([a],[a]) -> Rand g ([a],[a])
moveSomeCardsLeft 0 (deck, rest) = return (deck, rest) -- unchanged
moveSomeCardsLeft n (deck, rest) =
do
(deck1, rest1) <- moveOneCardLeft (deck, rest)
(deck2, rest2) <- moveSomeCardsLeft (n-1) (deck1, rest1)
return (deck2, rest2)
extractSomeCards :: RandomGen g => Int -> [a] -> Rand g ([a], [a])
extractSomeCards n xs =
do
(deck, rest) <- moveSomeCardsLeft n ([], xs)
return (deck, rest)
Next, the pure code and some tentative game-related utility functions:
drawSomeCards :: RandomGen g => g -> Int -> [a] -> (([a], [a]), g)
drawSomeCards gen0 n xs = runRand (extractSomeCards n xs) gen0
cardValue :: Int -> Int
cardValue n = let rank = mod n 13
in if (rank < 10) then (rank+1)
else {- Jack Queen King -} 10
deckValue :: [Int] -> Int
deckValue cards = sum (map cardValue cards)
totalOfCards :: [Int] -> Maybe Int
totalOfCards cards =
let s = deckValue cards
in if (s <= 21) then (Just s) else Nothing
Finally, the user test code:
main = do
let wholeDeck = [0..51]
randomSeed = 4243
gen0 = mkStdGen randomSeed
putStrLn "How many cards ?"
inLine <- getLine
let count = (read inLine :: Int)
putStrLn $ "Want to extract " ++ (show count) ++ " cards."
let ((deck, rest), gen1) = drawSomeCards gen0 count wholeDeck
sumw = sum wholeDeck
suma = sum deck
sumb = sum rest
sum0 = (suma + sumb) - sumw
putStrLn $ "Must be zero: " ++ (show sum0) -- sanity check
putStrLn $ "deck: " ++ (show deck)
putStrLn $ "rest: " ++ (show rest)
putStrLn $ "Deck value: " ++ (show $ deckValue deck)
Program execution:
$ q67025780.x
How many cards ?
10
Want to extract 10 cards.
Must be zero: 0
deck: [8,47,38,49,4,31,9,30,28,23]
rest: [0,1,2,3,5,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,24,25,26,27,29,32,33,34,35,36,37,39,40,41,42,43,44,45,46,48,50,51]
Deck value: 77
$
Note: if deemed appropriate, the above code beyond moveOneCardLeft can be simplified using the nest :: Monad m => Int -> (a -> m a) -> a -> m a function from the Control.Monad.HT package.
Like this:
import Control.Monad.HT (nest)
moveOneCardLeft :: RandomGen g => ([a],[a]) -> Rand g ([a],[a])
moveOneCardLeft (deck, rest) =
do
let remCount = length rest
choice <- getRandomR (0, (remCount-1))
let (top, bot) = splitAt choice rest
return $ ((head bot) : deck, top ++ (tail bot))
drawSomeCards :: RandomGen g => g -> Int -> [a] -> (([a], [a]), g)
drawSomeCards gen0 n xs = let action = nest n moveOneCardLeft ([], xs)
in runRand action gen0

Abstracting Function in Haskell

I am currently taking a class in Haskell and am having a bit of trouble understanding how functions are passed as parameters. For this assignment, we were tasked with creating a program that would evaluate expressions. To reduce boiler plating, I wanted to abstract the function by creating a helper function that would take in an operator as an input and return the result
Main Function:
eval :: EDict -> Expr -> Maybe Double
eval _ (Val x) = Just x
eval d (Var i) = find d i
eval d (Add x y) = evalOp d (+) x y
eval d (Mul x y) = evalOp d (*) x y
eval d (Sub x y) = evalOp d (-) x y
Helper Function:
evalOp:: EDict -> ((Num a) => a -> a -> a) -> Expr -> Expr -> Maybe Double
evalOp d op x y =
let r = eval d x
s = eval d y
in case (r, s) of
(Just m, Just n) -> Just (m `op` n)
_ -> Nothing
Other definitions
data Expr
= Val Double
| Add Expr Expr
| Mul Expr Expr
| Sub Expr Expr
| Dvd Expr Expr
| Var Id
| Def Id Expr Expr
deriving (Eq, Show)
type Dict k d = [(k,d)]
define :: Dict k d -> k -> d -> Dict k d
define d s v = (s,v):d
find :: Eq k => Dict k d -> k -> Maybe d
find [] _ = Nothing
find ( (s,v) : ds ) name | name == s = Just v
| otherwise = find ds name
type EDict = Dict String Double
I looked into how +,-, and * are to be passed into other functions and found that these operators are defined by the following definition:
ghci> :t (*)
(*) :: (Num a) => a -> a -> a
However, when I run my code I get the following compilation error:
Illegal polymorphic or qualified type: Num a => a -> a -> a
Perhaps you intended to use RankNTypes or Rank2Types
In the type signature for ‘evalOp’:
evalOp :: EDict
-> ((Num a) => a -> a -> a) -> Expr -> Expr -> Maybe Double
I am not really sure why this is happening as I gave my function the proper parameters as defined by Haskell. Any help would be greatly appreciated as I am still very new to the language.
Right now, your Expr data type is constrained to Double-valued expressions, so there is no need to deal with polymorphism.
evalOp:: EDict -> (Double -> Double -> Double) -> Expr -> Expr -> Maybe Double
evalOp d op x y =
let r = eval d x
s = eval d y
in case (r, s) of
(Just m, Just n) -> Just (m `op` n)
_ -> Nothing
(+) :: Num a => a -> a -> a is a valid argument for evalOp, because its type can be "restricted" to Double -> Double -> Double.
> let f :: Double -> Double -> Double; f = (+)
> f 3 5
8.0
If your expression type were parameterized, then you would put a Num a constraint on your functions (not just on the arguments that involve a, because you want the same a throughout the function).
data Expr a
= Val a
| Add (Expr a) (Expr a)
| Mul (Expr a) (Expr a)
| Sub (Expr a) (Expr a)
| Dvd (Expr a) (Expr a)
| Var Id
| Def Id (Expr a) (Expr a)
deriving (Eq, Show)
type EDict a = Dict String a
evalOp:: Num a => EDict a -> (a -> a -> a) -> Expr a -> Expr a -> Maybe a
evalOp d op x y =
let r = eval d x
s = eval d y
in case (r, s) of
(Just m, Just n) -> Just (m `op` n)
_ -> Nothing
eval :: Num a => EDict a -> Expr a -> Maybe a
eval _ (Val x) = Just x
eval d (Var i) = find d i
eval d (Add x y) = evalOp d (+) x y
eval d (Mul x y) = evalOp d (*) x y
eval d (Sub x y) = evalOp d (-) x y
The error is telling you that you cannot nest a type qualifier inside one of the types in your function chain. Instead, put all of the qualifiers at the beginning of the type signature:
evalOp:: (Num a) => EDict -> (a -> a -> a) -> Expr -> Expr -> Maybe Double
See Haskell - Illegal Polymorphic type? for a more thorough discussion.

Haskell program runs out of memory when trying to parse 115MB JSON file using Aeson

My Haskell program runs out of memory when trying to parse a 115MB JSON file. I suspect I'm doing something that you shouldn't do in Haskell - at an earlier step in the program, I ran out of memory because I was operating on Strings instead of ByteStrings - but I am unable to figure out what.
I've condensed my program down into the following MWE:
{-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings, FlexibleInstances #-}
----------------------------------------
-- Imports
----------------------------------------
import System.Environment
( getArgs )
import Control.Monad
( mzero
, when
)
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.Aeson
import Data.Maybe
import Data.Scientific
( Scientific )
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Vector as V
----------------------------------------
-- Data types
----------------------------------------
newtype Natural
= Natural Integer
deriving (Show, Eq, Ord)
instance Num Natural where
fromInteger = toNatural
x + y = toNatural (fromNatural x + fromNatural y)
x - y = let r = fromNatural x - fromNatural y
in if r < 0
then error "Subtraction yielded a negative value"
else toNatural r
x * y = toNatural (fromNatural x * fromNatural y)
abs x = x
signum x = toNatural $ signum $ fromNatural x
instance Enum Natural where
toEnum = toNatural . toInteger
fromEnum = fromInteger . fromNatural
instance Real Natural where
toRational (Natural i) = toRational i
instance Integral Natural where
quotRem (Natural x) (Natural y) =
( toNatural $ quot x y
, toNatural $ rem x y
)
toInteger (Natural i) = i
instance FromJSON Natural where
parseJSON (Number sn) = return $ sn2nat sn
parseJSON _ = mzero
instance ToJSON Natural where
toJSON i = toJSON (fromNatural i)
----------------------------------------
data PatternMatchset
= PatternMatchset
{ pmTarget :: TargetMachineID
, pmMatches :: [PatternMatch]
, pmTime :: Maybe Double
}
deriving (Show)
instance FromJSON PatternMatchset where
parseJSON (Object v) =
PatternMatchset
<$> v .: "target-machine-id"
<*> v .: "match-data"
<*> v .: "time"
parseJSON _ = mzero
instance ToJSON PatternMatchset where
toJSON m =
object [ "target-machine-id" .= (pmTarget m)
, "match-data" .= (pmMatches m)
, "time" .= (pmTime m)
]
----------------------------------------
data PatternMatch
= PatternMatch
{ pmInstrID :: InstructionID
, pmMatchID :: MatchID
, pmMatch :: Match NodeID
}
deriving (Show)
instance FromJSON PatternMatch where
parseJSON (Object v) =
PatternMatch
<$> v .: "instr-id"
<*> v .: "match-id"
<*> v .: "match"
parseJSON _ = mzero
instance ToJSON PatternMatch where
toJSON m =
object [ "instr-id" .= (pmInstrID m)
, "match-id" .= (pmMatchID m)
, "match" .= (pmMatch m)
]
----------------------------------------
data Match n
= Match { f2pMaps :: M.Map n [n]
, p2fMaps :: M.Map n [n]
}
deriving (Show, Eq, Ord)
instance FromJSON (Match NodeID) where
parseJSON v#(Array _) =
do list <- parseJSON v
return $ toMatch list
parseJSON _ = mzero
instance ToJSON (Match NodeID) where
toJSON m = toJSON $ fromMatch m
----------------------------------------
data Mapping n
= Mapping
{ fNode :: n
, pNode :: n
}
deriving (Show, Eq, Ord)
instance FromJSON (Mapping NodeID) where
parseJSON v#(Array _) =
do list <- parseJSON v
when (length list /= 2) mzero
return Mapping { fNode = head list
, pNode = last list
}
parseJSON _ = mzero
instance ToJSON (Mapping NodeID) where
toJSON m = Array (V.fromList [toJSON $ fNode m, toJSON $ pNode m])
----------------------------------------
newtype MatchID
= MatchID Natural
deriving (Show, Eq, Ord, Num, Enum, Real, Integral)
instance FromJSON MatchID where
parseJSON (Number sn) = return $ toMatchID $ sn2nat sn
parseJSON _ = mzero
instance ToJSON MatchID where
toJSON mid = toJSON (fromMatchID mid)
----------------------------------------
newtype NodeID
= NodeID Natural
deriving (Show, Eq, Ord, Num, Enum, Real, Integral)
instance FromJSON NodeID where
parseJSON (Number sn) = return $ toNodeID $ sn2nat sn
parseJSON _ = mzero
instance ToJSON NodeID where
toJSON mid = toJSON (fromNodeID mid)
----------------------------------------
newtype InstructionID
= InstructionID Natural
deriving (Show, Eq, Ord, Num, Enum, Real, Integral)
instance FromJSON InstructionID where
parseJSON (Number sn) = return $ toInstructionID $ sn2nat sn
parseJSON _ = mzero
instance ToJSON InstructionID where
toJSON mid = toJSON (fromInstructionID mid)
----------------------------------------
newtype TargetMachineID
= TargetMachineID String
deriving (Show, Eq)
instance FromJSON TargetMachineID where
parseJSON (String s) = return $ toTargetMachineID $ T.unpack s
parseJSON _ = mzero
instance ToJSON TargetMachineID where
toJSON tmid = toJSON (fromTargetMachineID tmid)
----------------------------------------
-- Help functions
----------------------------------------
-- | Converts an 'Integral' into a 'Natural'. If conversion fails, 'Nothing' is
-- returned.
maybeToNatural :: (Integral i) => i -> Maybe Natural
maybeToNatural x
| x < 0 = Nothing
| otherwise = Just $ Natural $ toInteger x
-- | Converts an 'Integral' into a 'Natural'. If conversion fails, an error is
-- reported.
toNatural :: (Integral i) => i -> Natural
toNatural x =
let n = maybeToNatural x
in if isJust n
then fromJust n
else error $ "toNatural: negative number: " ++
show (toInteger x :: Integer)
-- | Converts a 'Natural' into an 'Integer'.
fromNatural :: Natural -> Integer
fromNatural (Natural i) = i
-- | Converts a scientific number to a natural number. If the number is not an
-- non-negative then an error occurs.
sn2nat :: Scientific -> Natural
sn2nat sn =
let int_value = round sn
in if fromInteger int_value /= sn
then error $ "sn2nat: not an integer: " ++ show sn
else toNatural int_value
fromTargetMachineID :: TargetMachineID -> String
fromTargetMachineID (TargetMachineID i) = i
toTargetMachineID :: String -> TargetMachineID
toTargetMachineID = TargetMachineID
fromMatchID :: MatchID -> Natural
fromMatchID (MatchID i) = i
toMatchID :: (Integral i) => i -> MatchID
toMatchID = MatchID . toNatural
fromNodeID :: NodeID -> Natural
fromNodeID (NodeID i) = i
toNodeID :: (Integral i) => i -> NodeID
toNodeID = NodeID . toNatural
fromInstructionID :: InstructionID -> Natural
fromInstructionID (InstructionID i) = i
toInstructionID :: (Integral i) => i -> InstructionID
toInstructionID = InstructionID . toNatural
toMatch :: Ord n => [Mapping n] -> Match n
toMatch ms =
let insert (n1, n2) m = M.insertWith (++) n1 [n2] m
in Match { f2pMaps = foldr insert M.empty $
map (\m -> (fNode m, pNode m)) ms
, p2fMaps = foldr insert M.empty $
map (\m -> (pNode m, fNode m)) ms
}
fromMatch :: Ord n => Match n -> [Mapping n]
fromMatch m =
M.foldrWithKey
(\fn pns ms -> (ms ++ map (\pn -> Mapping { fNode = fn, pNode = pn }) pns))
[]
(f2pMaps m)
----------------------------------------
-- Main program
----------------------------------------
main :: IO ()
main =
do args <- getArgs
when (length args == 0) $
error $ "No input file"
when (length args > 1) $
error $ "Too many arguments"
let file = head args
str <- BS.readFile file
let pmset = decode str
when (isNothing pmset) $
error $ "Failed to parse JSON"
putStrLn $ BS.unpack $ encode (fromJust pmset :: PatternMatchset)
The input is of the following format:
{
"match-data": [
{
"instr-id": 31,
"match": [
[2354, 5],
[2343, 3],
[2341, 10],
[2340, 9],
[1478, 8],
[1476, 6]
],
"match-id": 0
}
],
"target-machine-id": "Architecture",
"time": 27.642428397
}
The program above simply parses the JSON file, converts it back to JSON and prints the data. To get a larger input file, simply copy-paste the object within the match-data list and append it to the list.
I've tried compiling the program using -O2 flag, to no avail.
Try changing:
putStrLn $ BS.unpack $ encode (fromJust pmset :: PatternMatchset)
to
BS.putStrLn $ encode (fromJust pmset :: PatternMatchset)
The former caused my machine to go into swap hell. The latter completed just fine.

Lifting a function with another function as an argument in Haskell

So I have a function in Haskell that I've simplified for the purpose of asking this question:
import Data.Foldable
import Data.Set
myFn :: Int -> Set Int
myFn a
| a <= 0 = singleton 1
| otherwise = foldMap helper (myFn (a - 1))
helper :: Int -> Set Int
helper a = insert (a + 2) (singleton a)
main :: IO ()
main = print . Data.Set.toList $ myFn 5
I want to have myFn's dependency on helper to be put into a Reader, since inversion of control allows me to switch implementations in my tests:
import Control.Monad.Reader
import Data.Foldable
import Data.Set
data MyEnv = MyEnv { helper' :: Int -> Set Int }
type MyReader = Reader MyEnv
myFn :: Int -> MyReader (Set Int)
myFn a
| a <= 0 = return $ singleton 1
| otherwise = do
myFn' <- myFn (a - 1)
helper'' <- asks helper'
return (foldMap helper'' myFn')
helper :: Int -> Set Int
helper a = insert (a + 2) (singleton a)
main :: IO ()
main =
let
myEnv = MyEnv helper
in
print . Data.Set.toList $ runReader (myFn 5) myEnv
This works fine, except I don't like these three lines in particular:
myFn' <- myFn (a - 1)
helper'' <- asks helper'
return (foldMap helper'' myFn')
I feel like there should be a way to lift foldMap in the same way as mapM is a lifted version of map through its composition with sequence. Ideally, I would like those three lines to collapse down to one:
foldMapM helper'' (partitions (n - 1))
Assuming that: helper'' :: Int -> MyReader (Set Int)
This would of course require a foldMapM function with a signature similar to:
foldMapM
:: (Monad m, Foldable t, Monoid n)
=> (a -> m n)
-> m (t a)
-> m n
I have tried so many things, but I just cannot seem to implement this function, though! Can anyone help?
Basically, you would like to create Monad m => m a -> m b -> m c from a -> b -> c. That's exactly what liftM2 (from Control.Monad) does:
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Promote a function to a monad, scanning the monadic arguments from
left to right. For example,
liftM2 (+) [0,1] [0,2] = [0,2,1,3]
liftM2 (+) (Just 1) Nothing = Nothing
Therefore, it's as simple as using liftM2 foldMap:
myFn :: Int -> MyReader (Set Int)
myFn a
| a <= 0 = return $ singleton 1
| otherwise = liftM2 foldMap (asks helper') (myFn (a - 1))
Alternatively you can use <$> and <*> from Control.Applicative if you don't like additional parentheses:
myFn :: Int -> MyReader (Set Int)
myFn a
| a <= 0 = return $ singleton 1
| otherwise = foldMap <$> asks helper' <*> myFn (a - 1)
For more information, have a look at the Typeclassopedia.